2019-08-27 22:48:30 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* evalvars.c: functions for dealing with variables
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vim.h"
|
|
|
|
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
static dictitem_T globvars_var; // variable used for g:
|
2019-09-01 16:01:30 +02:00
|
|
|
static dict_T globvardict; // Dictionary with g: variables
|
|
|
|
#define globvarht globvardict.dv_hashtab
|
2019-08-29 22:09:46 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Old Vim variables such as "v:version" are also available without the "v:".
|
|
|
|
* Also in functions. We need a special hashtable for them.
|
|
|
|
*/
|
|
|
|
static hashtab_T compat_hashtab;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Array to hold the value of v: variables.
|
|
|
|
* The value is in a dictitem, so that it can also be used in the v: scope.
|
|
|
|
* The reason to use this table anyway is for very quick access to the
|
|
|
|
* variables with the VV_ defines.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// values for vv_flags:
|
|
|
|
#define VV_COMPAT 1 // compatible, also used without "v:"
|
|
|
|
#define VV_RO 2 // read-only
|
|
|
|
#define VV_RO_SBX 4 // read-only in the sandbox
|
|
|
|
|
|
|
|
#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
typedef struct vimvar vimvar_T;
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
static struct vimvar
|
|
|
|
{
|
|
|
|
char *vv_name; // name of variable, without v:
|
|
|
|
dictitem16_T vv_di; // value and name for key (max 16 chars!)
|
2021-12-24 21:36:12 +00:00
|
|
|
type_T *vv_type; // type or NULL
|
2019-08-29 22:09:46 +02:00
|
|
|
char vv_flags; // VV_COMPAT, VV_RO, VV_RO_SBX
|
|
|
|
} vimvars[VV_LEN] =
|
|
|
|
{
|
2019-08-30 15:46:30 +02:00
|
|
|
// The order here must match the VV_ defines in vim.h!
|
|
|
|
// Initializing a union does not work, leave tv.vval empty to get zero's.
|
2021-12-24 21:36:12 +00:00
|
|
|
{VV_NAME("count", VAR_NUMBER), NULL, VV_COMPAT+VV_RO},
|
|
|
|
{VV_NAME("count1", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("prevcount", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("errmsg", VAR_STRING), NULL, VV_COMPAT},
|
|
|
|
{VV_NAME("warningmsg", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("statusmsg", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("shell_error", VAR_NUMBER), NULL, VV_COMPAT+VV_RO},
|
|
|
|
{VV_NAME("this_session", VAR_STRING), NULL, VV_COMPAT},
|
|
|
|
{VV_NAME("version", VAR_NUMBER), NULL, VV_COMPAT+VV_RO},
|
|
|
|
{VV_NAME("lnum", VAR_NUMBER), NULL, VV_RO_SBX},
|
|
|
|
{VV_NAME("termresponse", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fname", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("lang", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("lc_time", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("ctype", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("charconvert_from", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("charconvert_to", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fname_in", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fname_out", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fname_new", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fname_diff", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("cmdarg", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("foldstart", VAR_NUMBER), NULL, VV_RO_SBX},
|
|
|
|
{VV_NAME("foldend", VAR_NUMBER), NULL, VV_RO_SBX},
|
|
|
|
{VV_NAME("folddashes", VAR_STRING), NULL, VV_RO_SBX},
|
|
|
|
{VV_NAME("foldlevel", VAR_NUMBER), NULL, VV_RO_SBX},
|
|
|
|
{VV_NAME("progname", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("servername", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("dying", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("exception", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("throwpoint", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("register", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("cmdbang", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("insertmode", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("val", VAR_UNKNOWN), NULL, VV_RO},
|
|
|
|
{VV_NAME("key", VAR_UNKNOWN), NULL, VV_RO},
|
|
|
|
{VV_NAME("profiling", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("fcs_reason", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("fcs_choice", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("beval_bufnr", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("beval_winnr", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("beval_winid", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("beval_lnum", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("beval_col", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("beval_text", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("scrollstart", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("swapname", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("swapchoice", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("swapcommand", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("char", VAR_STRING), NULL, 0},
|
|
|
|
{VV_NAME("mouse_win", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("mouse_winid", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("mouse_lnum", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("mouse_col", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("operator", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("searchforward", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("hlsearch", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("oldfiles", VAR_LIST), &t_list_string, 0},
|
|
|
|
{VV_NAME("windowid", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("progpath", VAR_STRING), NULL, VV_RO},
|
2022-07-27 14:40:00 +01:00
|
|
|
{VV_NAME("completed_item", VAR_DICT), &t_dict_string, 0},
|
2021-12-24 21:36:12 +00:00
|
|
|
{VV_NAME("option_new", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("option_old", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("option_oldlocal", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("option_oldglobal", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("option_command", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("option_type", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("errors", VAR_LIST), &t_list_string, 0},
|
|
|
|
{VV_NAME("false", VAR_BOOL), NULL, VV_RO},
|
|
|
|
{VV_NAME("true", VAR_BOOL), NULL, VV_RO},
|
|
|
|
{VV_NAME("none", VAR_SPECIAL), NULL, VV_RO},
|
|
|
|
{VV_NAME("null", VAR_SPECIAL), NULL, VV_RO},
|
|
|
|
{VV_NAME("numbermax", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("numbermin", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("numbersize", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("vim_did_enter", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("testing", VAR_NUMBER), NULL, 0},
|
|
|
|
{VV_NAME("t_number", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_string", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_func", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_list", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_dict", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_float", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_bool", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_none", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_job", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_channel", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_blob", VAR_NUMBER), NULL, VV_RO},
|
2023-01-12 21:08:53 +00:00
|
|
|
{VV_NAME("t_class", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_object", VAR_NUMBER), NULL, VV_RO},
|
2021-12-24 21:36:12 +00:00
|
|
|
{VV_NAME("termrfgresp", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("termrbgresp", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("termu7resp", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("termstyleresp", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("termblinkresp", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("event", VAR_DICT), NULL, VV_RO},
|
|
|
|
{VV_NAME("versionlong", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("echospace", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("argv", VAR_LIST), &t_list_string, VV_RO},
|
|
|
|
{VV_NAME("collate", VAR_STRING), NULL, VV_RO},
|
|
|
|
{VV_NAME("exiting", VAR_SPECIAL), NULL, VV_RO},
|
|
|
|
{VV_NAME("colornames", VAR_DICT), &t_dict_string, VV_RO},
|
|
|
|
{VV_NAME("sizeofint", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("sizeoflong", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("sizeofpointer", VAR_NUMBER), NULL, VV_RO},
|
2022-01-01 14:59:44 +00:00
|
|
|
{VV_NAME("maxcol", VAR_NUMBER), NULL, VV_RO},
|
patch 9.0.1776: No support for stable Python 3 ABI
Problem: No support for stable Python 3 ABI
Solution: Support Python 3 stable ABI
Commits:
1) Support Python 3 stable ABI to allow mixed version interoperatbility
Vim currently supports embedding Python for use with plugins, and the
"dynamic" linking option allows the user to specify a locally installed
version of Python by setting `pythonthreedll`. However, one caveat is
that the Python 3 libs are not binary compatible across minor versions,
and mixing versions can potentially be dangerous (e.g. let's say Vim was
linked against the Python 3.10 SDK, but the user sets `pythonthreedll`
to a 3.11 lib). Usually, nothing bad happens, but in theory this could
lead to crashes, memory corruption, and other unpredictable behaviors.
It's also difficult for the user to tell something is wrong because Vim
has no way of reporting what Python 3 version Vim was linked with.
For Vim installed via a package manager, this usually isn't an issue
because all the dependencies would already be figured out. For prebuilt
Vim binaries like MacVim (my motivation for working on this), AppImage,
and Win32 installer this could potentially be an issue as usually a
single binary is distributed. This is more tricky when a new Python
version is released, as there's a chicken-and-egg issue with deciding
what Python version to build against and hard to keep in sync when a new
Python version just drops and we have a mix of users of different Python
versions, and a user just blindly upgrading to a new Python could lead to
bad interactions with Vim.
Python 3 does have a solution for this problem: stable ABI / limited API
(see https://docs.python.org/3/c-api/stable.html). The C SDK limits the
API to a set of functions that are promised to be stable across
versions. This pull request adds an ifdef config that allows us to turn
it on when building Vim. Vim binaries built with this option should be
safe to freely link with any Python 3 libraies without having the
constraint of having to use the same minor version.
Note: Python 2 has no such concept and this doesn't change how Python 2
integration works (not that there is going to be a new version of Python
2 that would cause compatibility issues in the future anyway).
---
Technical details:
======
The stable ABI can be accessed when we compile with the Python 3 limited
API (by defining `Py_LIMITED_API`). The Python 3 code (in `if_python3.c`
and `if_py_both.h`) would now handle this and switch to limited API
mode. Without it set, Vim will still use the full API as before so this
is an opt-in change.
The main difference is that `PyType_Object` is now an opaque struct that
we can't directly create "static types" out of, and we have to create
type objects as "heap types" instead. This is because the struct is not
stable and changes from version to version (e.g. 3.8 added a
`tp_vectorcall` field to it). I had to change all the types to be
allocated on the heap instead with just a pointer to them.
Other functions are also simply missing in limited API, or they are
introduced too late (e.g. `PyUnicode_AsUTF8AndSize` in 3.10) to it that
we need some other ways to do the same thing, so I had to abstract a few
things into macros, and sometimes re-implement functions like
`PyObject_NEW`.
One caveat is that in limited API, `OutputType` (used for replacing
`sys.stdout`) no longer inherits from `PyStdPrinter_Type` which I don't
think has any real issue other than minor differences in how they
convert to a string and missing a couple functions like `mode()` and
`fileno()`.
Also fixed an existing bug where `tp_basicsize` was set incorrectly for
`BufferObject`, `TabListObject, `WinListObject`.
Technically, there could be a small performance drop, there is a little
more indirection with accessing type objects, and some APIs like
`PyUnicode_AsUTF8AndSize` are missing, but in practice I didn't see any
difference, and any well-written Python plugin should try to avoid
excessing callbacks to the `vim` module in Python anyway.
I only tested limited API mode down to Python 3.7, which seemes to
compile and work fine. I haven't tried earlier Python versions.
2) Fix PyIter_Check on older Python vers / type##Ptr unused warning
For PyIter_Check, older versions exposed them as either macros (used in
full API), or a function (for use in limited API). A previous change
exposed PyIter_Check to the dynamic build because Python just moved it
to function-only in 3.10 anyway. Because of that, just make sure we
always grab the function in dynamic builds in earlier versions since
that's what Python eventually did anyway.
3) Move Py_LIMITED_API define to configure script
Can now use --with-python-stable-abi flag to customize what stable ABI
version to target. Can also use an env var to do so as well.
4) Show +python/dyn-stable in :version, and allow has() feature query
Not sure if the "/dyn-stable" suffix would break things, or whether we
should do it another way. Or just don't show it in version and rely on
has() feature checking.
5) Documentation first draft. Still need to implement v:python3_version
6) Fix PyIter_Check build breaks when compiling against Python 3.8
7) Add CI coverage stable ABI on Linux/Windows / make configurable on Windows
This adds configurable options for Windows make files (both MinGW and
MSVC). CI will also now exercise both traditional full API and stable
ABI for Linux and Windows in the matrix for coverage.
Also added a "dynamic" option to Linux matrix as a drive-by change to
make other scripting languages like Ruby / Perl testable under both
static and dynamic builds.
8) Fix inaccuracy in Windows docs
Python's own docs are confusing but you don't actually want to use
`python3.dll` for the dynamic linkage.
9) Add generated autoconf file
10) Add v:python3_version support
This variable indicates the version of Python3 that Vim was built
against (PY_VERSION_HEX), and will be useful to check whether the Python
library you are loading in dynamically actually fits it. When built with
stable ABI, it will be the limited ABI version instead
(`Py_LIMITED_API`), which indicates the minimum version of Python 3 the
user should have, rather than the exact match. When stable ABI is used,
we won't be exposing PY_VERSION_HEX in this var because it just doesn't
seem necessary to do so (the whole point of stable ABI is the promise
that it will work across versions), and I don't want to confuse the user
with too many variables.
Also, cleaned up some documentation, and added help tags.
11) Fix Python 3.7 compat issues
Fix a couple issues when using limited API < 3.8
- Crash on exit: In Python 3.7, if a heap-allocated type is destroyed
before all instances are, it would cause a crash later. This happens
when we destroyed `OptionsType` before calling `Py_Finalize` when
using the limited API. To make it worse, later versions changed the
semantics and now each instance has a strong reference to its own type
and the recommendation has changed to have each instance de-ref its
own type and have its type in GC traversal. To avoid dealing with
these cross-version variations, we just don't free the heap type. They
are static types in non-limited-API anyway and are designed to last
through the entirety of the app, and we also don't restart the Python
runtime and therefore do not need it to have absolutely 0 leaks.
See:
- https://docs.python.org/3/whatsnew/3.8.html#changes-in-the-c-api
- https://docs.python.org/3/whatsnew/3.9.html#changes-in-the-c-api
- PyIter_Check: This function is not provided in limited APIs older than
3.8. Previously I was trying to mock it out using manual
PyType_GetSlot() but it was brittle and also does not actually work
properly for static types (it will generate a Python error). Just
return false. It does mean using limited API < 3.8 is not recommended
as you lose the functionality to handle iterators, but from playing
with plugins I couldn't find it to be an issue.
- Fix loading of PyIter_Check so it will be done when limited API < 3.8.
Otherwise loading a 3.7 Python lib will fail even if limited API was
specified to use it.
12) Make sure to only load `PyUnicode_AsUTF8AndSize` in needed in limited API
We don't use this function unless limited API >= 3.10, but we were
loading it regardless. Usually it's ok in Unix-like systems where Python
just has a single lib that we load from, but in Windows where there is a
separate python3.dll this would not work as the symbol would not have
been exposed in this more limited DLL file. This makes it much clearer
under what condition is this function needed.
closes: #12032
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
2023-08-20 21:18:38 +02:00
|
|
|
{VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO},
|
2023-10-27 19:35:26 +02:00
|
|
|
{VV_NAME("t_typealias", VAR_NUMBER), NULL, VV_RO},
|
2024-03-28 10:36:42 +01:00
|
|
|
{VV_NAME("t_enum", VAR_NUMBER), NULL, VV_RO},
|
|
|
|
{VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO},
|
2019-08-29 22:09:46 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// shorthand
|
2021-12-24 21:36:12 +00:00
|
|
|
#define vv_tv_type vv_di.di_tv.v_type
|
2019-08-29 22:09:46 +02:00
|
|
|
#define vv_nr vv_di.di_tv.vval.v_number
|
|
|
|
#define vv_float vv_di.di_tv.vval.v_float
|
|
|
|
#define vv_str vv_di.di_tv.vval.v_string
|
|
|
|
#define vv_list vv_di.di_tv.vval.v_list
|
|
|
|
#define vv_dict vv_di.di_tv.vval.v_dict
|
|
|
|
#define vv_blob vv_di.di_tv.vval.v_blob
|
|
|
|
#define vv_tv vv_di.di_tv
|
|
|
|
|
|
|
|
static dictitem_T vimvars_var; // variable used for v:
|
2019-09-01 16:01:30 +02:00
|
|
|
static dict_T vimvardict; // Dictionary with v: variables
|
2019-08-29 22:09:46 +02:00
|
|
|
#define vimvarht vimvardict.dv_hashtab
|
|
|
|
|
|
|
|
// for VIM_VERSION_ defines
|
|
|
|
#include "version.h"
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
static void list_glob_vars(int *first);
|
|
|
|
static void list_buf_vars(int *first);
|
|
|
|
static void list_win_vars(int *first);
|
|
|
|
static void list_tab_vars(int *first);
|
|
|
|
static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
|
2021-02-11 21:19:34 +01:00
|
|
|
static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op, int var_idx);
|
2020-04-19 16:28:59 +02:00
|
|
|
static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
|
|
|
static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
|
2019-08-27 22:48:30 +02:00
|
|
|
static void list_one_var(dictitem_T *v, char *prefix, int *first);
|
|
|
|
static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first);
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Initialize global and vim special variables
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
evalvars_init(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct vimvar *p;
|
|
|
|
|
|
|
|
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
|
|
|
|
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
|
|
|
|
vimvardict.dv_lock = VAR_FIXED;
|
|
|
|
hash_init(&compat_hashtab);
|
|
|
|
|
|
|
|
for (i = 0; i < VV_LEN; ++i)
|
|
|
|
{
|
|
|
|
p = &vimvars[i];
|
|
|
|
if (STRLEN(p->vv_name) > DICTITEM16_KEY_LEN)
|
|
|
|
{
|
2023-05-24 21:02:24 +01:00
|
|
|
iemsg("Name too long, increase size of dictitem16_T");
|
2019-08-29 22:09:46 +02:00
|
|
|
getout(1);
|
|
|
|
}
|
|
|
|
STRCPY(p->vv_di.di_key, p->vv_name);
|
|
|
|
if (p->vv_flags & VV_RO)
|
|
|
|
p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
|
|
|
else if (p->vv_flags & VV_RO_SBX)
|
|
|
|
p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX;
|
|
|
|
else
|
|
|
|
p->vv_di.di_flags = DI_FLAGS_FIX;
|
|
|
|
|
|
|
|
// add to v: scope dict, unless the value is not always available
|
2021-12-24 21:36:12 +00:00
|
|
|
if (p->vv_tv_type != VAR_UNKNOWN)
|
2022-11-25 16:31:51 +00:00
|
|
|
hash_add(&vimvarht, p->vv_di.di_key, "initialization");
|
2019-08-29 22:09:46 +02:00
|
|
|
if (p->vv_flags & VV_COMPAT)
|
|
|
|
// add to compat scope dict
|
2022-11-25 16:31:51 +00:00
|
|
|
hash_add(&compat_hashtab, p->vv_di.di_key, "initialization");
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
2020-10-03 12:57:27 +02:00
|
|
|
set_vim_var_nr(VV_VERSION, VIM_VERSION_100);
|
|
|
|
set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch());
|
2019-08-29 22:09:46 +02:00
|
|
|
|
|
|
|
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
|
|
|
|
set_vim_var_nr(VV_HLSEARCH, 1L);
|
2020-11-30 17:42:10 +01:00
|
|
|
set_vim_var_nr(VV_EXITING, VVAL_NULL);
|
2019-08-29 22:09:46 +02:00
|
|
|
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
|
|
|
|
set_vim_var_list(VV_ERRORS, list_alloc());
|
|
|
|
set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));
|
|
|
|
|
|
|
|
set_vim_var_nr(VV_FALSE, VVAL_FALSE);
|
|
|
|
set_vim_var_nr(VV_TRUE, VVAL_TRUE);
|
|
|
|
set_vim_var_nr(VV_NONE, VVAL_NONE);
|
|
|
|
set_vim_var_nr(VV_NULL, VVAL_NULL);
|
2021-01-21 21:42:31 +01:00
|
|
|
set_vim_var_nr(VV_NUMBERMAX, VARNUM_MAX);
|
|
|
|
set_vim_var_nr(VV_NUMBERMIN, VARNUM_MIN);
|
2020-02-22 14:27:04 +01:00
|
|
|
set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
|
2021-11-02 21:39:49 +00:00
|
|
|
set_vim_var_nr(VV_SIZEOFINT, sizeof(int));
|
|
|
|
set_vim_var_nr(VV_SIZEOFLONG, sizeof(long));
|
|
|
|
set_vim_var_nr(VV_SIZEOFPOINTER, sizeof(char *));
|
2022-01-01 14:59:44 +00:00
|
|
|
set_vim_var_nr(VV_MAXCOL, MAXCOL);
|
2019-08-29 22:09:46 +02:00
|
|
|
|
|
|
|
set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
|
|
|
|
set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
|
|
|
|
set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
|
|
|
|
set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST);
|
|
|
|
set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
|
|
|
|
set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
|
|
|
|
set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
|
|
|
|
set_vim_var_nr(VV_TYPE_NONE, VAR_TYPE_NONE);
|
|
|
|
set_vim_var_nr(VV_TYPE_JOB, VAR_TYPE_JOB);
|
|
|
|
set_vim_var_nr(VV_TYPE_CHANNEL, VAR_TYPE_CHANNEL);
|
|
|
|
set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
|
2023-01-12 21:08:53 +00:00
|
|
|
set_vim_var_nr(VV_TYPE_CLASS, VAR_TYPE_CLASS);
|
|
|
|
set_vim_var_nr(VV_TYPE_OBJECT, VAR_TYPE_OBJECT);
|
2023-10-27 19:35:26 +02:00
|
|
|
set_vim_var_nr(VV_TYPE_TYPEALIAS, VAR_TYPE_TYPEALIAS);
|
2024-03-28 10:36:42 +01:00
|
|
|
set_vim_var_nr(VV_TYPE_ENUM, VAR_TYPE_ENUM);
|
|
|
|
set_vim_var_nr(VV_TYPE_ENUMVALUE, VAR_TYPE_ENUMVALUE);
|
2019-08-29 22:09:46 +02:00
|
|
|
|
|
|
|
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
|
|
|
|
|
2021-10-24 20:35:07 +01:00
|
|
|
set_vim_var_dict(VV_COLORNAMES, dict_alloc());
|
|
|
|
|
patch 9.0.1776: No support for stable Python 3 ABI
Problem: No support for stable Python 3 ABI
Solution: Support Python 3 stable ABI
Commits:
1) Support Python 3 stable ABI to allow mixed version interoperatbility
Vim currently supports embedding Python for use with plugins, and the
"dynamic" linking option allows the user to specify a locally installed
version of Python by setting `pythonthreedll`. However, one caveat is
that the Python 3 libs are not binary compatible across minor versions,
and mixing versions can potentially be dangerous (e.g. let's say Vim was
linked against the Python 3.10 SDK, but the user sets `pythonthreedll`
to a 3.11 lib). Usually, nothing bad happens, but in theory this could
lead to crashes, memory corruption, and other unpredictable behaviors.
It's also difficult for the user to tell something is wrong because Vim
has no way of reporting what Python 3 version Vim was linked with.
For Vim installed via a package manager, this usually isn't an issue
because all the dependencies would already be figured out. For prebuilt
Vim binaries like MacVim (my motivation for working on this), AppImage,
and Win32 installer this could potentially be an issue as usually a
single binary is distributed. This is more tricky when a new Python
version is released, as there's a chicken-and-egg issue with deciding
what Python version to build against and hard to keep in sync when a new
Python version just drops and we have a mix of users of different Python
versions, and a user just blindly upgrading to a new Python could lead to
bad interactions with Vim.
Python 3 does have a solution for this problem: stable ABI / limited API
(see https://docs.python.org/3/c-api/stable.html). The C SDK limits the
API to a set of functions that are promised to be stable across
versions. This pull request adds an ifdef config that allows us to turn
it on when building Vim. Vim binaries built with this option should be
safe to freely link with any Python 3 libraies without having the
constraint of having to use the same minor version.
Note: Python 2 has no such concept and this doesn't change how Python 2
integration works (not that there is going to be a new version of Python
2 that would cause compatibility issues in the future anyway).
---
Technical details:
======
The stable ABI can be accessed when we compile with the Python 3 limited
API (by defining `Py_LIMITED_API`). The Python 3 code (in `if_python3.c`
and `if_py_both.h`) would now handle this and switch to limited API
mode. Without it set, Vim will still use the full API as before so this
is an opt-in change.
The main difference is that `PyType_Object` is now an opaque struct that
we can't directly create "static types" out of, and we have to create
type objects as "heap types" instead. This is because the struct is not
stable and changes from version to version (e.g. 3.8 added a
`tp_vectorcall` field to it). I had to change all the types to be
allocated on the heap instead with just a pointer to them.
Other functions are also simply missing in limited API, or they are
introduced too late (e.g. `PyUnicode_AsUTF8AndSize` in 3.10) to it that
we need some other ways to do the same thing, so I had to abstract a few
things into macros, and sometimes re-implement functions like
`PyObject_NEW`.
One caveat is that in limited API, `OutputType` (used for replacing
`sys.stdout`) no longer inherits from `PyStdPrinter_Type` which I don't
think has any real issue other than minor differences in how they
convert to a string and missing a couple functions like `mode()` and
`fileno()`.
Also fixed an existing bug where `tp_basicsize` was set incorrectly for
`BufferObject`, `TabListObject, `WinListObject`.
Technically, there could be a small performance drop, there is a little
more indirection with accessing type objects, and some APIs like
`PyUnicode_AsUTF8AndSize` are missing, but in practice I didn't see any
difference, and any well-written Python plugin should try to avoid
excessing callbacks to the `vim` module in Python anyway.
I only tested limited API mode down to Python 3.7, which seemes to
compile and work fine. I haven't tried earlier Python versions.
2) Fix PyIter_Check on older Python vers / type##Ptr unused warning
For PyIter_Check, older versions exposed them as either macros (used in
full API), or a function (for use in limited API). A previous change
exposed PyIter_Check to the dynamic build because Python just moved it
to function-only in 3.10 anyway. Because of that, just make sure we
always grab the function in dynamic builds in earlier versions since
that's what Python eventually did anyway.
3) Move Py_LIMITED_API define to configure script
Can now use --with-python-stable-abi flag to customize what stable ABI
version to target. Can also use an env var to do so as well.
4) Show +python/dyn-stable in :version, and allow has() feature query
Not sure if the "/dyn-stable" suffix would break things, or whether we
should do it another way. Or just don't show it in version and rely on
has() feature checking.
5) Documentation first draft. Still need to implement v:python3_version
6) Fix PyIter_Check build breaks when compiling against Python 3.8
7) Add CI coverage stable ABI on Linux/Windows / make configurable on Windows
This adds configurable options for Windows make files (both MinGW and
MSVC). CI will also now exercise both traditional full API and stable
ABI for Linux and Windows in the matrix for coverage.
Also added a "dynamic" option to Linux matrix as a drive-by change to
make other scripting languages like Ruby / Perl testable under both
static and dynamic builds.
8) Fix inaccuracy in Windows docs
Python's own docs are confusing but you don't actually want to use
`python3.dll` for the dynamic linkage.
9) Add generated autoconf file
10) Add v:python3_version support
This variable indicates the version of Python3 that Vim was built
against (PY_VERSION_HEX), and will be useful to check whether the Python
library you are loading in dynamically actually fits it. When built with
stable ABI, it will be the limited ABI version instead
(`Py_LIMITED_API`), which indicates the minimum version of Python 3 the
user should have, rather than the exact match. When stable ABI is used,
we won't be exposing PY_VERSION_HEX in this var because it just doesn't
seem necessary to do so (the whole point of stable ABI is the promise
that it will work across versions), and I don't want to confuse the user
with too many variables.
Also, cleaned up some documentation, and added help tags.
11) Fix Python 3.7 compat issues
Fix a couple issues when using limited API < 3.8
- Crash on exit: In Python 3.7, if a heap-allocated type is destroyed
before all instances are, it would cause a crash later. This happens
when we destroyed `OptionsType` before calling `Py_Finalize` when
using the limited API. To make it worse, later versions changed the
semantics and now each instance has a strong reference to its own type
and the recommendation has changed to have each instance de-ref its
own type and have its type in GC traversal. To avoid dealing with
these cross-version variations, we just don't free the heap type. They
are static types in non-limited-API anyway and are designed to last
through the entirety of the app, and we also don't restart the Python
runtime and therefore do not need it to have absolutely 0 leaks.
See:
- https://docs.python.org/3/whatsnew/3.8.html#changes-in-the-c-api
- https://docs.python.org/3/whatsnew/3.9.html#changes-in-the-c-api
- PyIter_Check: This function is not provided in limited APIs older than
3.8. Previously I was trying to mock it out using manual
PyType_GetSlot() but it was brittle and also does not actually work
properly for static types (it will generate a Python error). Just
return false. It does mean using limited API < 3.8 is not recommended
as you lose the functionality to handle iterators, but from playing
with plugins I couldn't find it to be an issue.
- Fix loading of PyIter_Check so it will be done when limited API < 3.8.
Otherwise loading a 3.7 Python lib will fail even if limited API was
specified to use it.
12) Make sure to only load `PyUnicode_AsUTF8AndSize` in needed in limited API
We don't use this function unless limited API >= 3.10, but we were
loading it regardless. Usually it's ok in Unix-like systems where Python
just has a single lib that we load from, but in Windows where there is a
separate python3.dll this would not work as the symbol would not have
been exposed in this more limited DLL file. This makes it much clearer
under what condition is this function needed.
closes: #12032
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
2023-08-20 21:18:38 +02:00
|
|
|
#ifdef FEAT_PYTHON3
|
|
|
|
set_vim_var_nr(VV_PYTHON3_VERSION, python3_version());
|
|
|
|
#endif
|
|
|
|
|
2020-06-06 15:58:03 +02:00
|
|
|
// Default for v:register is not 0 but '"'. This is adjusted once the
|
|
|
|
// clipboard has been setup by calling reset_reg_var().
|
|
|
|
set_reg_var(0);
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(EXITFREE) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Free all vim variables information on exit
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
evalvars_clear(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct vimvar *p;
|
|
|
|
|
|
|
|
for (i = 0; i < VV_LEN; ++i)
|
|
|
|
{
|
|
|
|
p = &vimvars[i];
|
|
|
|
if (p->vv_di.di_tv.v_type == VAR_STRING)
|
|
|
|
VIM_CLEAR(p->vv_str);
|
|
|
|
else if (p->vv_di.di_tv.v_type == VAR_LIST)
|
|
|
|
{
|
|
|
|
list_unref(p->vv_list);
|
|
|
|
p->vv_list = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hash_clear(&vimvarht);
|
|
|
|
hash_init(&vimvarht); // garbage_collect() will access it
|
|
|
|
hash_clear(&compat_hashtab);
|
|
|
|
|
|
|
|
// global variables
|
|
|
|
vars_clear(&globvarht);
|
|
|
|
|
2020-01-12 17:42:55 +01:00
|
|
|
// Script-local variables. Clear all the variables here.
|
|
|
|
// The scriptvar_T is cleared later in free_scriptnames(), because a
|
|
|
|
// variable in one script might hold a reference to the whole scope of
|
|
|
|
// another script.
|
|
|
|
for (i = 1; i <= script_items.ga_len; ++i)
|
2019-08-29 22:09:46 +02:00
|
|
|
vars_clear(&SCRIPT_VARS(i));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
int
|
|
|
|
garbage_collect_globvars(int copyID)
|
|
|
|
{
|
|
|
|
return set_ref_in_ht(&globvarht, copyID, NULL);
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
int
|
|
|
|
garbage_collect_vimvars(int copyID)
|
|
|
|
{
|
|
|
|
return set_ref_in_ht(&vimvarht, copyID, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
garbage_collect_scriptvars(int copyID)
|
|
|
|
{
|
2020-10-15 20:42:20 +02:00
|
|
|
int i;
|
|
|
|
int idx;
|
|
|
|
int abort = FALSE;
|
|
|
|
scriptitem_T *si;
|
2019-08-29 22:09:46 +02:00
|
|
|
|
2020-01-12 17:42:55 +01:00
|
|
|
for (i = 1; i <= script_items.ga_len; ++i)
|
2020-10-15 20:42:20 +02:00
|
|
|
{
|
2019-08-29 22:09:46 +02:00
|
|
|
abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);
|
|
|
|
|
2020-10-15 20:42:20 +02:00
|
|
|
si = SCRIPT_ITEM(i);
|
|
|
|
for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
|
|
|
|
{
|
|
|
|
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
|
|
|
|
|
2021-03-08 20:47:14 +01:00
|
|
|
if (sv->sv_name != NULL)
|
|
|
|
abort = abort || set_ref_in_item(sv->sv_tv, copyID, NULL, NULL);
|
2020-10-15 20:42:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
return abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set an internal variable to a string value. Creates the variable if it does
|
|
|
|
* not already exist.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_internal_string_var(char_u *name, char_u *value)
|
|
|
|
{
|
|
|
|
char_u *val;
|
|
|
|
typval_T *tvp;
|
|
|
|
|
|
|
|
val = vim_strsave(value);
|
2023-01-02 16:54:53 +00:00
|
|
|
if (val == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tvp = alloc_string_tv(val);
|
|
|
|
if (tvp == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
set_var(name, tvp, FALSE);
|
|
|
|
free_tv(tvp);
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
int
|
|
|
|
eval_charconvert(
|
|
|
|
char_u *enc_from,
|
|
|
|
char_u *enc_to,
|
|
|
|
char_u *fname_from,
|
|
|
|
char_u *fname_to)
|
|
|
|
{
|
|
|
|
int err = FALSE;
|
2022-01-23 14:17:28 +00:00
|
|
|
sctx_T saved_sctx = current_sctx;
|
|
|
|
sctx_T *ctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
set_vim_var_string(VV_CC_FROM, enc_from, -1);
|
|
|
|
set_vim_var_string(VV_CC_TO, enc_to, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
|
2022-01-23 14:17:28 +00:00
|
|
|
ctx = get_option_sctx("charconvert");
|
|
|
|
if (ctx != NULL)
|
|
|
|
current_sctx = *ctx;
|
|
|
|
|
2022-10-01 19:43:52 +01:00
|
|
|
if (eval_to_bool(p_ccv, &err, NULL, FALSE, TRUE))
|
2019-09-01 16:01:30 +02:00
|
|
|
err = TRUE;
|
2022-01-23 14:17:28 +00:00
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
set_vim_var_string(VV_CC_FROM, NULL, -1);
|
|
|
|
set_vim_var_string(VV_CC_TO, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_IN, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
|
2022-01-23 14:17:28 +00:00
|
|
|
current_sctx = saved_sctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
if (err)
|
|
|
|
return FAIL;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
|
|
|
|
int
|
|
|
|
eval_printexpr(char_u *fname, char_u *args)
|
|
|
|
{
|
|
|
|
int err = FALSE;
|
2022-01-23 13:44:35 +00:00
|
|
|
sctx_T saved_sctx = current_sctx;
|
|
|
|
sctx_T *ctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
set_vim_var_string(VV_FNAME_IN, fname, -1);
|
|
|
|
set_vim_var_string(VV_CMDARG, args, -1);
|
2022-01-23 13:44:35 +00:00
|
|
|
ctx = get_option_sctx("printexpr");
|
|
|
|
if (ctx != NULL)
|
|
|
|
current_sctx = *ctx;
|
|
|
|
|
2022-10-01 19:43:52 +01:00
|
|
|
if (eval_to_bool(p_pexpr, &err, NULL, FALSE, TRUE))
|
2019-09-01 16:01:30 +02:00
|
|
|
err = TRUE;
|
2022-01-23 13:44:35 +00:00
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
set_vim_var_string(VV_FNAME_IN, NULL, -1);
|
|
|
|
set_vim_var_string(VV_CMDARG, NULL, -1);
|
2022-01-23 13:44:35 +00:00
|
|
|
current_sctx = saved_sctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
mch_remove(fname);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# if defined(FEAT_DIFF) || defined(PROTO)
|
|
|
|
void
|
|
|
|
eval_diff(
|
|
|
|
char_u *origfile,
|
|
|
|
char_u *newfile,
|
|
|
|
char_u *outfile)
|
|
|
|
{
|
2022-01-22 17:58:13 +00:00
|
|
|
sctx_T saved_sctx = current_sctx;
|
|
|
|
sctx_T *ctx;
|
|
|
|
typval_T *tv;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
set_vim_var_string(VV_FNAME_IN, origfile, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
|
2022-01-22 17:58:13 +00:00
|
|
|
|
|
|
|
ctx = get_option_sctx("diffexpr");
|
|
|
|
if (ctx != NULL)
|
|
|
|
current_sctx = *ctx;
|
|
|
|
|
|
|
|
// errors are ignored
|
2022-10-01 19:43:52 +01:00
|
|
|
tv = eval_expr_ext(p_dex, NULL, TRUE);
|
2022-01-22 18:21:36 +00:00
|
|
|
free_tv(tv);
|
2022-01-22 17:58:13 +00:00
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
set_vim_var_string(VV_FNAME_IN, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
|
2022-01-22 17:58:13 +00:00
|
|
|
current_sctx = saved_sctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
eval_patch(
|
|
|
|
char_u *origfile,
|
|
|
|
char_u *difffile,
|
|
|
|
char_u *outfile)
|
|
|
|
{
|
2022-01-22 20:55:30 +00:00
|
|
|
sctx_T saved_sctx = current_sctx;
|
|
|
|
sctx_T *ctx;
|
|
|
|
typval_T *tv;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
set_vim_var_string(VV_FNAME_IN, origfile, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
|
2022-01-22 20:55:30 +00:00
|
|
|
|
|
|
|
ctx = get_option_sctx("patchexpr");
|
|
|
|
if (ctx != NULL)
|
|
|
|
current_sctx = *ctx;
|
|
|
|
|
|
|
|
// errors are ignored
|
2022-10-01 19:43:52 +01:00
|
|
|
tv = eval_expr_ext(p_pex, NULL, TRUE);
|
2022-01-22 20:55:30 +00:00
|
|
|
free_tv(tv);
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
set_vim_var_string(VV_FNAME_IN, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_DIFF, NULL, -1);
|
|
|
|
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
|
2022-01-22 20:55:30 +00:00
|
|
|
current_sctx = saved_sctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
}
|
|
|
|
# endif
|
|
|
|
|
|
|
|
#if defined(FEAT_SPELL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Evaluate an expression to a list with suggestions.
|
|
|
|
* For the "expr:" part of 'spellsuggest'.
|
|
|
|
* Returns NULL when there is an error.
|
|
|
|
*/
|
|
|
|
list_T *
|
|
|
|
eval_spell_expr(char_u *badword, char_u *expr)
|
|
|
|
{
|
|
|
|
typval_T save_val;
|
|
|
|
typval_T rettv;
|
|
|
|
list_T *list = NULL;
|
|
|
|
char_u *p = skipwhite(expr);
|
2022-01-23 17:59:06 +00:00
|
|
|
sctx_T saved_sctx = current_sctx;
|
|
|
|
sctx_T *ctx;
|
2022-10-01 19:43:52 +01:00
|
|
|
int r;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
// Set "v:val" to the bad word.
|
|
|
|
prepare_vimvar(VV_VAL, &save_val);
|
|
|
|
set_vim_var_string(VV_VAL, badword, -1);
|
|
|
|
if (p_verbose == 0)
|
|
|
|
++emsg_off;
|
2022-01-23 17:59:06 +00:00
|
|
|
ctx = get_option_sctx("spellsuggest");
|
|
|
|
if (ctx != NULL)
|
|
|
|
current_sctx = *ctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
2022-10-01 19:43:52 +01:00
|
|
|
r = may_call_simple_func(p, &rettv);
|
|
|
|
if (r == NOTDONE)
|
|
|
|
r = eval1(&p, &rettv, &EVALARG_EVALUATE);
|
|
|
|
if (r == OK)
|
2019-09-01 16:01:30 +02:00
|
|
|
{
|
|
|
|
if (rettv.v_type != VAR_LIST)
|
|
|
|
clear_tv(&rettv);
|
|
|
|
else
|
|
|
|
list = rettv.vval.v_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_verbose == 0)
|
|
|
|
--emsg_off;
|
|
|
|
clear_tv(get_vim_var_tv(VV_VAL));
|
|
|
|
restore_vimvar(VV_VAL, &save_val);
|
2022-01-23 17:59:06 +00:00
|
|
|
current_sctx = saved_sctx;
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "list" is supposed to contain two items: a word and a number. Return the
|
|
|
|
* word in "pp" and the number as the return value.
|
|
|
|
* Return -1 if anything isn't right.
|
|
|
|
* Used to get the good word and score from the eval_spell_expr() result.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
get_spellword(list_T *list, char_u **pp)
|
|
|
|
{
|
|
|
|
listitem_T *li;
|
|
|
|
|
|
|
|
li = list->lv_first;
|
|
|
|
if (li == NULL)
|
|
|
|
return -1;
|
|
|
|
*pp = tv_get_string(&li->li_tv);
|
|
|
|
|
|
|
|
li = li->li_next;
|
|
|
|
if (li == NULL)
|
|
|
|
return -1;
|
|
|
|
return (int)tv_get_number(&li->li_tv);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Prepare v: variable "idx" to be used.
|
2019-09-03 17:13:37 +02:00
|
|
|
* Save the current typeval in "save_tv" and clear it.
|
2019-08-29 22:09:46 +02:00
|
|
|
* When not used yet add the variable to the v: hashtable.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
prepare_vimvar(int idx, typval_T *save_tv)
|
|
|
|
{
|
|
|
|
*save_tv = vimvars[idx].vv_tv;
|
2019-09-03 17:13:37 +02:00
|
|
|
vimvars[idx].vv_str = NULL; // don't free it now
|
2021-12-24 21:36:12 +00:00
|
|
|
if (vimvars[idx].vv_tv_type == VAR_UNKNOWN)
|
2022-11-25 16:31:51 +00:00
|
|
|
hash_add(&vimvarht, vimvars[idx].vv_di.di_key, "prepare vimvar");
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore v: variable "idx" to typeval "save_tv".
|
2019-09-03 17:13:37 +02:00
|
|
|
* Note that the v: variable must have been cleared already.
|
2019-08-29 22:09:46 +02:00
|
|
|
* When no longer defined, remove the variable from the v: hashtable.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
restore_vimvar(int idx, typval_T *save_tv)
|
|
|
|
{
|
|
|
|
hashitem_T *hi;
|
|
|
|
|
|
|
|
vimvars[idx].vv_tv = *save_tv;
|
2023-01-02 16:54:53 +00:00
|
|
|
if (vimvars[idx].vv_tv_type != VAR_UNKNOWN)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
|
|
|
|
if (HASHITEM_EMPTY(hi))
|
|
|
|
internal_error("restore_vimvar()");
|
|
|
|
else
|
|
|
|
hash_remove(&vimvarht, hi, "restore vimvar");
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List Vim variables.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_vim_vars(int *first)
|
|
|
|
{
|
|
|
|
list_hashtable_vars(&vimvarht, "v:", FALSE, first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List script-local variables, if there is a script.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_script_vars(int *first)
|
|
|
|
{
|
2020-08-29 13:39:17 +02:00
|
|
|
if (SCRIPT_ID_VALID(current_sctx.sc_sid))
|
2019-08-29 22:09:46 +02:00
|
|
|
list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid),
|
|
|
|
"s:", FALSE, first);
|
|
|
|
}
|
|
|
|
|
2022-09-11 15:14:05 +01:00
|
|
|
/*
|
|
|
|
* Return TRUE if "name" starts with "g:", "w:", "t:" or "b:".
|
|
|
|
* But only when an identifier character follows.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
is_scoped_variable(char_u *name)
|
|
|
|
{
|
|
|
|
return vim_strchr((char_u *)"gwbt", name[0]) != NULL
|
|
|
|
&& name[1] == ':'
|
|
|
|
&& eval_isnamec(name[2]);
|
|
|
|
}
|
|
|
|
|
2022-04-17 12:47:40 +01:00
|
|
|
/*
|
2022-05-10 13:24:30 +01:00
|
|
|
* Evaluate one Vim expression {expr} in string "p" and append the
|
|
|
|
* resulting string to "gap". "p" points to the opening "{".
|
2022-05-10 18:11:43 +01:00
|
|
|
* When "evaluate" is FALSE only skip over the expression.
|
2022-05-10 13:24:30 +01:00
|
|
|
* Return a pointer to the character after "}", NULL for an error.
|
|
|
|
*/
|
|
|
|
char_u *
|
2022-05-10 18:11:43 +01:00
|
|
|
eval_one_expr_in_str(char_u *p, garray_T *gap, int evaluate)
|
2022-05-10 13:24:30 +01:00
|
|
|
{
|
|
|
|
char_u *block_start = skipwhite(p + 1); // skip the opening {
|
|
|
|
char_u *block_end = block_start;
|
|
|
|
char_u *expr_val;
|
|
|
|
|
|
|
|
if (*block_start == NUL)
|
|
|
|
{
|
|
|
|
semsg(_(e_missing_close_curly_str), p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (skip_expr(&block_end, NULL) == FAIL)
|
|
|
|
return NULL;
|
|
|
|
block_end = skipwhite(block_end);
|
|
|
|
if (*block_end != '}')
|
|
|
|
{
|
|
|
|
semsg(_(e_missing_close_curly_str), p);
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-05-10 18:11:43 +01:00
|
|
|
if (evaluate)
|
|
|
|
{
|
|
|
|
*block_end = NUL;
|
2022-10-01 19:43:52 +01:00
|
|
|
expr_val = eval_to_string(block_start, TRUE, FALSE);
|
2022-05-10 18:11:43 +01:00
|
|
|
*block_end = '}';
|
|
|
|
if (expr_val == NULL)
|
|
|
|
return NULL;
|
|
|
|
ga_concat(gap, expr_val);
|
|
|
|
vim_free(expr_val);
|
|
|
|
}
|
2022-05-10 13:24:30 +01:00
|
|
|
|
|
|
|
return block_end + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Evaluate all the Vim expressions {expr} in "str" and return the resulting
|
|
|
|
* string in allocated memory. "{{" is reduced to "{" and "}}" to "}".
|
|
|
|
* Used for a heredoc assignment.
|
|
|
|
* Returns NULL for an error.
|
2022-04-17 12:47:40 +01:00
|
|
|
*/
|
2022-06-29 12:55:36 +01:00
|
|
|
static char_u *
|
2022-04-17 12:47:40 +01:00
|
|
|
eval_all_expr_in_str(char_u *str)
|
|
|
|
{
|
|
|
|
garray_T ga;
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
ga_init2(&ga, 1, 80);
|
|
|
|
p = str;
|
|
|
|
|
|
|
|
while (*p != NUL)
|
|
|
|
{
|
2022-05-06 13:14:50 +01:00
|
|
|
char_u *lit_start;
|
|
|
|
int escaped_brace = FALSE;
|
|
|
|
|
|
|
|
// Look for a block start.
|
|
|
|
lit_start = p;
|
|
|
|
while (*p != '{' && *p != '}' && *p != NUL)
|
|
|
|
++p;
|
|
|
|
|
|
|
|
if (*p != NUL && *p == p[1])
|
|
|
|
{
|
|
|
|
// Escaped brace, unescape and continue.
|
|
|
|
// Include the brace in the literal string.
|
|
|
|
++p;
|
|
|
|
escaped_brace = TRUE;
|
|
|
|
}
|
|
|
|
else if (*p == '}')
|
|
|
|
{
|
|
|
|
semsg(_(e_stray_closing_curly_str), str);
|
|
|
|
ga_clear(&ga);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the literal part.
|
|
|
|
ga_concat_len(&ga, lit_start, (size_t)(p - lit_start));
|
|
|
|
|
2022-04-17 12:47:40 +01:00
|
|
|
if (*p == NUL)
|
2022-05-06 13:14:50 +01:00
|
|
|
break;
|
2022-04-17 12:47:40 +01:00
|
|
|
|
2022-05-06 13:14:50 +01:00
|
|
|
if (escaped_brace)
|
|
|
|
{
|
|
|
|
// Skip the second brace.
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
}
|
2022-04-17 12:47:40 +01:00
|
|
|
|
2022-05-10 13:24:30 +01:00
|
|
|
// Evaluate the expression and append the result.
|
2022-05-10 18:11:43 +01:00
|
|
|
p = eval_one_expr_in_str(p, &ga, TRUE);
|
2022-05-10 13:24:30 +01:00
|
|
|
if (p == NULL)
|
2022-04-17 12:47:40 +01:00
|
|
|
{
|
2022-05-06 13:14:50 +01:00
|
|
|
ga_clear(&ga);
|
2022-04-17 12:47:40 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ga_append(&ga, NUL);
|
|
|
|
|
|
|
|
return ga.ga_data;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Get a list of lines from a HERE document. The here document is a list of
|
|
|
|
* lines surrounded by a marker.
|
|
|
|
* cmd << {marker}
|
|
|
|
* {line1}
|
|
|
|
* {line2}
|
|
|
|
* ....
|
|
|
|
* {marker}
|
|
|
|
*
|
|
|
|
* The {marker} is a string. If the optional 'trim' word is supplied before the
|
|
|
|
* marker, then the leading indentation before the lines (matching the
|
2022-04-21 23:30:15 +01:00
|
|
|
* indentation in the "cmd" line) is stripped.
|
2020-04-14 20:15:49 +02:00
|
|
|
*
|
|
|
|
* When getting lines for an embedded script (e.g. python, lua, perl, ruby,
|
2022-04-21 23:30:15 +01:00
|
|
|
* tcl, mzscheme), "script_get" is set to TRUE. In this case, if the marker is
|
2020-04-14 20:15:49 +02:00
|
|
|
* missing, then '.' is accepted as a marker.
|
|
|
|
*
|
2022-04-21 23:30:15 +01:00
|
|
|
* When compiling a heredoc assignment to a variable in a Vim9 def function,
|
|
|
|
* "vim9compile" is set to TRUE. In this case, instead of generating a list of
|
|
|
|
* string values from the heredoc, vim9 instructions are generated. On success
|
|
|
|
* the returned list will be empty.
|
|
|
|
*
|
2022-04-17 12:47:40 +01:00
|
|
|
* Returns a List with {lines} or NULL on failure.
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
2020-01-26 15:56:19 +01:00
|
|
|
list_T *
|
2022-04-21 23:30:15 +01:00
|
|
|
heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-04-18 15:45:23 +01:00
|
|
|
char_u *theline = NULL;
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *marker;
|
|
|
|
list_T *l;
|
|
|
|
char_u *p;
|
2022-04-17 12:47:40 +01:00
|
|
|
char_u *str;
|
2019-08-27 22:48:30 +02:00
|
|
|
int marker_indent_len = 0;
|
|
|
|
int text_indent_len = 0;
|
|
|
|
char_u *text_indent = NULL;
|
2020-04-14 20:15:49 +02:00
|
|
|
char_u dot[] = ".";
|
2020-09-27 14:22:48 +02:00
|
|
|
int comment_char = in_vim9script() ? '#' : '"';
|
2022-04-17 12:47:40 +01:00
|
|
|
int evalstr = FALSE;
|
|
|
|
int eval_failed = FALSE;
|
2022-04-21 23:30:15 +01:00
|
|
|
cctx_T *cctx = vim9compile ? eap->cookie : NULL;
|
|
|
|
int count = 0;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2023-12-19 20:53:07 +01:00
|
|
|
if (eap->ea_getline == NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_cannot_use_heredoc_here));
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for the optional 'trim' word before the marker
|
|
|
|
cmd = skipwhite(cmd);
|
2022-04-17 12:47:40 +01:00
|
|
|
|
|
|
|
while (TRUE)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-04-17 12:47:40 +01:00
|
|
|
if (STRNCMP(cmd, "trim", 4) == 0
|
|
|
|
&& (cmd[4] == NUL || VIM_ISWHITE(cmd[4])))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-04-17 12:47:40 +01:00
|
|
|
cmd = skipwhite(cmd + 4);
|
|
|
|
|
|
|
|
// Trim the indentation from all the lines in the here document.
|
|
|
|
// The amount of indentation trimmed is the same as the indentation
|
|
|
|
// of the first line after the :let command line. To find the end
|
|
|
|
// marker the indent of the :let command line is trimmed.
|
|
|
|
p = *eap->cmdlinep;
|
|
|
|
while (VIM_ISWHITE(*p))
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
marker_indent_len++;
|
|
|
|
}
|
|
|
|
text_indent_len = -1;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (STRNCMP(cmd, "eval", 4) == 0
|
|
|
|
&& (cmd[4] == NUL || VIM_ISWHITE(cmd[4])))
|
|
|
|
{
|
|
|
|
cmd = skipwhite(cmd + 4);
|
|
|
|
evalstr = TRUE;
|
|
|
|
continue;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2022-04-17 12:47:40 +01:00
|
|
|
break;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The marker is the next word.
|
2020-09-27 14:22:48 +02:00
|
|
|
if (*cmd != NUL && *cmd != comment_char)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
marker = skipwhite(cmd);
|
|
|
|
p = skiptowhite(marker);
|
2020-09-27 14:22:48 +02:00
|
|
|
if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-01 15:58:22 +00:00
|
|
|
semsg(_(e_trailing_characters_str), p);
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*p = NUL;
|
2020-05-01 14:10:13 +02:00
|
|
|
if (!script_get && vim_islower(*marker))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-12-31 18:49:43 +00:00
|
|
|
emsg(_(e_marker_cannot_start_with_lower_case_letter));
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-14 20:15:49 +02:00
|
|
|
// When getting lines for an embedded script, if the marker is missing,
|
|
|
|
// accept '.' as the marker.
|
|
|
|
if (script_get)
|
|
|
|
marker = dot;
|
|
|
|
else
|
|
|
|
{
|
2021-12-31 17:25:48 +00:00
|
|
|
emsg(_(e_missing_marker));
|
2020-04-14 20:15:49 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l = list_alloc();
|
|
|
|
if (l == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
int mi = 0;
|
|
|
|
int ti = 0;
|
|
|
|
|
2022-04-18 15:45:23 +01:00
|
|
|
vim_free(theline);
|
2023-12-19 20:53:07 +01:00
|
|
|
theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (theline == NULL)
|
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
semsg(_(e_missing_end_marker_str), marker);
|
2019-08-27 22:48:30 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// with "trim": skip the indent matching the :let line to find the
|
|
|
|
// marker
|
|
|
|
if (marker_indent_len > 0
|
|
|
|
&& STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0)
|
|
|
|
mi = marker_indent_len;
|
|
|
|
if (STRCMP(marker, theline + mi) == 0)
|
|
|
|
break;
|
|
|
|
|
2022-04-17 12:47:40 +01:00
|
|
|
// If expression evaluation failed in the heredoc, then skip till the
|
|
|
|
// end marker.
|
|
|
|
if (eval_failed)
|
|
|
|
continue;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (text_indent_len == -1 && *theline != NUL)
|
|
|
|
{
|
|
|
|
// set the text indent from the first line.
|
|
|
|
p = theline;
|
|
|
|
text_indent_len = 0;
|
|
|
|
while (VIM_ISWHITE(*p))
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
text_indent_len++;
|
|
|
|
}
|
|
|
|
text_indent = vim_strnsave(theline, text_indent_len);
|
|
|
|
}
|
|
|
|
// with "trim": skip the indent matching the first line
|
|
|
|
if (text_indent != NULL)
|
|
|
|
for (ti = 0; ti < text_indent_len; ++ti)
|
|
|
|
if (theline[ti] != text_indent[ti])
|
|
|
|
break;
|
|
|
|
|
2022-04-17 12:47:40 +01:00
|
|
|
str = theline + ti;
|
2022-04-21 23:30:15 +01:00
|
|
|
if (vim9compile)
|
2022-04-17 12:47:40 +01:00
|
|
|
{
|
2022-05-06 13:14:50 +01:00
|
|
|
if (compile_all_expr_in_str(str, evalstr, cctx) == FAIL)
|
2022-04-17 12:47:40 +01:00
|
|
|
{
|
2022-04-21 23:30:15 +01:00
|
|
|
vim_free(theline);
|
|
|
|
vim_free(text_indent);
|
|
|
|
return FAIL;
|
2022-04-17 12:47:40 +01:00
|
|
|
}
|
2022-04-21 23:30:15 +01:00
|
|
|
count++;
|
2022-04-17 12:47:40 +01:00
|
|
|
}
|
2022-04-21 23:30:15 +01:00
|
|
|
else
|
|
|
|
{
|
2022-04-28 16:51:41 +01:00
|
|
|
if (evalstr && !eap->skip)
|
2022-04-21 23:30:15 +01:00
|
|
|
{
|
|
|
|
str = eval_all_expr_in_str(str);
|
|
|
|
if (str == NULL)
|
|
|
|
{
|
|
|
|
// expression evaluation failed
|
|
|
|
eval_failed = TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
vim_free(theline);
|
|
|
|
theline = str;
|
|
|
|
}
|
2022-04-17 12:47:40 +01:00
|
|
|
|
2022-04-21 23:30:15 +01:00
|
|
|
if (list_append_string(l, str, -1) == FAIL)
|
|
|
|
break;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2022-04-18 15:45:23 +01:00
|
|
|
vim_free(theline);
|
2019-08-27 22:48:30 +02:00
|
|
|
vim_free(text_indent);
|
|
|
|
|
2022-04-21 23:30:15 +01:00
|
|
|
if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed)
|
|
|
|
generate_NEWLIST(cctx, count, FALSE);
|
|
|
|
|
2022-04-17 12:47:40 +01:00
|
|
|
if (eval_failed)
|
|
|
|
{
|
|
|
|
// expression evaluation in the heredoc failed
|
|
|
|
list_free(l);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
/*
|
|
|
|
* Vim9 variable declaration:
|
|
|
|
* ":var name"
|
|
|
|
* ":var name: type"
|
|
|
|
* ":var name = expr"
|
|
|
|
* ":var name: type = expr"
|
|
|
|
* etc.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_var(exarg_T *eap)
|
|
|
|
{
|
2022-03-05 11:05:57 +00:00
|
|
|
char_u *p = eap->cmd;
|
2022-03-05 11:37:48 +00:00
|
|
|
int has_var;
|
2022-03-05 11:05:57 +00:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
if (!in_vim9script())
|
|
|
|
{
|
|
|
|
semsg(_(e_str_cannot_be_used_in_legacy_vim_script), ":var");
|
|
|
|
return;
|
|
|
|
}
|
2022-03-05 11:37:48 +00:00
|
|
|
has_var = checkforcmd_noparen(&p, "var", 3);
|
|
|
|
if (current_sctx.sc_sid == 0 && has_var)
|
2022-03-03 17:05:35 +00:00
|
|
|
{
|
|
|
|
emsg(_(e_cannot_declare_variable_on_command_line));
|
|
|
|
return;
|
|
|
|
}
|
2020-09-26 15:09:30 +02:00
|
|
|
ex_let(eap);
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* ":let" list all variable values
|
|
|
|
* ":let var1 var2" list variable values
|
|
|
|
* ":let var = expr" assignment command.
|
|
|
|
* ":let var += expr" assignment command.
|
|
|
|
* ":let var -= expr" assignment command.
|
|
|
|
* ":let var *= expr" assignment command.
|
|
|
|
* ":let var /= expr" assignment command.
|
|
|
|
* ":let var %= expr" assignment command.
|
|
|
|
* ":let var .= expr" assignment command.
|
|
|
|
* ":let var ..= expr" assignment command.
|
|
|
|
* ":let [var1, var2] = expr" unpack list.
|
2020-01-26 15:56:19 +01:00
|
|
|
* ":let var =<< ..." heredoc
|
2020-02-26 13:43:51 +01:00
|
|
|
* ":let var: string" Vim9 declaration
|
2020-05-25 20:33:55 +02:00
|
|
|
*
|
2020-09-26 15:09:30 +02:00
|
|
|
* ":final var = expr" assignment command.
|
|
|
|
* ":final [var1, var2] = expr" unpack list.
|
|
|
|
*
|
2019-08-27 22:48:30 +02:00
|
|
|
* ":const" list all variable values
|
|
|
|
* ":const var1 var2" list variable values
|
|
|
|
* ":const var = expr" assignment command.
|
|
|
|
* ":const [var1, var2] = expr" unpack list.
|
|
|
|
*/
|
|
|
|
void
|
2020-05-25 20:33:55 +02:00
|
|
|
ex_let(exarg_T *eap)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
char_u *arg = eap->arg;
|
|
|
|
char_u *expr = NULL;
|
|
|
|
typval_T rettv;
|
|
|
|
int var_count = 0;
|
|
|
|
int semicolon = 0;
|
2020-07-23 13:11:37 +02:00
|
|
|
char_u op[4];
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *argend;
|
|
|
|
int first = TRUE;
|
|
|
|
int concat;
|
2020-05-14 22:41:15 +02:00
|
|
|
int has_assign;
|
2020-12-22 21:19:39 +01:00
|
|
|
int flags = 0;
|
2020-07-23 13:11:37 +02:00
|
|
|
int vim9script = in_vim9script();
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
if (eap->cmdidx == CMD_final && !vim9script)
|
|
|
|
{
|
2020-12-22 21:19:39 +01:00
|
|
|
// In legacy Vim script ":final" is short for ":finally".
|
|
|
|
ex_finally(eap);
|
|
|
|
return;
|
2020-09-26 15:09:30 +02:00
|
|
|
}
|
2020-10-21 20:58:52 +02:00
|
|
|
if (eap->cmdidx == CMD_let && vim9script)
|
2020-09-27 15:19:27 +02:00
|
|
|
{
|
|
|
|
emsg(_(e_cannot_use_let_in_vim9_script));
|
|
|
|
return;
|
|
|
|
}
|
2020-09-26 15:09:30 +02:00
|
|
|
|
2020-12-22 21:19:39 +01:00
|
|
|
if (eap->cmdidx == CMD_const)
|
|
|
|
flags |= ASSIGN_CONST;
|
|
|
|
else if (eap->cmdidx == CMD_final)
|
|
|
|
flags |= ASSIGN_FINAL;
|
|
|
|
|
|
|
|
// Vim9 assignment without ":let", ":const" or ":final"
|
2020-01-26 15:56:19 +01:00
|
|
|
if (eap->arg == eap->cmd)
|
2020-09-26 15:09:30 +02:00
|
|
|
flags |= ASSIGN_NO_DECL;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2020-06-14 23:05:10 +02:00
|
|
|
argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (argend == NULL)
|
|
|
|
return;
|
|
|
|
if (argend > arg && argend[-1] == '.') // for var.='str'
|
|
|
|
--argend;
|
|
|
|
expr = skipwhite(argend);
|
|
|
|
concat = expr[0] == '.'
|
2021-08-15 13:49:42 +02:00
|
|
|
&& ((expr[1] == '=' && in_old_script(2))
|
2019-08-27 22:48:30 +02:00
|
|
|
|| (expr[1] == '.' && expr[2] == '='));
|
2020-05-14 22:41:15 +02:00
|
|
|
has_assign = *expr == '=' || (vim_strchr((char_u *)"+-*/%", *expr) != NULL
|
|
|
|
&& expr[1] == '=');
|
2020-05-24 23:00:18 +02:00
|
|
|
if (!has_assign && !concat)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
// ":let" without "=": list variables
|
|
|
|
if (*arg == '[')
|
2021-12-31 22:49:24 +00:00
|
|
|
emsg(_(e_invalid_argument));
|
2020-07-23 13:11:37 +02:00
|
|
|
else if (expr[0] == '.' && expr[1] == '=')
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_dot_equal_not_supported_with_script_version_two));
|
2020-04-20 17:46:14 +02:00
|
|
|
else if (!ends_excmd2(eap->cmd, arg))
|
2020-06-13 18:09:19 +02:00
|
|
|
{
|
2020-07-23 13:11:37 +02:00
|
|
|
if (vim9script)
|
2020-06-13 18:09:19 +02:00
|
|
|
{
|
2021-03-26 21:27:52 +01:00
|
|
|
if (!ends_excmd2(eap->cmd, skipwhite(argend)))
|
2022-01-01 15:58:22 +00:00
|
|
|
semsg(_(e_trailing_characters_str), argend);
|
2021-03-26 21:27:52 +01:00
|
|
|
else
|
|
|
|
// Vim9 declaration ":var name: type"
|
|
|
|
arg = vim9_declare_scriptvar(eap, arg);
|
2020-06-13 18:09:19 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ":let var1 var2" - list values
|
|
|
|
arg = list_arg_vars(eap, arg, &first);
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else if (!eap->skip)
|
|
|
|
{
|
|
|
|
// ":let"
|
|
|
|
list_glob_vars(&first);
|
|
|
|
list_buf_vars(&first);
|
|
|
|
list_win_vars(&first);
|
|
|
|
list_tab_vars(&first);
|
|
|
|
list_script_vars(&first);
|
|
|
|
list_func_vars(&first);
|
|
|
|
list_vim_vars(&first);
|
|
|
|
}
|
2021-08-05 20:40:03 +02:00
|
|
|
set_nextcmd(eap, arg);
|
2023-04-12 21:36:03 +01:00
|
|
|
return;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-04-12 21:36:03 +01:00
|
|
|
|
|
|
|
if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<')
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-10-12 12:58:54 +01:00
|
|
|
list_T *l = NULL;
|
2021-07-28 21:25:49 +02:00
|
|
|
long cur_lnum = SOURCING_LNUM;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2022-10-12 12:58:54 +01:00
|
|
|
// :let text =<< [trim] [eval] END
|
|
|
|
// :var text =<< [trim] [eval] END
|
|
|
|
if (vim9script && !eap->skip && (!VIM_ISWHITE(expr[-1])
|
|
|
|
|| !IS_WHITE_OR_NUL(expr[3])))
|
|
|
|
semsg(_(e_white_space_required_before_and_after_str_at_str),
|
|
|
|
"=<<", expr);
|
|
|
|
else
|
|
|
|
l = heredoc_get(eap, expr + 3, FALSE, FALSE);
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (l != NULL)
|
|
|
|
{
|
|
|
|
rettv_list_set(&rettv, l);
|
2019-10-16 23:34:42 +02:00
|
|
|
if (!eap->skip)
|
|
|
|
{
|
2021-07-28 21:25:49 +02:00
|
|
|
// errors are for the assignment, not the end marker
|
|
|
|
SOURCING_LNUM = cur_lnum;
|
2019-10-16 23:34:42 +02:00
|
|
|
op[0] = '=';
|
|
|
|
op[1] = NUL;
|
|
|
|
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
|
2020-01-26 15:56:19 +01:00
|
|
|
flags, op);
|
2019-10-16 23:34:42 +02:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
clear_tv(&rettv);
|
|
|
|
}
|
2023-04-12 21:36:03 +01:00
|
|
|
return;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2020-05-14 22:41:15 +02:00
|
|
|
|
2023-04-12 21:36:03 +01:00
|
|
|
evalarg_T evalarg;
|
|
|
|
int len = 1;
|
2021-01-23 13:39:14 +01:00
|
|
|
|
2023-04-12 21:36:03 +01:00
|
|
|
CLEAR_FIELD(rettv);
|
2020-07-23 13:11:37 +02:00
|
|
|
|
2023-04-12 21:36:03 +01:00
|
|
|
int cur_lnum;
|
2020-05-14 22:41:15 +02:00
|
|
|
|
2023-04-12 21:36:03 +01:00
|
|
|
op[0] = '=';
|
|
|
|
op[1] = NUL;
|
|
|
|
if (*expr != '=')
|
|
|
|
{
|
|
|
|
if (vim9script && (flags & ASSIGN_NO_DECL) == 0)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-04-12 21:36:03 +01:00
|
|
|
// +=, /=, etc. require an existing variable
|
|
|
|
semsg(_(e_cannot_use_operator_on_new_variable_str), eap->arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-04-12 21:36:03 +01:00
|
|
|
else if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-04-12 21:36:03 +01:00
|
|
|
op[0] = *expr; // +=, -=, *=, /=, %= or .=
|
|
|
|
++len;
|
|
|
|
if (expr[0] == '.' && expr[1] == '.') // ..=
|
|
|
|
{
|
|
|
|
++expr;
|
|
|
|
++len;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-04-12 21:36:03 +01:00
|
|
|
expr += 2;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-04-12 21:36:03 +01:00
|
|
|
else
|
|
|
|
++expr;
|
|
|
|
|
|
|
|
if (vim9script && !eap->skip && (!VIM_ISWHITE(*argend)
|
|
|
|
|| !IS_WHITE_OR_NUL(*expr)))
|
|
|
|
{
|
|
|
|
vim_strncpy(op, expr - len, len);
|
|
|
|
semsg(_(e_white_space_required_before_and_after_str_at_str),
|
|
|
|
op, argend);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eap->skip)
|
|
|
|
++emsg_skip;
|
|
|
|
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
|
|
|
|
expr = skipwhite_and_linebreak(expr, &evalarg);
|
|
|
|
cur_lnum = SOURCING_LNUM;
|
|
|
|
int eval_res = eval0(expr, &rettv, eap, &evalarg);
|
|
|
|
if (eap->skip)
|
|
|
|
--emsg_skip;
|
|
|
|
clear_evalarg(&evalarg, eap);
|
|
|
|
|
|
|
|
// Restore the line number so that any type error is given for the
|
|
|
|
// declaration, not the expression.
|
|
|
|
SOURCING_LNUM = cur_lnum;
|
|
|
|
|
|
|
|
if (!eap->skip && eval_res != FAIL)
|
|
|
|
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
|
|
|
|
flags, op);
|
|
|
|
if (eval_res != FAIL)
|
|
|
|
clear_tv(&rettv);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-03-04 12:38:21 +01:00
|
|
|
* Assign the typeval "tv" to the variable or variables at "arg_start".
|
2019-08-27 22:48:30 +02:00
|
|
|
* Handles both "var" with any type and "[var, var; var]" with a list type.
|
|
|
|
* When "op" is not NULL it points to a string with characters that
|
|
|
|
* must appear after the variable(s). Use "+", "-" or "." for add, subtract
|
|
|
|
* or concatenate.
|
|
|
|
* Returns OK or FAIL;
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ex_let_vars(
|
|
|
|
char_u *arg_start,
|
|
|
|
typval_T *tv,
|
|
|
|
int copy, // copy values from "tv", don't move
|
|
|
|
int semicolon, // from skip_var_list()
|
|
|
|
int var_count, // from skip_var_list()
|
2021-01-01 21:05:55 +01:00
|
|
|
int flags, // ASSIGN_FINAL, ASSIGN_CONST, etc.
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *op)
|
|
|
|
{
|
|
|
|
char_u *arg = arg_start;
|
|
|
|
list_T *l;
|
|
|
|
int i;
|
2021-02-11 21:19:34 +01:00
|
|
|
int var_idx = 0;
|
2019-08-27 22:48:30 +02:00
|
|
|
listitem_T *item;
|
|
|
|
typval_T ltv;
|
|
|
|
|
2022-03-08 13:18:55 +00:00
|
|
|
if (tv->v_type == VAR_VOID)
|
|
|
|
{
|
|
|
|
emsg(_(e_cannot_use_void_value));
|
|
|
|
return FAIL;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
if (*arg != '[')
|
|
|
|
{
|
|
|
|
// ":let var = expr" or ":for var in list"
|
2021-02-11 21:19:34 +01:00
|
|
|
if (ex_let_one(arg, tv, copy, flags, op, op, var_idx) == NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
|
|
|
|
if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL)
|
|
|
|
{
|
2022-01-01 14:19:49 +00:00
|
|
|
emsg(_(e_list_required));
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = list_len(l);
|
|
|
|
if (semicolon == 0 && var_count < i)
|
|
|
|
{
|
2022-01-04 21:30:47 +00:00
|
|
|
emsg(_(e_less_targets_than_list_items));
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
if (var_count - semicolon > i)
|
|
|
|
{
|
2022-01-04 21:30:47 +00:00
|
|
|
emsg(_(e_more_targets_than_list_items));
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2020-05-13 22:44:22 +02:00
|
|
|
CHECK_LIST_MATERIALIZE(l);
|
2019-08-27 22:48:30 +02:00
|
|
|
item = l->lv_first;
|
|
|
|
while (*arg != ']')
|
|
|
|
{
|
|
|
|
arg = skipwhite(arg + 1);
|
2021-02-11 21:19:34 +01:00
|
|
|
++var_idx;
|
2021-04-10 22:35:43 +02:00
|
|
|
arg = ex_let_one(arg, &item->li_tv, TRUE,
|
|
|
|
flags | ASSIGN_UNPACK, (char_u *)",;]", op, var_idx);
|
2019-08-27 22:48:30 +02:00
|
|
|
item = item->li_next;
|
|
|
|
if (arg == NULL)
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
arg = skipwhite(arg);
|
|
|
|
if (*arg == ';')
|
|
|
|
{
|
|
|
|
// Put the rest of the list (may be empty) in the var after ';'.
|
|
|
|
// Create a new list for this.
|
|
|
|
l = list_alloc();
|
|
|
|
if (l == NULL)
|
|
|
|
return FAIL;
|
|
|
|
while (item != NULL)
|
|
|
|
{
|
|
|
|
list_append_tv(l, &item->li_tv);
|
|
|
|
item = item->li_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
ltv.v_type = VAR_LIST;
|
|
|
|
ltv.v_lock = 0;
|
|
|
|
ltv.vval.v_list = l;
|
|
|
|
l->lv_refcount = 1;
|
2021-02-11 21:19:34 +01:00
|
|
|
++var_idx;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-04-10 22:35:43 +02:00
|
|
|
arg = ex_let_one(skipwhite(arg + 1), <v, FALSE,
|
|
|
|
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
|
2019-08-27 22:48:30 +02:00
|
|
|
clear_tv(<v);
|
|
|
|
if (arg == NULL)
|
|
|
|
return FAIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (*arg != ',' && *arg != ']')
|
|
|
|
{
|
|
|
|
internal_error("ex_let_vars()");
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip over assignable variable "var" or list of variables "[var, var]".
|
|
|
|
* Used for ":let varvar = expr" and ":for varvar in expr".
|
|
|
|
* For "[var, var]" increment "*var_count" for each variable.
|
2020-06-14 23:05:10 +02:00
|
|
|
* for "[var, var; var]" set "semicolon" to 1.
|
|
|
|
* If "silent" is TRUE do not give an "invalid argument" error message.
|
2019-08-27 22:48:30 +02:00
|
|
|
* Return NULL for an error.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
skip_var_list(
|
|
|
|
char_u *arg,
|
2020-01-26 15:56:19 +01:00
|
|
|
int include_type,
|
2019-08-27 22:48:30 +02:00
|
|
|
int *var_count,
|
2020-06-14 23:05:10 +02:00
|
|
|
int *semicolon,
|
|
|
|
int silent)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
char_u *p, *s;
|
|
|
|
|
|
|
|
if (*arg == '[')
|
|
|
|
{
|
|
|
|
// "[var, var]": find the matching ']'.
|
|
|
|
p = arg;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
|
2021-01-17 20:23:38 +01:00
|
|
|
s = skip_var_one(p, include_type);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (s == p)
|
|
|
|
{
|
2020-06-14 23:05:10 +02:00
|
|
|
if (!silent)
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), p);
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
++*var_count;
|
|
|
|
|
|
|
|
p = skipwhite(s);
|
|
|
|
if (*p == ']')
|
|
|
|
break;
|
|
|
|
else if (*p == ';')
|
|
|
|
{
|
|
|
|
if (*semicolon == 1)
|
|
|
|
{
|
2022-02-15 21:17:56 +00:00
|
|
|
if (!silent)
|
|
|
|
emsg(_(e_double_semicolon_in_list_of_variables));
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*semicolon = 1;
|
|
|
|
}
|
|
|
|
else if (*p != ',')
|
|
|
|
{
|
2020-06-14 23:05:10 +02:00
|
|
|
if (!silent)
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), p);
|
2019-08-27 22:48:30 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p + 1;
|
|
|
|
}
|
2022-10-13 17:47:42 +01:00
|
|
|
|
2022-09-21 18:59:14 +01:00
|
|
|
return skip_var_one(arg, include_type);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
|
|
|
|
* l[idx].
|
2020-01-26 15:56:19 +01:00
|
|
|
* In Vim9 script also skip over ": type" if "include_type" is TRUE.
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
2020-06-14 23:05:10 +02:00
|
|
|
char_u *
|
2020-01-26 15:56:19 +01:00
|
|
|
skip_var_one(char_u *arg, int include_type)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-01-17 20:52:13 +01:00
|
|
|
char_u *end;
|
|
|
|
int vim9 = in_vim9script();
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (*arg == '@' && arg[1] != NUL)
|
|
|
|
return arg + 2;
|
2022-11-16 20:33:21 +00:00
|
|
|
|
|
|
|
// termcap option name may have non-alpha characters
|
|
|
|
if (STRNCMP(arg, "&t_", 3) == 0 && arg[3] != NUL && arg[4] != NUL)
|
|
|
|
return arg + 5;
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
end = find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
|
2019-08-27 22:48:30 +02:00
|
|
|
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
|
2021-01-17 20:23:38 +01:00
|
|
|
|
|
|
|
// "a: type" is declaring variable "a" with a type, not "a:".
|
|
|
|
// Same for "s: type".
|
2021-01-17 20:52:13 +01:00
|
|
|
if (vim9 && end == arg + 2 && end[-1] == ':')
|
2021-01-17 20:23:38 +01:00
|
|
|
--end;
|
|
|
|
|
2021-01-17 20:52:13 +01:00
|
|
|
if (include_type && vim9)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2023-01-30 21:12:34 +00:00
|
|
|
if (*skipwhite(end) == ':')
|
|
|
|
end = skip_type(skipwhite(skipwhite(end) + 1), FALSE);
|
2020-01-26 15:56:19 +01:00
|
|
|
}
|
|
|
|
return end;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List variables for hashtab "ht" with prefix "prefix".
|
|
|
|
* If "empty" is TRUE also list NULL strings as empty strings.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
list_hashtable_vars(
|
|
|
|
hashtab_T *ht,
|
|
|
|
char *prefix,
|
|
|
|
int empty,
|
|
|
|
int *first)
|
|
|
|
{
|
|
|
|
hashitem_T *hi;
|
|
|
|
dictitem_T *di;
|
|
|
|
int todo;
|
|
|
|
char_u buf[IOSIZE];
|
|
|
|
|
2022-11-25 16:31:51 +00:00
|
|
|
int save_ht_flags = ht->ht_flags;
|
|
|
|
ht->ht_flags |= HTFLAGS_FROZEN;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
todo = (int)ht->ht_used;
|
|
|
|
for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
|
|
|
|
{
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
--todo;
|
|
|
|
di = HI2DI(hi);
|
|
|
|
|
|
|
|
// apply :filter /pat/ to variable name
|
|
|
|
vim_strncpy((char_u *)buf, (char_u *)prefix, IOSIZE - 1);
|
|
|
|
vim_strcat((char_u *)buf, di->di_key, IOSIZE);
|
|
|
|
if (message_filtered(buf))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (empty || di->di_tv.v_type != VAR_STRING
|
|
|
|
|| di->di_tv.vval.v_string != NULL)
|
|
|
|
list_one_var(di, prefix, first);
|
|
|
|
}
|
|
|
|
}
|
2022-11-25 16:31:51 +00:00
|
|
|
|
|
|
|
ht->ht_flags = save_ht_flags;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List global variables.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_glob_vars(int *first)
|
|
|
|
{
|
|
|
|
list_hashtable_vars(&globvarht, "", TRUE, first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List buffer variables.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_buf_vars(int *first)
|
|
|
|
{
|
|
|
|
list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", TRUE, first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List window variables.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_win_vars(int *first)
|
|
|
|
{
|
|
|
|
list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", TRUE, first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List tab page variables.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_tab_vars(int *first)
|
|
|
|
{
|
|
|
|
list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", TRUE, first);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List variables in "arg".
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
list_arg_vars(exarg_T *eap, char_u *arg, int *first)
|
|
|
|
{
|
|
|
|
int error = FALSE;
|
|
|
|
int len;
|
|
|
|
char_u *name;
|
|
|
|
char_u *name_start;
|
|
|
|
char_u *arg_subsc;
|
|
|
|
char_u *tofree;
|
|
|
|
typval_T tv;
|
|
|
|
|
2020-04-20 17:46:14 +02:00
|
|
|
while (!ends_excmd2(eap->cmd, arg) && !got_int)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (error || eap->skip)
|
|
|
|
{
|
|
|
|
arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
|
|
|
|
if (!VIM_ISWHITE(*arg) && !ends_excmd(*arg))
|
|
|
|
{
|
|
|
|
emsg_severe = TRUE;
|
2021-08-14 14:59:27 +02:00
|
|
|
if (!did_emsg)
|
2022-01-01 15:58:22 +00:00
|
|
|
semsg(_(e_trailing_characters_str), arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get_name_len() takes care of expanding curly braces
|
|
|
|
name_start = name = arg;
|
|
|
|
len = get_name_len(&arg, &tofree, TRUE, TRUE);
|
|
|
|
if (len <= 0)
|
|
|
|
{
|
|
|
|
// This is mainly to keep test 49 working: when expanding
|
|
|
|
// curly braces fails overrule the exception error message.
|
|
|
|
if (len < 0 && !aborting())
|
|
|
|
{
|
|
|
|
emsg_severe = TRUE;
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-05 10:53:21 +02:00
|
|
|
arg = skipwhite(arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (tofree != NULL)
|
|
|
|
name = tofree;
|
2022-01-06 21:10:28 +00:00
|
|
|
if (eval_variable(name, len, 0, &tv, NULL,
|
2021-03-13 20:57:19 +01:00
|
|
|
EVAL_VAR_VERBOSE) == FAIL)
|
2019-08-27 22:48:30 +02:00
|
|
|
error = TRUE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// handle d.key, l[idx], f(expr)
|
|
|
|
arg_subsc = arg;
|
2022-01-07 12:45:29 +00:00
|
|
|
if (handle_subscript(&arg, name_start, &tv,
|
|
|
|
&EVALARG_EVALUATE, TRUE) == FAIL)
|
2019-08-27 22:48:30 +02:00
|
|
|
error = TRUE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (arg == arg_subsc && len == 2 && name[1] == ':')
|
|
|
|
{
|
|
|
|
switch (*name)
|
|
|
|
{
|
|
|
|
case 'g': list_glob_vars(first); break;
|
|
|
|
case 'b': list_buf_vars(first); break;
|
|
|
|
case 'w': list_win_vars(first); break;
|
|
|
|
case 't': list_tab_vars(first); break;
|
|
|
|
case 'v': list_vim_vars(first); break;
|
|
|
|
case 's': list_script_vars(first); break;
|
|
|
|
case 'l': list_func_vars(first); break;
|
|
|
|
default:
|
2022-01-04 21:30:47 +00:00
|
|
|
semsg(_(e_cant_list_variables_for_str), name);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char_u numbuf[NUMBUFLEN];
|
|
|
|
char_u *tf;
|
|
|
|
int c;
|
|
|
|
char_u *s;
|
|
|
|
|
|
|
|
s = echo_string(&tv, &tf, numbuf, 0);
|
|
|
|
c = *arg;
|
|
|
|
*arg = NUL;
|
|
|
|
list_one_var_a("",
|
|
|
|
arg == arg_subsc ? name : name_start,
|
|
|
|
tv.v_type,
|
|
|
|
s == NULL ? (char_u *)"" : s,
|
|
|
|
first);
|
|
|
|
*arg = c;
|
|
|
|
vim_free(tf);
|
|
|
|
}
|
|
|
|
clear_tv(&tv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(tofree);
|
|
|
|
}
|
|
|
|
|
|
|
|
arg = skipwhite(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-11-28 19:53:42 +00:00
|
|
|
* Set an environment variable, part of ex_let_one().
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
|
|
|
static char_u *
|
2021-11-28 19:53:42 +00:00
|
|
|
ex_let_env(
|
|
|
|
char_u *arg,
|
|
|
|
typval_T *tv,
|
|
|
|
int flags,
|
|
|
|
char_u *endchars,
|
|
|
|
char_u *op)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
char_u *arg_end = NULL;
|
2021-11-28 19:53:42 +00:00
|
|
|
char_u *name;
|
2019-08-27 22:48:30 +02:00
|
|
|
int len;
|
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
|
|
|
|
&& (flags & ASSIGN_FOR_LOOP) == 0)
|
2020-08-02 16:59:00 +02:00
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_cannot_lock_environment_variable));
|
2020-08-02 16:59:00 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
// Find the end of the name.
|
|
|
|
++arg;
|
|
|
|
name = arg;
|
|
|
|
len = get_env_len(&arg);
|
|
|
|
if (len == 0)
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), name - 1);
|
2021-11-28 19:53:42 +00:00
|
|
|
else
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL)
|
2022-01-01 14:19:49 +00:00
|
|
|
semsg(_(e_wrong_variable_type_for_str_equal), op);
|
2021-11-28 19:53:42 +00:00
|
|
|
else if (endchars != NULL
|
|
|
|
&& vim_strchr(endchars, *skipwhite(arg)) == NULL)
|
|
|
|
emsg(_(e_unexpected_characters_in_let));
|
|
|
|
else if (!check_secure())
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
char_u *tofree = NULL;
|
|
|
|
int c1 = name[len];
|
|
|
|
char_u *p;
|
2020-06-21 15:52:59 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
name[len] = NUL;
|
|
|
|
p = tv_get_string_chk(tv);
|
|
|
|
if (p != NULL && op != NULL && *op == '.')
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
int mustfree = FALSE;
|
|
|
|
char_u *s = vim_getenv(name, &mustfree);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
if (s != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
p = tofree = concat_str(s, p);
|
|
|
|
if (mustfree)
|
|
|
|
vim_free(s);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 19:53:42 +00:00
|
|
|
if (p != NULL)
|
|
|
|
{
|
|
|
|
vim_setenv_ext(name, p);
|
|
|
|
arg_end = arg;
|
|
|
|
}
|
|
|
|
name[len] = c1;
|
|
|
|
vim_free(tofree);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 19:53:42 +00:00
|
|
|
return arg_end;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
/*
|
|
|
|
* Set an option, part of ex_let_one().
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
ex_let_option(
|
|
|
|
char_u *arg,
|
|
|
|
typval_T *tv,
|
|
|
|
int flags,
|
|
|
|
char_u *endchars,
|
|
|
|
char_u *op)
|
|
|
|
{
|
|
|
|
char_u *p;
|
2021-12-06 11:03:55 +00:00
|
|
|
int scope;
|
2021-11-28 19:53:42 +00:00
|
|
|
char_u *arg_end = NULL;
|
|
|
|
|
|
|
|
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
|
|
|
|
&& (flags & ASSIGN_FOR_LOOP) == 0)
|
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_cannot_lock_option));
|
2021-11-28 19:53:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the end of the name.
|
2021-12-06 11:03:55 +00:00
|
|
|
p = find_option_end(&arg, &scope);
|
2021-11-28 19:53:42 +00:00
|
|
|
if (p == NULL || (endchars != NULL
|
|
|
|
&& vim_strchr(endchars, *skipwhite(p)) == NULL))
|
2023-06-14 16:39:54 +01:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
emsg(_(e_unexpected_characters_in_let));
|
2023-06-14 16:39:54 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int c1;
|
|
|
|
long n = 0;
|
|
|
|
getoption_T opt_type;
|
|
|
|
long numval;
|
|
|
|
char_u *stringval = NULL;
|
|
|
|
char_u *s = NULL;
|
|
|
|
int failed = FALSE;
|
|
|
|
int opt_p_flags;
|
|
|
|
char_u *tofree = NULL;
|
|
|
|
char_u numbuf[NUMBUFLEN];
|
|
|
|
|
|
|
|
c1 = *p;
|
|
|
|
*p = NUL;
|
|
|
|
|
|
|
|
opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
|
|
|
|
if (opt_type == gov_unknown && arg[0] != 't' && arg[1] != '_')
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
semsg(_(e_unknown_option_str_2), arg);
|
|
|
|
goto theend;
|
|
|
|
}
|
|
|
|
if (op != NULL && *op != '='
|
|
|
|
&& (((opt_type == gov_bool || opt_type == gov_number) && *op == '.')
|
|
|
|
|| (opt_type == gov_string && *op != '.')))
|
|
|
|
{
|
|
|
|
semsg(_(e_wrong_variable_type_for_str_equal), op);
|
|
|
|
goto theend;
|
|
|
|
}
|
2021-11-28 19:53:42 +00:00
|
|
|
|
2023-06-14 16:39:54 +01:00
|
|
|
if ((opt_type == gov_bool
|
|
|
|
|| opt_type == gov_number
|
|
|
|
|| opt_type == gov_hidden_bool
|
|
|
|
|| opt_type == gov_hidden_number)
|
|
|
|
&& (tv->v_type != VAR_STRING || !in_vim9script()))
|
|
|
|
{
|
|
|
|
if (opt_type == gov_bool || opt_type == gov_hidden_bool)
|
|
|
|
// bool, possibly hidden
|
|
|
|
n = (long)tv_get_bool_chk(tv, &failed);
|
|
|
|
else
|
|
|
|
// number, possibly hidden
|
|
|
|
n = (long)tv_get_number_chk(tv, &failed);
|
|
|
|
if (failed)
|
|
|
|
goto theend;
|
|
|
|
}
|
2020-08-16 21:29:05 +02:00
|
|
|
|
2023-06-14 16:39:54 +01:00
|
|
|
if ((opt_p_flags & P_FUNC) && (tv->v_type == VAR_PARTIAL
|
|
|
|
|| tv->v_type == VAR_FUNC))
|
|
|
|
{
|
|
|
|
// If the option can be set to a function reference or a lambda
|
|
|
|
// and the passed value is a function reference, then convert it to
|
|
|
|
// the name (string) of the function reference.
|
|
|
|
s = tv2string(tv, &tofree, numbuf, 0);
|
|
|
|
if (s == NULL)
|
|
|
|
goto theend;
|
|
|
|
}
|
|
|
|
// Avoid setting a string option to the text "v:false" or similar.
|
|
|
|
// In Vim9 script also don't convert a number to string.
|
|
|
|
else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL
|
|
|
|
&& (!in_vim9script() || tv->v_type != VAR_NUMBER))
|
|
|
|
{
|
|
|
|
s = tv_get_string_chk(tv);
|
|
|
|
if (s == NULL)
|
|
|
|
goto theend;
|
|
|
|
}
|
|
|
|
else if (opt_type == gov_string || opt_type == gov_hidden_string)
|
|
|
|
{
|
|
|
|
emsg(_(e_string_required));
|
|
|
|
goto theend;
|
|
|
|
}
|
2020-08-16 21:29:05 +02:00
|
|
|
|
2023-06-14 16:39:54 +01:00
|
|
|
if (op != NULL && *op != '=')
|
|
|
|
{
|
|
|
|
// number, in legacy script also bool
|
|
|
|
if (opt_type == gov_number
|
|
|
|
|| (opt_type == gov_bool && !in_vim9script()))
|
2021-11-28 19:53:42 +00:00
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
switch (*op)
|
2021-11-28 19:53:42 +00:00
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
case '+': n = numval + n; break;
|
|
|
|
case '-': n = numval - n; break;
|
|
|
|
case '*': n = numval * n; break;
|
|
|
|
case '/': n = (long)num_divide(numval, n, &failed); break;
|
|
|
|
case '%': n = (long)num_modulus(numval, n, &failed); break;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-06-14 16:39:54 +01:00
|
|
|
s = NULL;
|
|
|
|
if (failed)
|
|
|
|
goto theend;
|
2021-11-28 19:53:42 +00:00
|
|
|
}
|
2023-06-14 16:39:54 +01:00
|
|
|
else if (opt_type == gov_string && stringval != NULL && s != NULL)
|
2021-11-28 19:53:42 +00:00
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
// string
|
|
|
|
s = concat_str(stringval, s);
|
|
|
|
vim_free(stringval);
|
|
|
|
stringval = s;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-14 16:39:54 +01:00
|
|
|
|
|
|
|
char *err = set_option_value(arg, n, s, scope);
|
|
|
|
arg_end = p;
|
|
|
|
if (err != NULL)
|
|
|
|
emsg(_(err));
|
|
|
|
|
|
|
|
theend:
|
|
|
|
*p = c1;
|
|
|
|
vim_free(stringval);
|
|
|
|
vim_free(tofree);
|
2021-11-28 19:53:42 +00:00
|
|
|
return arg_end;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
/*
|
|
|
|
* Set a register, part of ex_let_one().
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
ex_let_register(
|
|
|
|
char_u *arg,
|
|
|
|
typval_T *tv,
|
|
|
|
int flags,
|
|
|
|
char_u *endchars,
|
|
|
|
char_u *op)
|
|
|
|
{
|
|
|
|
char_u *arg_end = NULL;
|
|
|
|
|
|
|
|
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
|
|
|
|
&& (flags & ASSIGN_FOR_LOOP) == 0)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_cannot_lock_register));
|
2021-11-28 19:53:42 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
++arg;
|
|
|
|
if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL)
|
2022-01-01 14:19:49 +00:00
|
|
|
semsg(_(e_wrong_variable_type_for_str_equal), op);
|
2021-11-28 19:53:42 +00:00
|
|
|
else if (endchars != NULL
|
|
|
|
&& vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
|
|
|
|
emsg(_(e_unexpected_characters_in_let));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char_u *ptofree = NULL;
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
p = tv_get_string_chk(tv);
|
|
|
|
if (p != NULL && op != NULL && *op == '.')
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
char_u *s = get_reg_contents(*arg == '@'
|
|
|
|
? '"' : *arg, GREG_EXPR_SRC);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
if (s != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-11-28 19:53:42 +00:00
|
|
|
p = ptofree = concat_str(s, p);
|
|
|
|
vim_free(s);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-28 19:53:42 +00:00
|
|
|
if (p != NULL)
|
|
|
|
{
|
|
|
|
write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
|
|
|
|
arg_end = arg + 1;
|
|
|
|
}
|
|
|
|
vim_free(ptofree);
|
|
|
|
}
|
|
|
|
return arg_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value.
|
|
|
|
* Returns a pointer to the char just after the var name.
|
|
|
|
* Returns NULL if there is an error.
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
ex_let_one(
|
|
|
|
char_u *arg, // points to variable name
|
|
|
|
typval_T *tv, // value to assign to variable
|
|
|
|
int copy, // copy value from "tv"
|
|
|
|
int flags, // ASSIGN_CONST, ASSIGN_FINAL, etc.
|
|
|
|
char_u *endchars, // valid chars after variable name or NULL
|
|
|
|
char_u *op, // "+", "-", "." or NULL
|
|
|
|
int var_idx) // variable index for "let [a, b] = list"
|
|
|
|
{
|
|
|
|
char_u *arg_end = NULL;
|
|
|
|
|
|
|
|
if (in_vim9script() && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
|
|
|
|
&& (flags & (ASSIGN_CONST | ASSIGN_FINAL)) == 0
|
|
|
|
&& vim_strchr((char_u *)"$@&", *arg) != NULL)
|
|
|
|
{
|
|
|
|
vim9_declare_error(arg);
|
|
|
|
return NULL;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2023-12-11 17:40:46 +01:00
|
|
|
if (check_typval_is_value(tv) == FAIL)
|
2023-10-27 19:35:26 +02:00
|
|
|
return NULL;
|
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
if (*arg == '$')
|
|
|
|
{
|
|
|
|
// ":let $VAR = expr": Set environment variable.
|
|
|
|
return ex_let_env(arg, tv, flags, endchars, op);
|
|
|
|
}
|
|
|
|
else if (*arg == '&')
|
|
|
|
{
|
|
|
|
// ":let &option = expr": Set option value.
|
|
|
|
// ":let &l:option = expr": Set local option value.
|
|
|
|
// ":let &g:option = expr": Set global option value.
|
|
|
|
// ":for &ts in range(8)": Set option value for for loop
|
|
|
|
return ex_let_option(arg, tv, flags, endchars, op);
|
|
|
|
}
|
|
|
|
else if (*arg == '@')
|
|
|
|
{
|
|
|
|
// ":let @r = expr": Set register contents.
|
|
|
|
return ex_let_register(arg, tv, flags, endchars, op);
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else if (eval_isnamec1(*arg) || *arg == '{')
|
|
|
|
{
|
|
|
|
lval_T lv;
|
2021-11-28 19:53:42 +00:00
|
|
|
char_u *p;
|
2022-04-01 15:26:58 +01:00
|
|
|
int lval_flags = (flags & (ASSIGN_NO_DECL | ASSIGN_DECL))
|
|
|
|
? GLV_NO_DECL : 0;
|
2023-10-05 20:14:43 +02:00
|
|
|
lval_flags |= (flags & ASSIGN_FOR_LOOP) ? GLV_FOR_LOOP : 0;
|
2022-04-01 15:26:58 +01:00
|
|
|
if (op != NULL && *op != '=')
|
|
|
|
lval_flags |= GLV_ASSIGN_WITH_OP;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-11-28 19:53:42 +00:00
|
|
|
// ":let var = expr": Set internal variable.
|
|
|
|
// ":let var: type = expr": Set internal variable with type.
|
|
|
|
// ":let {expr} = expr": Idem, name made with curly braces
|
2022-04-01 15:26:58 +01:00
|
|
|
p = get_lval(arg, tv, &lv, FALSE, FALSE, lval_flags, FNE_CHECK_START);
|
2020-05-24 23:00:18 +02:00
|
|
|
if (p != NULL && lv.ll_name != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-01-26 15:56:19 +01:00
|
|
|
if (endchars != NULL && vim_strchr(endchars,
|
|
|
|
*skipwhite(lv.ll_name_end)) == NULL)
|
2021-11-28 19:53:42 +00:00
|
|
|
{
|
2021-06-27 22:03:33 +02:00
|
|
|
emsg(_(e_unexpected_characters_in_let));
|
2021-11-28 19:53:42 +00:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
|
|
|
{
|
2021-02-11 21:19:34 +01:00
|
|
|
set_var_lval(&lv, p, tv, copy, flags, op, var_idx);
|
2021-04-14 13:30:46 +02:00
|
|
|
arg_end = lv.ll_name_end;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
clear_lval(&lv);
|
|
|
|
}
|
|
|
|
else
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
return arg_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":unlet[!] var1 ... " command.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_unlet(exarg_T *eap)
|
|
|
|
{
|
2020-04-19 16:28:59 +02:00
|
|
|
ex_unletlock(eap, eap->arg, 0, 0, do_unlet_var, NULL);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":lockvar" and ":unlockvar" commands
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_lockvar(exarg_T *eap)
|
|
|
|
{
|
|
|
|
char_u *arg = eap->arg;
|
|
|
|
int deep = 2;
|
|
|
|
|
|
|
|
if (eap->forceit)
|
|
|
|
deep = -1;
|
|
|
|
else if (vim_isdigit(*arg))
|
|
|
|
{
|
|
|
|
deep = getdigits(&arg);
|
|
|
|
arg = skipwhite(arg);
|
|
|
|
}
|
|
|
|
|
2020-04-19 16:28:59 +02:00
|
|
|
ex_unletlock(eap, arg, deep, 0, do_lock_var, NULL);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":unlet", ":lockvar" and ":unlockvar" are quite similar.
|
2020-04-19 16:28:59 +02:00
|
|
|
* Also used for Vim9 script. "callback" is invoked as:
|
|
|
|
* callback(&lv, name_end, eap, deep, cookie)
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
2020-04-19 16:28:59 +02:00
|
|
|
void
|
2019-08-27 22:48:30 +02:00
|
|
|
ex_unletlock(
|
|
|
|
exarg_T *eap,
|
|
|
|
char_u *argstart,
|
2020-04-19 16:28:59 +02:00
|
|
|
int deep,
|
|
|
|
int glv_flags,
|
|
|
|
int (*callback)(lval_T *, char_u *, exarg_T *, int, void *),
|
|
|
|
void *cookie)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
char_u *arg = argstart;
|
|
|
|
char_u *name_end;
|
|
|
|
int error = FALSE;
|
|
|
|
lval_T lv;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (*arg == '$')
|
|
|
|
{
|
2020-04-19 17:24:53 +02:00
|
|
|
lv.ll_name = arg;
|
|
|
|
lv.ll_tv = NULL;
|
|
|
|
++arg;
|
2019-08-27 22:48:30 +02:00
|
|
|
if (get_env_len(&arg) == 0)
|
|
|
|
{
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), arg - 1);
|
2019-08-27 22:48:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-04-19 17:24:53 +02:00
|
|
|
if (!error && !eap->skip
|
|
|
|
&& callback(&lv, arg, eap, deep, cookie) == FAIL)
|
|
|
|
error = TRUE;
|
2020-04-19 22:57:44 +02:00
|
|
|
name_end = arg;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2020-04-19 22:57:44 +02:00
|
|
|
else
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-04-19 22:57:44 +02:00
|
|
|
// Parse the name and find the end.
|
|
|
|
name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error,
|
2021-01-01 19:40:02 +01:00
|
|
|
glv_flags | GLV_NO_DECL, FNE_CHECK_START);
|
2020-04-19 22:57:44 +02:00
|
|
|
if (lv.ll_name == NULL)
|
|
|
|
error = TRUE; // error but continue parsing
|
|
|
|
if (name_end == NULL || (!VIM_ISWHITE(*name_end)
|
|
|
|
&& !ends_excmd(*name_end)))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-04-19 22:57:44 +02:00
|
|
|
if (name_end != NULL)
|
|
|
|
{
|
|
|
|
emsg_severe = TRUE;
|
2022-01-01 15:58:22 +00:00
|
|
|
semsg(_(e_trailing_characters_str), name_end);
|
2020-04-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
if (!(eap->skip || error))
|
|
|
|
clear_lval(&lv);
|
|
|
|
break;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2020-04-19 22:57:44 +02:00
|
|
|
if (!error && !eap->skip
|
2020-04-19 16:28:59 +02:00
|
|
|
&& callback(&lv, name_end, eap, deep, cookie) == FAIL)
|
2020-04-19 22:57:44 +02:00
|
|
|
error = TRUE;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2020-04-19 22:57:44 +02:00
|
|
|
if (!eap->skip)
|
|
|
|
clear_lval(&lv);
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
arg = skipwhite(name_end);
|
2020-04-19 16:28:59 +02:00
|
|
|
} while (!ends_excmd2(name_end, arg));
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-08-05 20:40:03 +02:00
|
|
|
set_nextcmd(eap, arg);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_unlet_var(
|
|
|
|
lval_T *lp,
|
|
|
|
char_u *name_end,
|
2020-04-19 16:28:59 +02:00
|
|
|
exarg_T *eap,
|
|
|
|
int deep UNUSED,
|
|
|
|
void *cookie UNUSED)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-04-19 16:28:59 +02:00
|
|
|
int forceit = eap->forceit;
|
2019-08-27 22:48:30 +02:00
|
|
|
int ret = OK;
|
|
|
|
int cc;
|
|
|
|
|
|
|
|
if (lp->ll_tv == NULL)
|
|
|
|
{
|
|
|
|
cc = *name_end;
|
|
|
|
*name_end = NUL;
|
|
|
|
|
2020-04-19 17:24:53 +02:00
|
|
|
// Environment variable, normal name or expanded name.
|
|
|
|
if (*lp->ll_name == '$')
|
2022-04-15 20:50:46 +01:00
|
|
|
vim_unsetenv_ext(lp->ll_name + 1);
|
2020-04-19 17:24:53 +02:00
|
|
|
else if (do_unlet(lp->ll_name, forceit) == FAIL)
|
2019-08-27 22:48:30 +02:00
|
|
|
ret = FAIL;
|
|
|
|
*name_end = cc;
|
|
|
|
}
|
|
|
|
else if ((lp->ll_list != NULL
|
2020-09-16 21:08:28 +02:00
|
|
|
&& value_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE))
|
2019-08-27 22:48:30 +02:00
|
|
|
|| (lp->ll_dict != NULL
|
2020-09-16 21:08:28 +02:00
|
|
|
&& value_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE)))
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
else if (lp->ll_range)
|
2022-03-20 17:46:06 +00:00
|
|
|
list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_n1,
|
|
|
|
!lp->ll_empty2, lp->ll_n2);
|
|
|
|
else if (lp->ll_list != NULL)
|
|
|
|
// unlet a List item.
|
|
|
|
listitem_remove(lp->ll_list, lp->ll_li);
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
2022-03-20 17:46:06 +00:00
|
|
|
// unlet a Dictionary item.
|
2022-11-25 16:31:51 +00:00
|
|
|
dictitem_remove(lp->ll_dict, lp->ll_di, "unlet");
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-02-20 17:04:02 +01:00
|
|
|
/*
|
|
|
|
* Unlet one item or a range of items from a list.
|
|
|
|
* Return OK or FAIL.
|
|
|
|
*/
|
2022-03-20 17:46:06 +00:00
|
|
|
void
|
2021-02-20 17:04:02 +01:00
|
|
|
list_unlet_range(
|
|
|
|
list_T *l,
|
|
|
|
listitem_T *li_first,
|
|
|
|
long n1_arg,
|
|
|
|
int has_n2,
|
|
|
|
long n2)
|
|
|
|
{
|
|
|
|
listitem_T *li = li_first;
|
|
|
|
int n1 = n1_arg;
|
|
|
|
|
|
|
|
// Delete a range of List items.
|
|
|
|
li = li_first;
|
|
|
|
n1 = n1_arg;
|
|
|
|
while (li != NULL && (!has_n2 || n2 >= n1))
|
|
|
|
{
|
|
|
|
listitem_T *next = li->li_next;
|
|
|
|
|
|
|
|
listitem_remove(l, li);
|
|
|
|
li = next;
|
|
|
|
++n1;
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* "unlet" a variable. Return OK if it existed, FAIL if not.
|
|
|
|
* When "forceit" is TRUE don't complain if the variable doesn't exist.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
do_unlet(char_u *name, int forceit)
|
|
|
|
{
|
|
|
|
hashtab_T *ht;
|
|
|
|
hashitem_T *hi;
|
2022-05-25 17:29:46 +01:00
|
|
|
char_u *varname = NULL; // init to shut up gcc
|
2019-08-27 22:48:30 +02:00
|
|
|
dict_T *d;
|
|
|
|
dictitem_T *di;
|
|
|
|
|
2020-12-18 15:38:00 +01:00
|
|
|
// can't :unlet a script variable in Vim9 script
|
2020-07-12 17:07:05 +02:00
|
|
|
if (in_vim9script() && check_vim9_unlet(name) == FAIL)
|
2020-04-19 16:28:59 +02:00
|
|
|
return FAIL;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
ht = find_var_ht(name, &varname);
|
2020-12-18 15:38:00 +01:00
|
|
|
|
|
|
|
// can't :unlet a script variable in Vim9 script from a function
|
|
|
|
if (ht == get_script_local_ht()
|
|
|
|
&& SCRIPT_ID_VALID(current_sctx.sc_sid)
|
|
|
|
&& SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
|
|
|
|
== SCRIPT_VERSION_VIM9
|
|
|
|
&& check_vim9_unlet(name) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (ht != NULL && *varname != NUL)
|
|
|
|
{
|
|
|
|
d = get_current_funccal_dict(ht);
|
|
|
|
if (d == NULL)
|
|
|
|
{
|
|
|
|
if (ht == &globvarht)
|
|
|
|
d = &globvardict;
|
2019-08-29 22:09:46 +02:00
|
|
|
else if (ht == &compat_hashtab)
|
2019-08-27 22:48:30 +02:00
|
|
|
d = &vimvardict;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
di = find_var_in_ht(ht, *name, (char_u *)"", FALSE);
|
|
|
|
d = di == NULL ? NULL : di->di_tv.vval.v_dict;
|
|
|
|
}
|
|
|
|
if (d == NULL)
|
|
|
|
{
|
|
|
|
internal_error("do_unlet()");
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hi = hash_find(ht, varname);
|
|
|
|
if (HASHITEM_EMPTY(hi))
|
|
|
|
hi = find_hi_in_scoped_ht(name, &ht);
|
|
|
|
if (hi != NULL && !HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
di = HI2DI(hi);
|
|
|
|
if (var_check_fixed(di->di_flags, name, FALSE)
|
|
|
|
|| var_check_ro(di->di_flags, name, FALSE)
|
2022-11-25 16:31:51 +00:00
|
|
|
|| value_check_lock(d->dv_lock, name, FALSE)
|
|
|
|
|| check_hashtab_frozen(ht, "unlet"))
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
delete_var(ht, hi);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (forceit)
|
|
|
|
return OK;
|
2021-12-16 20:56:57 +00:00
|
|
|
semsg(_(e_no_such_variable_str), name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2023-09-29 19:53:55 +02:00
|
|
|
static void
|
|
|
|
report_lockvar_member(char *msg, lval_T *lp)
|
|
|
|
{
|
|
|
|
int did_alloc = FALSE;
|
|
|
|
char_u *vname = (char_u *)"";
|
|
|
|
char_u *class_name = lp->ll_class != NULL
|
|
|
|
? lp->ll_class->class_name : (char_u *)"";
|
|
|
|
if (lp->ll_name != NULL)
|
|
|
|
{
|
|
|
|
if (lp->ll_name_end == NULL)
|
|
|
|
vname = lp->ll_name;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vname = vim_strnsave(lp->ll_name, lp->ll_name_end - lp->ll_name);
|
|
|
|
if (vname == NULL)
|
|
|
|
return;
|
|
|
|
did_alloc = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
semsg(_(msg), vname, class_name);
|
|
|
|
if (did_alloc)
|
|
|
|
vim_free(vname);
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Lock or unlock variable indicated by "lp".
|
|
|
|
* "deep" is the levels to go (-1 for unlimited);
|
|
|
|
* "lock" is TRUE for ":lockvar", FALSE for ":unlockvar".
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
do_lock_var(
|
|
|
|
lval_T *lp,
|
|
|
|
char_u *name_end,
|
2020-04-19 16:28:59 +02:00
|
|
|
exarg_T *eap,
|
2019-08-27 22:48:30 +02:00
|
|
|
int deep,
|
2020-04-19 16:28:59 +02:00
|
|
|
void *cookie UNUSED)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-04-19 16:28:59 +02:00
|
|
|
int lock = eap->cmdidx == CMD_lockvar;
|
2019-08-27 22:48:30 +02:00
|
|
|
int ret = OK;
|
|
|
|
int cc;
|
|
|
|
dictitem_T *di;
|
|
|
|
|
2023-09-29 19:53:55 +02:00
|
|
|
#ifdef LOG_LOCKVAR
|
|
|
|
ch_log(NULL, "LKVAR: do_lock_var(): name %s, is_root %d", lp->ll_name, lp->ll_is_root);
|
|
|
|
#endif
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (lp->ll_tv == NULL)
|
|
|
|
{
|
|
|
|
cc = *name_end;
|
|
|
|
*name_end = NUL;
|
2020-04-19 17:24:53 +02:00
|
|
|
if (*lp->ll_name == '$')
|
|
|
|
{
|
2022-01-01 16:21:00 +00:00
|
|
|
semsg(_(e_cannot_lock_or_unlock_variable_str), lp->ll_name);
|
2019-08-27 22:48:30 +02:00
|
|
|
ret = FAIL;
|
2020-04-19 17:24:53 +02:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
|
|
|
{
|
2020-04-19 17:24:53 +02:00
|
|
|
// Normal name or expanded name.
|
|
|
|
di = find_var(lp->ll_name, NULL, TRUE);
|
|
|
|
if (di == NULL)
|
2021-11-22 21:58:41 +00:00
|
|
|
{
|
|
|
|
if (in_vim9script())
|
|
|
|
semsg(_(e_cannot_find_variable_to_unlock_str),
|
|
|
|
lp->ll_name);
|
2020-04-19 17:24:53 +02:00
|
|
|
ret = FAIL;
|
2021-11-22 21:58:41 +00:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
2020-04-19 17:24:53 +02:00
|
|
|
{
|
2022-04-04 14:58:06 +01:00
|
|
|
if ((di->di_flags & DI_FLAGS_FIX)
|
|
|
|
&& di->di_tv.v_type != VAR_DICT
|
|
|
|
&& di->di_tv.v_type != VAR_LIST)
|
|
|
|
{
|
|
|
|
// For historic reasons this error is not given for a list
|
|
|
|
// or dict. E.g., the b: dict could be locked/unlocked.
|
|
|
|
semsg(_(e_cannot_lock_or_unlock_variable_str), lp->ll_name);
|
|
|
|
ret = FAIL;
|
|
|
|
}
|
2020-04-19 17:24:53 +02:00
|
|
|
else
|
2022-04-04 14:58:06 +01:00
|
|
|
{
|
|
|
|
if (in_vim9script())
|
|
|
|
{
|
|
|
|
svar_T *sv = find_typval_in_script(&di->di_tv,
|
|
|
|
0, FALSE);
|
|
|
|
|
|
|
|
if (sv != NULL && sv->sv_const != 0)
|
|
|
|
{
|
|
|
|
semsg(_(e_cannot_change_readonly_variable_str),
|
|
|
|
lp->ll_name);
|
|
|
|
ret = FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == OK)
|
|
|
|
{
|
|
|
|
if (lock)
|
|
|
|
di->di_flags |= DI_FLAGS_LOCK;
|
|
|
|
else
|
|
|
|
di->di_flags &= ~DI_FLAGS_LOCK;
|
|
|
|
if (deep != 0)
|
|
|
|
item_lock(&di->di_tv, deep, lock, FALSE);
|
|
|
|
}
|
|
|
|
}
|
2020-04-19 17:24:53 +02:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
*name_end = cc;
|
|
|
|
}
|
2023-09-29 19:53:55 +02:00
|
|
|
else if (deep == 0 && lp->ll_object == NULL && lp->ll_class == NULL)
|
2020-09-16 21:08:28 +02:00
|
|
|
{
|
|
|
|
// nothing to do
|
|
|
|
}
|
2023-09-29 19:53:55 +02:00
|
|
|
else if (lp->ll_is_root)
|
|
|
|
// (un)lock the item.
|
|
|
|
item_lock(lp->ll_tv, deep, lock, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
else if (lp->ll_range)
|
|
|
|
{
|
|
|
|
listitem_T *li = lp->ll_li;
|
|
|
|
|
|
|
|
// (un)lock a range of List items.
|
|
|
|
while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
|
|
|
|
{
|
2020-08-17 21:07:22 +02:00
|
|
|
item_lock(&li->li_tv, deep, lock, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
li = li->li_next;
|
|
|
|
++lp->ll_n1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (lp->ll_list != NULL)
|
|
|
|
// (un)lock a List item.
|
2020-08-17 21:07:22 +02:00
|
|
|
item_lock(&lp->ll_li->li_tv, deep, lock, FALSE);
|
2023-09-29 19:53:55 +02:00
|
|
|
else if (lp->ll_object != NULL) // This check must be before ll_class.
|
|
|
|
{
|
|
|
|
// (un)lock an object variable.
|
|
|
|
report_lockvar_member(e_cannot_lock_object_variable_str, lp);
|
|
|
|
ret = FAIL;
|
|
|
|
}
|
|
|
|
else if (lp->ll_class != NULL)
|
|
|
|
{
|
|
|
|
// (un)lock a class variable.
|
|
|
|
report_lockvar_member(e_cannot_lock_class_variable_str, lp);
|
|
|
|
ret = FAIL;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
2023-09-29 19:53:55 +02:00
|
|
|
{
|
2019-08-27 22:48:30 +02:00
|
|
|
// (un)lock a Dictionary item.
|
2023-09-29 19:53:55 +02:00
|
|
|
if (lp->ll_di == NULL)
|
|
|
|
{
|
|
|
|
emsg(_(e_dictionary_required));
|
|
|
|
ret = FAIL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
item_lock(&lp->ll_di->di_tv, deep, lock, FALSE);
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock or unlock an item. "deep" is nr of levels to go.
|
2020-08-17 21:07:22 +02:00
|
|
|
* When "check_refcount" is TRUE do not lock a list or dict with a reference
|
|
|
|
* count larger than 1.
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
2020-09-14 21:39:44 +02:00
|
|
|
void
|
2020-08-17 21:07:22 +02:00
|
|
|
item_lock(typval_T *tv, int deep, int lock, int check_refcount)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
static int recurse = 0;
|
|
|
|
list_T *l;
|
|
|
|
listitem_T *li;
|
|
|
|
dict_T *d;
|
|
|
|
blob_T *b;
|
|
|
|
hashitem_T *hi;
|
|
|
|
int todo;
|
|
|
|
|
2023-09-29 19:53:55 +02:00
|
|
|
#ifdef LOG_LOCKVAR
|
2023-10-04 20:16:22 +02:00
|
|
|
ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_name(tv->v_type));
|
2023-09-29 19:53:55 +02:00
|
|
|
#endif
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (recurse >= DICT_MAXNEST)
|
|
|
|
{
|
2022-01-05 16:09:06 +00:00
|
|
|
emsg(_(e_variable_nested_too_deep_for_unlock));
|
2019-08-27 22:48:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (deep == 0)
|
|
|
|
return;
|
|
|
|
++recurse;
|
|
|
|
|
|
|
|
// lock/unlock the item itself
|
|
|
|
if (lock)
|
|
|
|
tv->v_lock |= VAR_LOCKED;
|
|
|
|
else
|
|
|
|
tv->v_lock &= ~VAR_LOCKED;
|
|
|
|
|
|
|
|
switch (tv->v_type)
|
|
|
|
{
|
|
|
|
case VAR_UNKNOWN:
|
2020-04-05 21:38:23 +02:00
|
|
|
case VAR_ANY:
|
2020-01-26 15:56:19 +01:00
|
|
|
case VAR_VOID:
|
2019-08-27 22:48:30 +02:00
|
|
|
case VAR_NUMBER:
|
2020-01-26 15:56:19 +01:00
|
|
|
case VAR_BOOL:
|
2019-08-27 22:48:30 +02:00
|
|
|
case VAR_STRING:
|
|
|
|
case VAR_FUNC:
|
|
|
|
case VAR_PARTIAL:
|
|
|
|
case VAR_FLOAT:
|
|
|
|
case VAR_SPECIAL:
|
|
|
|
case VAR_JOB:
|
|
|
|
case VAR_CHANNEL:
|
2021-05-07 17:55:55 +02:00
|
|
|
case VAR_INSTR:
|
2022-12-08 15:32:33 +00:00
|
|
|
case VAR_CLASS:
|
|
|
|
case VAR_OBJECT:
|
2023-10-27 19:35:26 +02:00
|
|
|
case VAR_TYPEALIAS:
|
2019-08-27 22:48:30 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAR_BLOB:
|
2020-08-17 21:07:22 +02:00
|
|
|
if ((b = tv->vval.v_blob) != NULL
|
|
|
|
&& !(check_refcount && b->bv_refcount > 1))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (lock)
|
|
|
|
b->bv_lock |= VAR_LOCKED;
|
|
|
|
else
|
|
|
|
b->bv_lock &= ~VAR_LOCKED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VAR_LIST:
|
2020-08-17 21:07:22 +02:00
|
|
|
if ((l = tv->vval.v_list) != NULL
|
|
|
|
&& !(check_refcount && l->lv_refcount > 1))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (lock)
|
|
|
|
l->lv_lock |= VAR_LOCKED;
|
|
|
|
else
|
|
|
|
l->lv_lock &= ~VAR_LOCKED;
|
2022-01-26 21:01:15 +00:00
|
|
|
if (deep < 0 || deep > 1)
|
|
|
|
{
|
|
|
|
if (l->lv_first == &range_list_item)
|
|
|
|
l->lv_lock |= VAR_ITEMS_LOCKED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// recursive: lock/unlock the items the List contains
|
|
|
|
CHECK_LIST_MATERIALIZE(l);
|
|
|
|
FOR_ALL_LIST_ITEMS(l, li) item_lock(&li->li_tv,
|
|
|
|
deep - 1, lock, check_refcount);
|
|
|
|
}
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VAR_DICT:
|
2020-08-17 21:07:22 +02:00
|
|
|
if ((d = tv->vval.v_dict) != NULL
|
|
|
|
&& !(check_refcount && d->dv_refcount > 1))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (lock)
|
|
|
|
d->dv_lock |= VAR_LOCKED;
|
|
|
|
else
|
|
|
|
d->dv_lock &= ~VAR_LOCKED;
|
|
|
|
if (deep < 0 || deep > 1)
|
|
|
|
{
|
|
|
|
// recursive: lock/unlock the items the List contains
|
|
|
|
todo = (int)d->dv_hashtab.ht_used;
|
2023-03-07 17:13:51 +00:00
|
|
|
FOR_ALL_HASHTAB_ITEMS(&d->dv_hashtab, hi, todo)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
--todo;
|
2020-08-17 21:07:22 +02:00
|
|
|
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock,
|
|
|
|
check_refcount);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--recurse;
|
|
|
|
}
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Delete all "menutrans_" variables.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
del_menutrans_vars(void)
|
|
|
|
{
|
|
|
|
hashitem_T *hi;
|
|
|
|
int todo;
|
|
|
|
|
|
|
|
hash_lock(&globvarht);
|
|
|
|
todo = (int)globvarht.ht_used;
|
|
|
|
for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi)
|
|
|
|
{
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
--todo;
|
|
|
|
if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0)
|
|
|
|
delete_var(&globvarht, hi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hash_unlock(&globvarht);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Local string buffer for the next two functions to store a variable name
|
|
|
|
* with its prefix. Allocated in cat_prefix_varname(), freed later in
|
|
|
|
* get_user_var_name().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char_u *varnamebuf = NULL;
|
|
|
|
static int varnamebuflen = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function to concatenate a prefix and a variable name.
|
|
|
|
*/
|
2021-01-13 19:48:46 +01:00
|
|
|
char_u *
|
2019-08-29 22:09:46 +02:00
|
|
|
cat_prefix_varname(int prefix, char_u *name)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = (int)STRLEN(name) + 3;
|
|
|
|
if (len > varnamebuflen)
|
|
|
|
{
|
|
|
|
vim_free(varnamebuf);
|
2019-08-30 15:46:30 +02:00
|
|
|
len += 10; // some additional space
|
2019-08-29 22:09:46 +02:00
|
|
|
varnamebuf = alloc(len);
|
|
|
|
if (varnamebuf == NULL)
|
|
|
|
{
|
|
|
|
varnamebuflen = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
varnamebuflen = len;
|
|
|
|
}
|
|
|
|
*varnamebuf = prefix;
|
|
|
|
varnamebuf[1] = ':';
|
|
|
|
STRCPY(varnamebuf + 2, name);
|
|
|
|
return varnamebuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function given to ExpandGeneric() to obtain the list of user defined
|
|
|
|
* (global/buffer/window/built-in) variable names.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_user_var_name(expand_T *xp, int idx)
|
|
|
|
{
|
|
|
|
static long_u gdone;
|
|
|
|
static long_u bdone;
|
|
|
|
static long_u wdone;
|
|
|
|
static long_u tdone;
|
|
|
|
static int vidx;
|
|
|
|
static hashitem_T *hi;
|
|
|
|
hashtab_T *ht;
|
|
|
|
|
|
|
|
if (idx == 0)
|
|
|
|
{
|
|
|
|
gdone = bdone = wdone = vidx = 0;
|
|
|
|
tdone = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Global variables
|
|
|
|
if (gdone < globvarht.ht_used)
|
|
|
|
{
|
|
|
|
if (gdone++ == 0)
|
|
|
|
hi = globvarht.ht_array;
|
|
|
|
else
|
|
|
|
++hi;
|
|
|
|
while (HASHITEM_EMPTY(hi))
|
|
|
|
++hi;
|
|
|
|
if (STRNCMP("g:", xp->xp_pattern, 2) == 0)
|
|
|
|
return cat_prefix_varname('g', hi->hi_key);
|
|
|
|
return hi->hi_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
// b: variables
|
2022-02-20 20:49:35 +00:00
|
|
|
ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab;
|
2019-08-29 22:09:46 +02:00
|
|
|
if (bdone < ht->ht_used)
|
|
|
|
{
|
|
|
|
if (bdone++ == 0)
|
|
|
|
hi = ht->ht_array;
|
|
|
|
else
|
|
|
|
++hi;
|
|
|
|
while (HASHITEM_EMPTY(hi))
|
|
|
|
++hi;
|
|
|
|
return cat_prefix_varname('b', hi->hi_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// w: variables
|
2022-02-20 20:49:35 +00:00
|
|
|
ht = &prevwin_curwin()->w_vars->dv_hashtab;
|
2019-08-29 22:09:46 +02:00
|
|
|
if (wdone < ht->ht_used)
|
|
|
|
{
|
|
|
|
if (wdone++ == 0)
|
|
|
|
hi = ht->ht_array;
|
|
|
|
else
|
|
|
|
++hi;
|
|
|
|
while (HASHITEM_EMPTY(hi))
|
|
|
|
++hi;
|
|
|
|
return cat_prefix_varname('w', hi->hi_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// t: variables
|
|
|
|
ht = &curtab->tp_vars->dv_hashtab;
|
|
|
|
if (tdone < ht->ht_used)
|
|
|
|
{
|
|
|
|
if (tdone++ == 0)
|
|
|
|
hi = ht->ht_array;
|
|
|
|
else
|
|
|
|
++hi;
|
|
|
|
while (HASHITEM_EMPTY(hi))
|
|
|
|
++hi;
|
|
|
|
return cat_prefix_varname('t', hi->hi_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// v: variables
|
|
|
|
if (vidx < VV_LEN)
|
|
|
|
return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
|
|
|
|
|
|
|
|
VIM_CLEAR(varnamebuf);
|
|
|
|
varnamebuflen = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
char *
|
|
|
|
get_var_special_name(int nr)
|
|
|
|
{
|
|
|
|
switch (nr)
|
|
|
|
{
|
2021-01-01 15:11:04 +01:00
|
|
|
case VVAL_FALSE: return in_vim9script() ? "false" : "v:false";
|
|
|
|
case VVAL_TRUE: return in_vim9script() ? "true" : "v:true";
|
2021-01-03 21:53:53 +01:00
|
|
|
case VVAL_NULL: return in_vim9script() ? "null" : "v:null";
|
2019-09-01 16:01:30 +02:00
|
|
|
case VVAL_NONE: return "v:none";
|
|
|
|
}
|
|
|
|
internal_error("get_var_special_name()");
|
|
|
|
return "42";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the global variable dictionary
|
|
|
|
*/
|
|
|
|
dict_T *
|
|
|
|
get_globvar_dict(void)
|
|
|
|
{
|
|
|
|
return &globvardict;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the global variable hash table
|
|
|
|
*/
|
|
|
|
hashtab_T *
|
|
|
|
get_globvar_ht(void)
|
|
|
|
{
|
|
|
|
return &globvarht;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the v: variable dictionary
|
|
|
|
*/
|
|
|
|
dict_T *
|
|
|
|
get_vimvar_dict(void)
|
|
|
|
{
|
|
|
|
return &vimvardict;
|
|
|
|
}
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
/*
|
|
|
|
* Returns the index of a v:variable. Negative if not found.
|
2020-04-09 19:34:43 +02:00
|
|
|
* Returns DI_ flags in "di_flags".
|
2020-01-26 15:56:19 +01:00
|
|
|
*/
|
|
|
|
int
|
2020-04-09 19:34:43 +02:00
|
|
|
find_vim_var(char_u *name, int *di_flags)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2020-04-09 19:34:43 +02:00
|
|
|
dictitem_T *di = find_var_in_ht(&vimvarht, 0, name, TRUE);
|
|
|
|
struct vimvar *vv;
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
if (di == NULL)
|
|
|
|
return -1;
|
2020-04-09 19:34:43 +02:00
|
|
|
*di_flags = di->di_flags;
|
2020-01-26 15:56:19 +01:00
|
|
|
vv = (struct vimvar *)((char *)di - offsetof(vimvar_T, vv_di));
|
|
|
|
return (int)(vv - vimvars);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-29 22:48:24 +02:00
|
|
|
/*
|
|
|
|
* Set type of v: variable to "type".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_type(int idx, vartype_T type)
|
|
|
|
{
|
2021-12-24 21:36:12 +00:00
|
|
|
vimvars[idx].vv_tv_type = type;
|
2019-08-29 22:48:24 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Set number v: variable to "val".
|
2019-08-30 15:46:30 +02:00
|
|
|
* Note that this does not set the type, use set_vim_var_type() for that.
|
2019-08-29 22:09:46 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_nr(int idx, varnumber_T val)
|
|
|
|
{
|
|
|
|
vimvars[idx].vv_nr = val;
|
|
|
|
}
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
char *
|
|
|
|
get_vim_var_name(int idx)
|
|
|
|
{
|
|
|
|
return vimvars[idx].vv_name;
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Get typval_T v: variable value.
|
|
|
|
*/
|
|
|
|
typval_T *
|
|
|
|
get_vim_var_tv(int idx)
|
|
|
|
{
|
|
|
|
return &vimvars[idx].vv_tv;
|
|
|
|
}
|
|
|
|
|
2021-12-24 21:36:12 +00:00
|
|
|
type_T *
|
|
|
|
get_vim_var_type(int idx, garray_T *type_list)
|
|
|
|
{
|
|
|
|
if (vimvars[idx].vv_type != NULL)
|
|
|
|
return vimvars[idx].vv_type;
|
|
|
|
return typval2type_vimvar(&vimvars[idx].vv_tv, type_list);
|
|
|
|
}
|
|
|
|
|
2020-02-02 22:24:04 +01:00
|
|
|
/*
|
|
|
|
* Set v: variable to "tv". Only accepts the same type.
|
|
|
|
* Takes over the value of "tv".
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
set_vim_var_tv(int idx, typval_T *tv)
|
|
|
|
{
|
2021-12-24 21:36:12 +00:00
|
|
|
if (vimvars[idx].vv_tv_type != tv->v_type)
|
2020-02-02 22:24:04 +01:00
|
|
|
{
|
2020-08-15 16:33:28 +02:00
|
|
|
emsg(_(e_type_mismatch_for_v_variable));
|
2020-02-02 22:24:04 +01:00
|
|
|
clear_tv(tv);
|
|
|
|
return FAIL;
|
|
|
|
}
|
2020-04-09 20:10:55 +02:00
|
|
|
// VV_RO is also checked when compiling, but let's check here as well.
|
|
|
|
if (vimvars[idx].vv_flags & VV_RO)
|
|
|
|
{
|
2021-07-21 22:20:33 +02:00
|
|
|
semsg(_(e_cannot_change_readonly_variable_str), vimvars[idx].vv_name);
|
2020-04-09 20:10:55 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
if (sandbox && (vimvars[idx].vv_flags & VV_RO_SBX))
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
semsg(_(e_cannot_set_variable_in_sandbox_str), vimvars[idx].vv_name);
|
2020-04-09 20:10:55 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
2020-02-02 22:24:04 +01:00
|
|
|
clear_tv(&vimvars[idx].vv_di.di_tv);
|
|
|
|
vimvars[idx].vv_di.di_tv = *tv;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Get number v: variable value.
|
|
|
|
*/
|
|
|
|
varnumber_T
|
|
|
|
get_vim_var_nr(int idx)
|
|
|
|
{
|
|
|
|
return vimvars[idx].vv_nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get string v: variable value. Uses a static buffer, can only be used once.
|
|
|
|
* If the String variable has never been set, return an empty string.
|
|
|
|
* Never returns NULL;
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_vim_var_str(int idx)
|
|
|
|
{
|
|
|
|
return tv_get_string(&vimvars[idx].vv_tv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get List v: variable value. Caller must take care of reference count when
|
|
|
|
* needed.
|
|
|
|
*/
|
|
|
|
list_T *
|
|
|
|
get_vim_var_list(int idx)
|
|
|
|
{
|
|
|
|
return vimvars[idx].vv_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get Dict v: variable value. Caller must take care of reference count when
|
|
|
|
* needed.
|
|
|
|
*/
|
|
|
|
dict_T *
|
|
|
|
get_vim_var_dict(int idx)
|
|
|
|
{
|
|
|
|
return vimvars[idx].vv_dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set v:char to character "c".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_char(int c)
|
|
|
|
{
|
|
|
|
char_u buf[MB_MAXBYTES + 1];
|
|
|
|
|
|
|
|
if (has_mbyte)
|
|
|
|
buf[(*mb_char2bytes)(c, buf)] = NUL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf[0] = c;
|
|
|
|
buf[1] = NUL;
|
|
|
|
}
|
|
|
|
set_vim_var_string(VV_CHAR, buf, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set v:count to "count" and v:count1 to "count1".
|
|
|
|
* When "set_prevcount" is TRUE first set v:prevcount from v:count.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vcount(
|
|
|
|
long count,
|
|
|
|
long count1,
|
|
|
|
int set_prevcount)
|
|
|
|
{
|
|
|
|
if (set_prevcount)
|
|
|
|
vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr;
|
|
|
|
vimvars[VV_COUNT].vv_nr = count;
|
|
|
|
vimvars[VV_COUNT1].vv_nr = count1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save variables that might be changed as a side effect. Used when executing
|
|
|
|
* a timer callback.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
save_vimvars(vimvars_save_T *vvsave)
|
|
|
|
{
|
|
|
|
vvsave->vv_prevcount = vimvars[VV_PREVCOUNT].vv_nr;
|
|
|
|
vvsave->vv_count = vimvars[VV_COUNT].vv_nr;
|
|
|
|
vvsave->vv_count1 = vimvars[VV_COUNT1].vv_nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore variables saved by save_vimvars().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
restore_vimvars(vimvars_save_T *vvsave)
|
|
|
|
{
|
|
|
|
vimvars[VV_PREVCOUNT].vv_nr = vvsave->vv_prevcount;
|
|
|
|
vimvars[VV_COUNT].vv_nr = vvsave->vv_count;
|
|
|
|
vimvars[VV_COUNT1].vv_nr = vvsave->vv_count1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set string v: variable to a copy of "val". If 'copy' is FALSE, then set the
|
|
|
|
* value.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_string(
|
|
|
|
int idx,
|
|
|
|
char_u *val,
|
|
|
|
int len) // length of "val" to use or -1 (whole string)
|
|
|
|
{
|
|
|
|
clear_tv(&vimvars[idx].vv_di.di_tv);
|
2021-12-24 21:36:12 +00:00
|
|
|
vimvars[idx].vv_tv_type = VAR_STRING;
|
2019-08-29 22:09:46 +02:00
|
|
|
if (val == NULL)
|
|
|
|
vimvars[idx].vv_str = NULL;
|
|
|
|
else if (len == -1)
|
|
|
|
vimvars[idx].vv_str = vim_strsave(val);
|
|
|
|
else
|
|
|
|
vimvars[idx].vv_str = vim_strnsave(val, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set List v: variable to "val".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_list(int idx, list_T *val)
|
|
|
|
{
|
|
|
|
clear_tv(&vimvars[idx].vv_di.di_tv);
|
2021-12-24 21:36:12 +00:00
|
|
|
vimvars[idx].vv_tv_type = VAR_LIST;
|
2019-08-29 22:09:46 +02:00
|
|
|
vimvars[idx].vv_list = val;
|
|
|
|
if (val != NULL)
|
|
|
|
++val->lv_refcount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set Dictionary v: variable to "val".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_vim_var_dict(int idx, dict_T *val)
|
|
|
|
{
|
|
|
|
clear_tv(&vimvars[idx].vv_di.di_tv);
|
2021-12-24 21:36:12 +00:00
|
|
|
vimvars[idx].vv_tv_type = VAR_DICT;
|
2019-08-29 22:09:46 +02:00
|
|
|
vimvars[idx].vv_dict = val;
|
2023-01-02 16:54:53 +00:00
|
|
|
if (val == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
++val->dv_refcount;
|
|
|
|
dict_set_items_ro(val);
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 04:16:57 +01:00
|
|
|
/*
|
|
|
|
* Set the v:argv list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_argv_var(char **argv, int argc)
|
|
|
|
{
|
|
|
|
list_T *l = list_alloc();
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (l == NULL)
|
|
|
|
getout(1);
|
|
|
|
l->lv_lock = VAR_FIXED;
|
|
|
|
for (i = 0; i < argc; ++i)
|
|
|
|
{
|
|
|
|
if (list_append_string(l, (char_u *)argv[i], -1) == FAIL)
|
|
|
|
getout(1);
|
2020-01-29 21:27:21 +01:00
|
|
|
l->lv_u.mat.lv_last->li_tv.v_lock = VAR_FIXED;
|
2019-10-29 04:16:57 +01:00
|
|
|
}
|
|
|
|
set_vim_var_list(VV_ARGV, l);
|
|
|
|
}
|
|
|
|
|
2020-06-06 15:58:03 +02:00
|
|
|
/*
|
|
|
|
* Reset v:register, taking the 'clipboard' setting into account.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
reset_reg_var(void)
|
|
|
|
{
|
|
|
|
int regname = 0;
|
|
|
|
|
|
|
|
// Adjust the register according to 'clipboard', so that when
|
|
|
|
// "unnamed" is present it becomes '*' or '+' instead of '"'.
|
|
|
|
#ifdef FEAT_CLIPBOARD
|
|
|
|
adjust_clip_reg(®name);
|
|
|
|
#endif
|
|
|
|
set_reg_var(regname);
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Set v:register if needed.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_reg_var(int c)
|
|
|
|
{
|
|
|
|
char_u regname;
|
|
|
|
|
|
|
|
if (c == 0 || c == ' ')
|
|
|
|
regname = '"';
|
|
|
|
else
|
|
|
|
regname = c;
|
|
|
|
// Avoid free/alloc when the value is already right.
|
|
|
|
if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c)
|
|
|
|
set_vim_var_string(VV_REG, ®name, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get or set v:exception. If "oldval" == NULL, return the current value.
|
|
|
|
* Otherwise, restore the value to "oldval" and return NULL.
|
|
|
|
* Must always be called in pairs to save and restore v:exception! Does not
|
|
|
|
* take care of memory allocations.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
v_exception(char_u *oldval)
|
|
|
|
{
|
|
|
|
if (oldval == NULL)
|
|
|
|
return vimvars[VV_EXCEPTION].vv_str;
|
|
|
|
|
|
|
|
vimvars[VV_EXCEPTION].vv_str = oldval;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get or set v:throwpoint. If "oldval" == NULL, return the current value.
|
|
|
|
* Otherwise, restore the value to "oldval" and return NULL.
|
|
|
|
* Must always be called in pairs to save and restore v:throwpoint! Does not
|
|
|
|
* take care of memory allocations.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
v_throwpoint(char_u *oldval)
|
|
|
|
{
|
|
|
|
if (oldval == NULL)
|
|
|
|
return vimvars[VV_THROWPOINT].vv_str;
|
|
|
|
|
|
|
|
vimvars[VV_THROWPOINT].vv_str = oldval;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set v:cmdarg.
|
|
|
|
* If "eap" != NULL, use "eap" to generate the value and return the old value.
|
|
|
|
* If "oldarg" != NULL, restore the value to "oldarg" and return NULL.
|
|
|
|
* Must always be called in pairs!
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
set_cmdarg(exarg_T *eap, char_u *oldarg)
|
|
|
|
{
|
|
|
|
char_u *oldval;
|
|
|
|
char_u *newval;
|
|
|
|
unsigned len;
|
|
|
|
|
|
|
|
oldval = vimvars[VV_CMDARG].vv_str;
|
|
|
|
if (eap == NULL)
|
|
|
|
{
|
|
|
|
vim_free(oldval);
|
|
|
|
vimvars[VV_CMDARG].vv_str = oldarg;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eap->force_bin == FORCE_BIN)
|
|
|
|
len = 6;
|
|
|
|
else if (eap->force_bin == FORCE_NOBIN)
|
|
|
|
len = 8;
|
|
|
|
else
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
if (eap->read_edit)
|
|
|
|
len += 7;
|
|
|
|
|
|
|
|
if (eap->force_ff != 0)
|
|
|
|
len += 10; // " ++ff=unix"
|
|
|
|
if (eap->force_enc != 0)
|
|
|
|
len += (unsigned)STRLEN(eap->cmd + eap->force_enc) + 7;
|
|
|
|
if (eap->bad_char != 0)
|
|
|
|
len += 7 + 4; // " ++bad=" + "keep" or "drop"
|
|
|
|
|
|
|
|
newval = alloc(len + 1);
|
|
|
|
if (newval == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (eap->force_bin == FORCE_BIN)
|
|
|
|
sprintf((char *)newval, " ++bin");
|
|
|
|
else if (eap->force_bin == FORCE_NOBIN)
|
|
|
|
sprintf((char *)newval, " ++nobin");
|
|
|
|
else
|
|
|
|
*newval = NUL;
|
|
|
|
|
|
|
|
if (eap->read_edit)
|
|
|
|
STRCAT(newval, " ++edit");
|
|
|
|
|
|
|
|
if (eap->force_ff != 0)
|
|
|
|
sprintf((char *)newval + STRLEN(newval), " ++ff=%s",
|
|
|
|
eap->force_ff == 'u' ? "unix"
|
|
|
|
: eap->force_ff == 'd' ? "dos"
|
|
|
|
: "mac");
|
|
|
|
if (eap->force_enc != 0)
|
|
|
|
sprintf((char *)newval + STRLEN(newval), " ++enc=%s",
|
|
|
|
eap->cmd + eap->force_enc);
|
|
|
|
if (eap->bad_char == BAD_KEEP)
|
|
|
|
STRCPY(newval + STRLEN(newval), " ++bad=keep");
|
|
|
|
else if (eap->bad_char == BAD_DROP)
|
|
|
|
STRCPY(newval + STRLEN(newval), " ++bad=drop");
|
|
|
|
else if (eap->bad_char != 0)
|
|
|
|
sprintf((char *)newval + STRLEN(newval), " ++bad=%c", eap->bad_char);
|
|
|
|
vimvars[VV_CMDARG].vv_str = newval;
|
|
|
|
return oldval;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Get the value of internal variable "name".
|
2021-03-13 20:57:19 +01:00
|
|
|
* If "flags" has EVAL_VAR_IMPORT may return a VAR_ANY with v_number set to the
|
|
|
|
* imported script ID.
|
2019-08-27 22:48:30 +02:00
|
|
|
* Return OK or FAIL. If OK is returned "rettv" must be cleared.
|
|
|
|
*/
|
|
|
|
int
|
2020-07-01 18:29:55 +02:00
|
|
|
eval_variable(
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *name,
|
2023-01-06 18:42:20 +00:00
|
|
|
int len, // length of "name" or zero
|
2022-01-06 21:10:28 +00:00
|
|
|
scid_T sid, // script ID for imported item or zero
|
2019-08-27 22:48:30 +02:00
|
|
|
typval_T *rettv, // NULL when only checking existence
|
|
|
|
dictitem_T **dip, // non-NULL when typval's dict item is needed
|
2021-03-13 20:57:19 +01:00
|
|
|
int flags) // EVAL_VAR_ flags
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
int ret = OK;
|
|
|
|
typval_T *tv = NULL;
|
2021-03-13 20:57:19 +01:00
|
|
|
int found = FALSE;
|
2021-07-08 20:57:24 +02:00
|
|
|
hashtab_T *ht = NULL;
|
2023-01-06 18:42:20 +00:00
|
|
|
int cc = 0;
|
2021-07-08 21:38:50 +02:00
|
|
|
type_T *type = NULL;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2023-01-06 18:42:20 +00:00
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
// truncate the name, so that we can use strcmp()
|
|
|
|
cc = name[len];
|
|
|
|
name[len] = NUL;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-06-14 21:32:21 +02:00
|
|
|
// Check for local variable when debugging.
|
2023-10-27 19:35:26 +02:00
|
|
|
if ((sid == 0) && (tv = lookup_debug_var(name)) == NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-06-14 21:32:21 +02:00
|
|
|
// Check for user-defined variables.
|
2021-07-08 21:38:50 +02:00
|
|
|
dictitem_T *v = find_var(name, &ht, flags & EVAL_VAR_NOAUTOLOAD);
|
|
|
|
|
2021-06-14 21:32:21 +02:00
|
|
|
if (v != NULL)
|
|
|
|
{
|
|
|
|
tv = &v->di_tv;
|
|
|
|
if (dip != NULL)
|
|
|
|
*dip = v;
|
|
|
|
}
|
2021-07-08 21:38:50 +02:00
|
|
|
else
|
|
|
|
ht = NULL;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2020-07-12 17:07:05 +02:00
|
|
|
if (tv == NULL && (in_vim9script() || STRNCMP(name, "s:", 2) == 0))
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
imported_T *import = NULL;
|
2020-06-11 23:10:46 +02:00
|
|
|
char_u *p = STRNCMP(name, "s:", 2) == 0 ? name + 2 : name;
|
|
|
|
|
2022-01-06 21:10:28 +00:00
|
|
|
if (sid == 0)
|
2022-02-13 21:51:08 +00:00
|
|
|
import = find_imported(p, 0, TRUE);
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
// imported variable from another script
|
2022-01-06 21:10:28 +00:00
|
|
|
if (import != NULL || sid != 0)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
if ((flags & EVAL_VAR_IMPORT) == 0)
|
2020-07-08 15:16:19 +02:00
|
|
|
{
|
2022-01-07 18:20:55 +00:00
|
|
|
if (SCRIPT_ID_VALID(sid))
|
2020-07-08 15:16:19 +02:00
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
ht = &SCRIPT_VARS(sid);
|
|
|
|
if (ht != NULL)
|
|
|
|
{
|
|
|
|
dictitem_T *v = find_var_in_ht(ht, 0, name,
|
|
|
|
flags & EVAL_VAR_NOAUTOLOAD);
|
|
|
|
|
|
|
|
if (v != NULL)
|
|
|
|
{
|
|
|
|
tv = &v->di_tv;
|
|
|
|
if (dip != NULL)
|
|
|
|
*dip = v;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ht = NULL;
|
|
|
|
}
|
2020-07-08 15:16:19 +02:00
|
|
|
}
|
2022-01-06 21:10:28 +00:00
|
|
|
else
|
2021-03-13 20:57:19 +01:00
|
|
|
{
|
|
|
|
if (flags & EVAL_VAR_VERBOSE)
|
2022-01-20 21:32:54 +00:00
|
|
|
semsg(_(e_expected_dot_after_name_str), name);
|
2021-03-13 20:57:19 +01:00
|
|
|
ret = FAIL;
|
|
|
|
}
|
2020-09-27 13:51:14 +02:00
|
|
|
}
|
2020-07-08 15:16:19 +02:00
|
|
|
else
|
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
if (rettv != NULL)
|
|
|
|
{
|
2022-08-24 16:30:36 +01:00
|
|
|
// special value that is used in handle_subscript()
|
2022-01-06 21:10:28 +00:00
|
|
|
rettv->v_type = VAR_ANY;
|
|
|
|
rettv->vval.v_number = sid != 0 ? sid : import->imp_sid;
|
|
|
|
}
|
|
|
|
found = TRUE;
|
2020-07-08 15:16:19 +02:00
|
|
|
}
|
2020-01-26 15:56:19 +01:00
|
|
|
}
|
2021-12-11 13:54:46 +00:00
|
|
|
else if (in_vim9script() && (flags & EVAL_VAR_NO_FUNC) == 0)
|
2020-08-27 21:33:10 +02:00
|
|
|
{
|
2022-01-30 15:28:30 +00:00
|
|
|
int has_g_prefix = STRNCMP(name, "g:", 2) == 0;
|
2022-01-13 21:15:21 +00:00
|
|
|
ufunc_T *ufunc = find_func(name, FALSE);
|
2020-08-27 21:33:10 +02:00
|
|
|
|
2021-08-15 16:08:36 +02:00
|
|
|
// In Vim9 script we can get a function reference by using the
|
2022-01-30 15:28:30 +00:00
|
|
|
// function name. For a global non-autoload function "g:" is
|
|
|
|
// required.
|
|
|
|
if (ufunc != NULL && (has_g_prefix
|
|
|
|
|| !func_requires_g_prefix(ufunc)))
|
2020-08-27 21:33:10 +02:00
|
|
|
{
|
2021-03-13 20:57:19 +01:00
|
|
|
found = TRUE;
|
2020-08-27 21:33:10 +02:00
|
|
|
if (rettv != NULL)
|
|
|
|
{
|
|
|
|
rettv->v_type = VAR_FUNC;
|
2022-01-30 15:28:30 +00:00
|
|
|
if (has_g_prefix)
|
2021-12-12 21:02:03 +00:00
|
|
|
// Keep the "g:", otherwise script-local may be
|
|
|
|
// assumed.
|
|
|
|
rettv->vval.v_string = vim_strsave(name);
|
|
|
|
else
|
|
|
|
rettv->vval.v_string = vim_strsave(ufunc->uf_name);
|
2021-08-15 16:08:36 +02:00
|
|
|
if (rettv->vval.v_string != NULL)
|
|
|
|
func_ref(ufunc->uf_name);
|
2020-08-27 21:33:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-26 15:56:19 +01:00
|
|
|
}
|
|
|
|
|
2021-03-13 20:57:19 +01:00
|
|
|
if (!found)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-07-08 15:16:19 +02:00
|
|
|
if (tv == NULL)
|
|
|
|
{
|
2021-03-13 20:57:19 +01:00
|
|
|
if (rettv != NULL && (flags & EVAL_VAR_VERBOSE))
|
2020-08-15 16:33:28 +02:00
|
|
|
semsg(_(e_undefined_variable_str), name);
|
2020-07-08 15:16:19 +02:00
|
|
|
ret = FAIL;
|
|
|
|
}
|
|
|
|
else if (rettv != NULL)
|
2020-11-04 11:36:35 +01:00
|
|
|
{
|
2022-04-05 21:40:38 +01:00
|
|
|
svar_T *sv = NULL;
|
|
|
|
int was_assigned = FALSE;
|
|
|
|
|
2021-07-11 20:59:00 +02:00
|
|
|
if (ht != NULL && ht == get_script_local_ht()
|
|
|
|
&& tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv)
|
2021-07-08 20:57:24 +02:00
|
|
|
{
|
2022-04-05 21:40:38 +01:00
|
|
|
sv = find_typval_in_script(tv, 0, TRUE);
|
2021-07-08 20:57:24 +02:00
|
|
|
if (sv != NULL)
|
2022-04-05 21:40:38 +01:00
|
|
|
{
|
2021-07-08 20:57:24 +02:00
|
|
|
type = sv->sv_type;
|
2022-04-05 21:40:38 +01:00
|
|
|
was_assigned = sv->sv_flags & SVFLAG_ASSIGNED;
|
|
|
|
}
|
2021-07-08 20:57:24 +02:00
|
|
|
}
|
|
|
|
|
2023-10-27 19:35:26 +02:00
|
|
|
if ((tv->v_type == VAR_TYPEALIAS || tv->v_type == VAR_CLASS)
|
|
|
|
&& sid != 0)
|
|
|
|
{
|
|
|
|
// type alias or class imported from another script. Check
|
|
|
|
// whether it is exported from the other script.
|
|
|
|
sv = find_typval_in_script(tv, sid, TRUE);
|
|
|
|
if (sv == NULL)
|
|
|
|
{
|
|
|
|
ret = FAIL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
|
|
|
|
{
|
|
|
|
semsg(_(e_item_not_exported_in_script_str), name);
|
|
|
|
ret = FAIL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-27 16:29:53 +01:00
|
|
|
// If a list or dict variable wasn't initialized and has meaningful
|
|
|
|
// type, do it now. Not for global variables, they are not
|
|
|
|
// declared.
|
2022-03-01 19:23:24 +00:00
|
|
|
if (ht != &globvarht)
|
2020-11-04 11:36:35 +01:00
|
|
|
{
|
2022-03-27 16:29:53 +01:00
|
|
|
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
|
2022-04-05 21:40:38 +01:00
|
|
|
&& ((type != NULL && !was_assigned)
|
2022-03-28 15:22:35 +01:00
|
|
|
|| !in_vim9script()))
|
2021-07-08 20:57:24 +02:00
|
|
|
{
|
2022-03-01 19:23:24 +00:00
|
|
|
tv->vval.v_dict = dict_alloc();
|
|
|
|
if (tv->vval.v_dict != NULL)
|
|
|
|
{
|
|
|
|
++tv->vval.v_dict->dv_refcount;
|
|
|
|
tv->vval.v_dict->dv_type = alloc_type(type);
|
2022-04-05 21:40:38 +01:00
|
|
|
if (sv != NULL)
|
|
|
|
sv->sv_flags |= SVFLAG_ASSIGNED;
|
2022-03-01 19:23:24 +00:00
|
|
|
}
|
2021-07-08 20:57:24 +02:00
|
|
|
}
|
2022-03-27 16:29:53 +01:00
|
|
|
else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
|
2022-04-05 21:40:38 +01:00
|
|
|
&& ((type != NULL && !was_assigned)
|
2022-03-27 16:51:04 +01:00
|
|
|
|| !in_vim9script()))
|
2022-03-01 19:23:24 +00:00
|
|
|
{
|
|
|
|
tv->vval.v_list = list_alloc();
|
|
|
|
if (tv->vval.v_list != NULL)
|
|
|
|
{
|
|
|
|
++tv->vval.v_list->lv_refcount;
|
|
|
|
tv->vval.v_list->lv_type = alloc_type(type);
|
2022-04-05 21:40:38 +01:00
|
|
|
if (sv != NULL)
|
|
|
|
sv->sv_flags |= SVFLAG_ASSIGNED;
|
2022-03-01 19:23:24 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-28 15:22:35 +01:00
|
|
|
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
|
2022-04-05 21:40:38 +01:00
|
|
|
&& ((type != NULL && !was_assigned)
|
2022-03-28 15:22:35 +01:00
|
|
|
|| !in_vim9script()))
|
|
|
|
{
|
|
|
|
tv->vval.v_blob = blob_alloc();
|
|
|
|
if (tv->vval.v_blob != NULL)
|
2022-04-05 21:40:38 +01:00
|
|
|
{
|
2022-03-28 15:22:35 +01:00
|
|
|
++tv->vval.v_blob->bv_refcount;
|
2022-04-05 21:40:38 +01:00
|
|
|
if (sv != NULL)
|
|
|
|
sv->sv_flags |= SVFLAG_ASSIGNED;
|
|
|
|
}
|
2022-03-28 15:22:35 +01:00
|
|
|
}
|
2020-11-04 11:36:35 +01:00
|
|
|
}
|
2020-07-08 15:16:19 +02:00
|
|
|
copy_tv(tv, rettv);
|
2020-11-04 11:36:35 +01:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2023-10-27 19:35:26 +02:00
|
|
|
done:
|
2023-01-06 18:42:20 +00:00
|
|
|
if (len > 0)
|
|
|
|
name[len] = cc;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-01-12 17:06:27 +00:00
|
|
|
/*
|
|
|
|
* Get the value of internal variable "name", also handling "import.name".
|
|
|
|
* Return OK or FAIL. If OK is returned "rettv" must be cleared.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
eval_variable_import(
|
|
|
|
char_u *name,
|
|
|
|
typval_T *rettv)
|
|
|
|
{
|
|
|
|
char_u *s = name;
|
|
|
|
while (ASCII_ISALNUM(*s) || *s == '_')
|
|
|
|
++s;
|
|
|
|
int len = (int)(s - name);
|
|
|
|
|
|
|
|
if (eval_variable(name, len, 0, rettv, NULL, EVAL_VAR_IMPORT) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
if (rettv->v_type == VAR_ANY && *s == '.')
|
|
|
|
{
|
2023-01-12 20:04:51 +00:00
|
|
|
char_u *ns = s + 1;
|
|
|
|
s = ns;
|
|
|
|
while (ASCII_ISALNUM(*s) || *s == '_')
|
|
|
|
++s;
|
2023-01-12 17:06:27 +00:00
|
|
|
int sid = rettv->vval.v_number;
|
2023-01-12 20:04:51 +00:00
|
|
|
return eval_variable(ns, (int)(s - ns), sid, rettv, NULL, 0);
|
2023-01-12 17:06:27 +00:00
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Check if variable "name[len]" is a local variable or an argument.
|
|
|
|
* If so, "*eval_lavars_used" is set to TRUE.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
check_vars(char_u *name, int len)
|
|
|
|
{
|
|
|
|
int cc;
|
|
|
|
char_u *varname;
|
|
|
|
hashtab_T *ht;
|
|
|
|
|
|
|
|
if (eval_lavars_used == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// truncate the name, so that we can use strcmp()
|
|
|
|
cc = name[len];
|
|
|
|
name[len] = NUL;
|
|
|
|
|
|
|
|
ht = find_var_ht(name, &varname);
|
|
|
|
if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht())
|
|
|
|
{
|
|
|
|
if (find_var(name, NULL, TRUE) != NULL)
|
|
|
|
*eval_lavars_used = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
name[len] = cc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find variable "name" in the list of variables.
|
|
|
|
* Return a pointer to it if found, NULL if not found.
|
|
|
|
* Careful: "a:0" variables don't have a name.
|
2021-01-06 21:59:39 +01:00
|
|
|
* When "htp" is not NULL set "htp" to the hashtab_T used.
|
2019-08-29 22:09:46 +02:00
|
|
|
*/
|
|
|
|
dictitem_T *
|
|
|
|
find_var(char_u *name, hashtab_T **htp, int no_autoload)
|
|
|
|
{
|
|
|
|
char_u *varname;
|
|
|
|
hashtab_T *ht;
|
|
|
|
dictitem_T *ret = NULL;
|
|
|
|
|
|
|
|
ht = find_var_ht(name, &varname);
|
|
|
|
if (htp != NULL)
|
|
|
|
*htp = ht;
|
|
|
|
if (ht == NULL)
|
|
|
|
return NULL;
|
2021-01-06 21:59:39 +01:00
|
|
|
ret = find_var_in_ht(ht, *name, varname, no_autoload);
|
2019-08-29 22:09:46 +02:00
|
|
|
if (ret != NULL)
|
|
|
|
return ret;
|
|
|
|
|
2019-08-30 15:46:30 +02:00
|
|
|
// Search in parent scope for lambda
|
2021-01-06 21:59:39 +01:00
|
|
|
ret = find_var_in_scoped_ht(name, no_autoload);
|
2020-11-19 21:47:56 +01:00
|
|
|
if (ret != NULL)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
// in Vim9 script items without a scope can be script-local
|
|
|
|
if (in_vim9script() && name[0] != NUL && name[1] != ':')
|
|
|
|
{
|
|
|
|
ht = get_script_local_ht();
|
|
|
|
if (ht != NULL)
|
|
|
|
{
|
2021-01-06 21:59:39 +01:00
|
|
|
ret = find_var_in_ht(ht, *name, varname, no_autoload);
|
2020-11-19 21:47:56 +01:00
|
|
|
if (ret != NULL)
|
|
|
|
{
|
|
|
|
if (htp != NULL)
|
|
|
|
*htp = ht;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 20:18:56 +00:00
|
|
|
// When using "vim9script autoload" script-local items are prefixed but can
|
|
|
|
// be used with s:name.
|
|
|
|
if (SCRIPT_ID_VALID(current_sctx.sc_sid)
|
2022-10-13 17:47:42 +01:00
|
|
|
&& (in_vim9script() || (name[0] == 's' && name[1] == ':')))
|
2022-01-13 20:18:56 +00:00
|
|
|
{
|
|
|
|
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
|
|
|
|
|
|
|
|
if (si->sn_autoload_prefix != NULL)
|
|
|
|
{
|
2022-10-13 17:47:42 +01:00
|
|
|
char_u *base_name = (name[0] == 's' && name[1] == ':')
|
|
|
|
? name + 2 : name;
|
|
|
|
char_u *auto_name = concat_str(si->sn_autoload_prefix, base_name);
|
2022-01-13 20:18:56 +00:00
|
|
|
|
|
|
|
if (auto_name != NULL)
|
|
|
|
{
|
|
|
|
ht = &globvarht;
|
2022-10-13 17:47:42 +01:00
|
|
|
ret = find_var_in_ht(ht, 'g', auto_name, TRUE);
|
2022-01-13 20:39:41 +00:00
|
|
|
vim_free(auto_name);
|
2022-01-13 20:18:56 +00:00
|
|
|
if (ret != NULL)
|
|
|
|
{
|
|
|
|
if (htp != NULL)
|
|
|
|
*htp = ht;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 21:47:56 +01:00
|
|
|
return NULL;
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
2022-01-07 18:20:55 +00:00
|
|
|
/*
|
|
|
|
* Like find_var() but if the name starts with <SNR>99_ then look in the
|
|
|
|
* referenced script (used for a funcref).
|
|
|
|
*/
|
|
|
|
dictitem_T *
|
|
|
|
find_var_also_in_script(char_u *name, hashtab_T **htp, int no_autoload)
|
|
|
|
{
|
2024-01-04 21:19:04 +01:00
|
|
|
if (STRNCMP(name, "<SNR>", 5) == 0 && SAFE_isdigit(name[5]))
|
2022-01-07 18:20:55 +00:00
|
|
|
{
|
|
|
|
char_u *p = name + 5;
|
|
|
|
int sid = getdigits(&p);
|
|
|
|
|
|
|
|
if (SCRIPT_ID_VALID(sid) && *p == '_')
|
|
|
|
{
|
|
|
|
hashtab_T *ht = &SCRIPT_VARS(sid);
|
|
|
|
|
|
|
|
if (ht != NULL)
|
|
|
|
{
|
|
|
|
dictitem_T *di = find_var_in_ht(ht, 0, p + 1, no_autoload);
|
|
|
|
|
|
|
|
if (di != NULL)
|
2022-01-08 15:44:22 +00:00
|
|
|
{
|
|
|
|
if (htp != NULL)
|
|
|
|
*htp = ht;
|
2022-01-07 18:20:55 +00:00
|
|
|
return di;
|
2022-01-08 15:44:22 +00:00
|
|
|
}
|
2022-01-07 18:20:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return find_var(name, htp, no_autoload);
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Find variable "varname" in hashtab "ht" with name "htname".
|
2020-04-03 18:43:35 +02:00
|
|
|
* When "varname" is empty returns curwin/curtab/etc vars dictionary.
|
2019-08-29 22:09:46 +02:00
|
|
|
* Returns NULL if not found.
|
|
|
|
*/
|
|
|
|
dictitem_T *
|
|
|
|
find_var_in_ht(
|
|
|
|
hashtab_T *ht,
|
|
|
|
int htname,
|
|
|
|
char_u *varname,
|
|
|
|
int no_autoload)
|
|
|
|
{
|
|
|
|
hashitem_T *hi;
|
|
|
|
|
|
|
|
if (*varname == NUL)
|
|
|
|
{
|
|
|
|
// Must be something like "s:", otherwise "ht" would be NULL.
|
|
|
|
switch (htname)
|
|
|
|
{
|
|
|
|
case 's': return &SCRIPT_SV(current_sctx.sc_sid)->sv_var;
|
|
|
|
case 'g': return &globvars_var;
|
|
|
|
case 'v': return &vimvars_var;
|
|
|
|
case 'b': return &curbuf->b_bufvar;
|
|
|
|
case 'w': return &curwin->w_winvar;
|
|
|
|
case 't': return &curtab->tp_winvar;
|
|
|
|
case 'l': return get_funccal_local_var();
|
|
|
|
case 'a': return get_funccal_args_var();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hi = hash_find(ht, varname);
|
|
|
|
if (HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
// For global variables we may try auto-loading the script. If it
|
|
|
|
// worked find the variable again. Don't auto-load a script if it was
|
|
|
|
// loaded already, otherwise it would be loaded every time when
|
|
|
|
// checking if a function name is a Funcref variable.
|
|
|
|
if (ht == &globvarht && !no_autoload)
|
|
|
|
{
|
|
|
|
// Note: script_autoload() may make "hi" invalid. It must either
|
|
|
|
// be obtained again or not used.
|
|
|
|
if (!script_autoload(varname, FALSE) || aborting())
|
|
|
|
return NULL;
|
|
|
|
hi = hash_find(ht, varname);
|
|
|
|
}
|
|
|
|
if (HASHITEM_EMPTY(hi))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return HI2DI(hi);
|
|
|
|
}
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
/*
|
|
|
|
* Get the script-local hashtab. NULL if not in a script context.
|
|
|
|
*/
|
2020-10-08 21:30:40 +02:00
|
|
|
hashtab_T *
|
2020-01-26 15:56:19 +01:00
|
|
|
get_script_local_ht(void)
|
|
|
|
{
|
|
|
|
scid_T sid = current_sctx.sc_sid;
|
|
|
|
|
2020-08-29 13:39:17 +02:00
|
|
|
if (SCRIPT_ID_VALID(sid))
|
2020-01-26 15:56:19 +01:00
|
|
|
return &SCRIPT_VARS(sid);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-03-03 21:22:41 +01:00
|
|
|
* Look for "name[len]" in script-local variables and functions.
|
2021-03-14 13:21:35 +01:00
|
|
|
* When "cmd" is TRUE it must look like a command, a function must be followed
|
|
|
|
* by "(" or "->".
|
2020-12-12 14:33:41 +01:00
|
|
|
* Return OK when found, FAIL when not found.
|
2020-01-26 15:56:19 +01:00
|
|
|
*/
|
2020-12-12 14:33:41 +01:00
|
|
|
int
|
2021-03-03 21:22:41 +01:00
|
|
|
lookup_scriptitem(
|
2020-12-12 14:33:41 +01:00
|
|
|
char_u *name,
|
|
|
|
size_t len,
|
2021-03-14 13:21:35 +01:00
|
|
|
int cmd,
|
2020-12-12 14:33:41 +01:00
|
|
|
cctx_T *dummy UNUSED)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
|
|
|
hashtab_T *ht = get_script_local_ht();
|
|
|
|
char_u buffer[30];
|
|
|
|
char_u *p;
|
2020-12-12 14:33:41 +01:00
|
|
|
int res;
|
2020-01-26 15:56:19 +01:00
|
|
|
hashitem_T *hi;
|
2021-03-03 21:22:41 +01:00
|
|
|
int is_global = FALSE;
|
|
|
|
char_u *fname = name;
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
if (ht == NULL)
|
2020-12-12 14:33:41 +01:00
|
|
|
return FAIL;
|
2020-01-26 15:56:19 +01:00
|
|
|
if (len < sizeof(buffer) - 1)
|
|
|
|
{
|
2020-05-09 13:06:24 +02:00
|
|
|
// avoid an alloc/free for short names
|
2020-01-26 15:56:19 +01:00
|
|
|
vim_strncpy(buffer, name, len);
|
|
|
|
p = buffer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-06-12 22:59:11 +02:00
|
|
|
p = vim_strnsave(name, len);
|
2020-01-26 15:56:19 +01:00
|
|
|
if (p == NULL)
|
2020-12-12 14:33:41 +01:00
|
|
|
return FAIL;
|
2020-01-26 15:56:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hi = hash_find(ht, p);
|
2020-12-12 14:33:41 +01:00
|
|
|
res = HASHITEM_EMPTY(hi) ? FAIL : OK;
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
// if not script-local, then perhaps imported
|
2022-02-13 21:51:08 +00:00
|
|
|
if (res == FAIL && find_imported(p, 0, FALSE) != NULL)
|
2020-12-12 14:33:41 +01:00
|
|
|
res = OK;
|
2020-01-26 15:56:19 +01:00
|
|
|
if (p != buffer)
|
|
|
|
vim_free(p);
|
2021-03-03 21:22:41 +01:00
|
|
|
|
2021-03-14 13:21:35 +01:00
|
|
|
// Find a function, so that a following "->" works.
|
|
|
|
// When used as a command require "(" or "->" to follow, "Cmd" is a user
|
|
|
|
// command while "Cmd()" is a function call.
|
2021-03-03 21:22:41 +01:00
|
|
|
if (res != OK)
|
|
|
|
{
|
2021-03-14 13:21:35 +01:00
|
|
|
p = skipwhite(name + len);
|
|
|
|
|
|
|
|
if (!cmd || name[len] == '(' || (p[0] == '-' && p[1] == '>'))
|
2021-03-03 21:22:41 +01:00
|
|
|
{
|
2021-03-14 13:21:35 +01:00
|
|
|
// Do not check for an internal function, since it might also be a
|
|
|
|
// valid command, such as ":split" versus "split()".
|
|
|
|
// Skip "g:" before a function name.
|
|
|
|
if (name[0] == 'g' && name[1] == ':')
|
|
|
|
{
|
|
|
|
is_global = TRUE;
|
|
|
|
fname = name + 2;
|
|
|
|
}
|
2022-01-13 21:15:21 +00:00
|
|
|
if (find_func(fname, is_global) != NULL)
|
2021-03-14 13:21:35 +01:00
|
|
|
res = OK;
|
2021-03-03 21:22:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-12 14:33:41 +01:00
|
|
|
return res;
|
2020-01-26 15:56:19 +01:00
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Find the hashtab used for a variable name.
|
|
|
|
* Return NULL if the name is not valid.
|
|
|
|
* Set "varname" to the start of name without ':'.
|
|
|
|
*/
|
|
|
|
hashtab_T *
|
|
|
|
find_var_ht(char_u *name, char_u **varname)
|
|
|
|
{
|
|
|
|
hashitem_T *hi;
|
|
|
|
hashtab_T *ht;
|
|
|
|
|
|
|
|
if (name[0] == NUL)
|
|
|
|
return NULL;
|
|
|
|
if (name[1] != ':')
|
|
|
|
{
|
|
|
|
// The name must not start with a colon or #.
|
|
|
|
if (name[0] == ':' || name[0] == AUTOLOAD_CHAR)
|
|
|
|
return NULL;
|
|
|
|
*varname = name;
|
|
|
|
|
|
|
|
// "version" is "v:version" in all scopes if scriptversion < 3.
|
|
|
|
// Same for a few other variables marked with VV_COMPAT.
|
2021-08-15 13:49:42 +02:00
|
|
|
if (in_old_script(3))
|
2019-08-29 22:09:46 +02:00
|
|
|
{
|
|
|
|
hi = hash_find(&compat_hashtab, name);
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
return &compat_hashtab;
|
|
|
|
}
|
|
|
|
|
|
|
|
ht = get_funccal_local_ht();
|
2020-01-26 15:56:19 +01:00
|
|
|
if (ht != NULL)
|
|
|
|
return ht; // local variable
|
|
|
|
|
2021-06-11 22:05:47 +02:00
|
|
|
// In Vim9 script items at the script level are script-local, except
|
|
|
|
// for autoload names.
|
|
|
|
if (in_vim9script() && vim_strchr(name, AUTOLOAD_CHAR) == NULL)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
|
|
|
ht = get_script_local_ht();
|
|
|
|
if (ht != NULL)
|
|
|
|
return ht;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &globvarht; // global variable
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
*varname = name + 2;
|
|
|
|
if (*name == 'g') // global variable
|
|
|
|
return &globvarht;
|
|
|
|
// There must be no ':' or '#' in the rest of the name, unless g: is used
|
|
|
|
if (vim_strchr(name + 2, ':') != NULL
|
|
|
|
|| vim_strchr(name + 2, AUTOLOAD_CHAR) != NULL)
|
|
|
|
return NULL;
|
|
|
|
if (*name == 'b') // buffer variable
|
|
|
|
return &curbuf->b_vars->dv_hashtab;
|
|
|
|
if (*name == 'w') // window variable
|
|
|
|
return &curwin->w_vars->dv_hashtab;
|
|
|
|
if (*name == 't') // tab page variable
|
|
|
|
return &curtab->tp_vars->dv_hashtab;
|
|
|
|
if (*name == 'v') // v: variable
|
|
|
|
return &vimvarht;
|
2020-02-26 20:15:18 +01:00
|
|
|
if (get_current_funccal() != NULL
|
2022-09-06 18:57:08 +01:00
|
|
|
&& get_current_funccal()->fc_func->uf_def_status
|
|
|
|
== UF_NOT_COMPILED)
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2020-02-26 20:15:18 +01:00
|
|
|
// a: and l: are only used in functions defined with ":function"
|
2020-01-26 15:56:19 +01:00
|
|
|
if (*name == 'a') // a: function argument
|
|
|
|
return get_funccal_args_ht();
|
|
|
|
if (*name == 'l') // l: local function variable
|
|
|
|
return get_funccal_local_ht();
|
|
|
|
}
|
|
|
|
if (*name == 's') // script variable
|
|
|
|
{
|
|
|
|
ht = get_script_local_ht();
|
|
|
|
if (ht != NULL)
|
|
|
|
return ht;
|
|
|
|
}
|
2019-08-29 22:09:46 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Get the string value of a (global/local) variable.
|
|
|
|
* Note: see tv_get_string() for how long the pointer remains valid.
|
|
|
|
* Returns NULL when it doesn't exist.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_var_value(char_u *name)
|
|
|
|
{
|
|
|
|
dictitem_T *v;
|
|
|
|
|
|
|
|
v = find_var(name, NULL, FALSE);
|
|
|
|
if (v == NULL)
|
|
|
|
return NULL;
|
|
|
|
return tv_get_string(&v->di_tv);
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* Allocate a new hashtab for a sourced script. It will be used while
|
|
|
|
* sourcing this script and when executing functions defined in the script.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
new_script_vars(scid_T id)
|
|
|
|
{
|
|
|
|
scriptvar_T *sv;
|
|
|
|
|
2020-01-12 17:42:55 +01:00
|
|
|
sv = ALLOC_CLEAR_ONE(scriptvar_T);
|
|
|
|
if (sv == NULL)
|
|
|
|
return;
|
|
|
|
init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
|
2020-01-26 19:26:46 +01:00
|
|
|
SCRIPT_ITEM(id)->sn_vars = sv;
|
2019-08-29 22:09:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize dictionary "dict" as a scope and set variable "dict_var" to
|
|
|
|
* point to it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
|
|
|
|
{
|
|
|
|
hash_init(&dict->dv_hashtab);
|
|
|
|
dict->dv_lock = 0;
|
|
|
|
dict->dv_scope = scope;
|
|
|
|
dict->dv_refcount = DO_NOT_FREE_CNT;
|
|
|
|
dict->dv_copyID = 0;
|
|
|
|
dict_var->di_tv.vval.v_dict = dict;
|
|
|
|
dict_var->di_tv.v_type = VAR_DICT;
|
|
|
|
dict_var->di_tv.v_lock = VAR_FIXED;
|
|
|
|
dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
|
|
|
dict_var->di_key[0] = NUL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unreference a dictionary initialized by init_var_dict().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
unref_var_dict(dict_T *dict)
|
|
|
|
{
|
2019-08-30 15:46:30 +02:00
|
|
|
// Now the dict needs to be freed if no one else is using it, go back to
|
|
|
|
// normal reference counting.
|
2019-08-29 22:09:46 +02:00
|
|
|
dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
|
|
|
|
dict_unref(dict);
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Clean up a list of internal variables.
|
|
|
|
* Frees all allocated variables and the value they contain.
|
|
|
|
* Clears hashtab "ht", does not free it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
vars_clear(hashtab_T *ht)
|
|
|
|
{
|
|
|
|
vars_clear_ext(ht, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like vars_clear(), but only free the value if "free_val" is TRUE.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
vars_clear_ext(hashtab_T *ht, int free_val)
|
|
|
|
{
|
|
|
|
int todo;
|
|
|
|
hashitem_T *hi;
|
|
|
|
dictitem_T *v;
|
|
|
|
|
|
|
|
hash_lock(ht);
|
|
|
|
todo = (int)ht->ht_used;
|
2023-03-07 17:13:51 +00:00
|
|
|
FOR_ALL_HASHTAB_ITEMS(ht, hi, todo)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
{
|
|
|
|
--todo;
|
|
|
|
|
|
|
|
// Free the variable. Don't remove it from the hashtab,
|
|
|
|
// ht_array might change then. hash_clear() takes care of it
|
|
|
|
// later.
|
|
|
|
v = HI2DI(hi);
|
|
|
|
if (free_val)
|
|
|
|
clear_tv(&v->di_tv);
|
|
|
|
if (v->di_flags & DI_FLAGS_ALLOC)
|
|
|
|
vim_free(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hash_clear(ht);
|
2020-10-14 19:39:19 +02:00
|
|
|
hash_init(ht);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a variable from hashtab "ht" at item "hi".
|
|
|
|
* Clear the variable value and free the dictitem.
|
|
|
|
*/
|
2020-10-10 19:07:09 +02:00
|
|
|
void
|
2019-08-27 22:48:30 +02:00
|
|
|
delete_var(hashtab_T *ht, hashitem_T *hi)
|
|
|
|
{
|
|
|
|
dictitem_T *di = HI2DI(hi);
|
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
if (hash_remove(ht, hi, "delete variable") != OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clear_tv(&di->di_tv);
|
|
|
|
vim_free(di);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* List the value of one internal variable.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
list_one_var(dictitem_T *v, char *prefix, int *first)
|
|
|
|
{
|
|
|
|
char_u *tofree;
|
|
|
|
char_u *s;
|
|
|
|
char_u numbuf[NUMBUFLEN];
|
|
|
|
|
|
|
|
s = echo_string(&v->di_tv, &tofree, numbuf, get_copyID());
|
|
|
|
list_one_var_a(prefix, v->di_key, v->di_tv.v_type,
|
|
|
|
s == NULL ? (char_u *)"" : s, first);
|
|
|
|
vim_free(tofree);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
list_one_var_a(
|
|
|
|
char *prefix,
|
|
|
|
char_u *name,
|
|
|
|
int type,
|
|
|
|
char_u *string,
|
|
|
|
int *first) // when TRUE clear rest of screen and set to FALSE
|
|
|
|
{
|
|
|
|
// don't use msg() or msg_attr() to avoid overwriting "v:statusmsg"
|
|
|
|
msg_start();
|
|
|
|
msg_puts(prefix);
|
|
|
|
if (name != NULL) // "a:" vars don't have a name stored
|
|
|
|
msg_puts((char *)name);
|
|
|
|
msg_putchar(' ');
|
|
|
|
msg_advance(22);
|
|
|
|
if (type == VAR_NUMBER)
|
|
|
|
msg_putchar('#');
|
|
|
|
else if (type == VAR_FUNC || type == VAR_PARTIAL)
|
|
|
|
msg_putchar('*');
|
|
|
|
else if (type == VAR_LIST)
|
|
|
|
{
|
|
|
|
msg_putchar('[');
|
|
|
|
if (*string == '[')
|
|
|
|
++string;
|
|
|
|
}
|
|
|
|
else if (type == VAR_DICT)
|
|
|
|
{
|
|
|
|
msg_putchar('{');
|
|
|
|
if (*string == '{')
|
|
|
|
++string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
msg_putchar(' ');
|
|
|
|
|
|
|
|
msg_outtrans(string);
|
|
|
|
|
|
|
|
if (type == VAR_FUNC || type == VAR_PARTIAL)
|
|
|
|
msg_puts("()");
|
|
|
|
if (*first)
|
|
|
|
{
|
|
|
|
msg_clr_eos();
|
|
|
|
*first = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-24 23:13:51 +02:00
|
|
|
/*
|
|
|
|
* Addition handling for setting a v: variable.
|
|
|
|
* Return TRUE if the variable should be set normally,
|
|
|
|
* FALSE if nothing else needs to be done.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
before_set_vvar(
|
|
|
|
char_u *varname,
|
|
|
|
dictitem_T *di,
|
|
|
|
typval_T *tv,
|
|
|
|
int copy,
|
|
|
|
int *type_error)
|
|
|
|
{
|
|
|
|
if (di->di_tv.v_type == VAR_STRING)
|
|
|
|
{
|
|
|
|
VIM_CLEAR(di->di_tv.vval.v_string);
|
|
|
|
if (copy || tv->v_type != VAR_STRING)
|
|
|
|
{
|
|
|
|
char_u *val = tv_get_string(tv);
|
|
|
|
|
|
|
|
// Careful: when assigning to v:errmsg and
|
|
|
|
// tv_get_string() causes an error message the variable
|
|
|
|
// will already be set.
|
|
|
|
if (di->di_tv.vval.v_string == NULL)
|
|
|
|
di->di_tv.vval.v_string = vim_strsave(val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Take over the string to avoid an extra alloc/free.
|
|
|
|
di->di_tv.vval.v_string = tv->vval.v_string;
|
|
|
|
tv->vval.v_string = NULL;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else if (di->di_tv.v_type == VAR_NUMBER)
|
|
|
|
{
|
|
|
|
di->di_tv.vval.v_number = tv_get_number(tv);
|
|
|
|
if (STRCMP(varname, "searchforward") == 0)
|
|
|
|
set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
|
|
|
|
#ifdef FEAT_SEARCH_EXTRA
|
|
|
|
else if (STRCMP(varname, "hlsearch") == 0)
|
|
|
|
{
|
|
|
|
no_hlsearch = !di->di_tv.vval.v_number;
|
|
|
|
redraw_all_later(UPD_SOME_VALID);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else if (di->di_tv.v_type != tv->v_type)
|
|
|
|
{
|
|
|
|
*type_error = TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Set variable "name" to value in "tv".
|
|
|
|
* If the variable already exists, the value is updated.
|
|
|
|
* Otherwise the variable is created.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_var(
|
|
|
|
char_u *name,
|
|
|
|
typval_T *tv,
|
|
|
|
int copy) // make copy of value in "tv"
|
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
set_var_const(name, 0, NULL, tv, copy, ASSIGN_DECL, 0);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-01-13 20:18:56 +00:00
|
|
|
* Set variable "name" to value in "tv_arg".
|
2022-01-06 21:10:28 +00:00
|
|
|
* When "sid" is non-zero "name" is in the script with this ID.
|
2019-08-27 22:48:30 +02:00
|
|
|
* If the variable already exists and "is_const" is FALSE the value is updated.
|
|
|
|
* Otherwise the variable is created.
|
|
|
|
*/
|
2024-03-09 15:44:19 +01:00
|
|
|
int
|
2019-08-27 22:48:30 +02:00
|
|
|
set_var_const(
|
|
|
|
char_u *name,
|
2022-01-06 21:10:28 +00:00
|
|
|
scid_T sid,
|
2021-11-26 17:36:51 +00:00
|
|
|
type_T *type_arg,
|
2020-09-09 22:27:58 +02:00
|
|
|
typval_T *tv_arg,
|
2019-08-27 22:48:30 +02:00
|
|
|
int copy, // make copy of value in "tv"
|
2021-04-13 21:48:03 +02:00
|
|
|
int flags_arg, // ASSIGN_CONST, ASSIGN_FINAL, etc.
|
2021-02-11 21:19:34 +01:00
|
|
|
int var_idx) // index for ":let [a, b] = list"
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2020-09-09 22:27:58 +02:00
|
|
|
typval_T *tv = tv_arg;
|
2021-11-26 17:36:51 +00:00
|
|
|
type_T *type = type_arg;
|
2020-09-09 22:27:58 +02:00
|
|
|
typval_T bool_tv;
|
2020-01-26 15:56:19 +01:00
|
|
|
dictitem_T *di;
|
2022-02-21 15:59:11 +00:00
|
|
|
typval_T *dest_tv;
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *varname;
|
2022-01-10 18:08:00 +00:00
|
|
|
char_u *name_tofree = NULL;
|
2022-01-06 21:10:28 +00:00
|
|
|
hashtab_T *ht = NULL;
|
2020-01-26 15:56:19 +01:00
|
|
|
int is_script_local;
|
2020-09-14 18:15:09 +02:00
|
|
|
int vim9script = in_vim9script();
|
2021-03-31 21:07:24 +02:00
|
|
|
int var_in_vim9script;
|
2022-01-10 18:08:00 +00:00
|
|
|
int var_in_autoload = FALSE;
|
2021-04-13 21:48:03 +02:00
|
|
|
int flags = flags_arg;
|
2021-12-10 10:37:38 +00:00
|
|
|
int free_tv_arg = !copy; // free tv_arg if not used
|
2024-03-09 15:44:19 +01:00
|
|
|
int rc = FAIL;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2022-01-06 21:10:28 +00:00
|
|
|
if (sid != 0)
|
|
|
|
{
|
|
|
|
if (SCRIPT_ID_VALID(sid))
|
|
|
|
ht = &SCRIPT_VARS(sid);
|
|
|
|
varname = name;
|
|
|
|
}
|
|
|
|
else
|
2022-01-10 18:08:00 +00:00
|
|
|
{
|
2022-01-21 13:29:56 +00:00
|
|
|
scriptitem_T *si;
|
2022-01-10 18:08:00 +00:00
|
|
|
|
2022-01-21 13:29:56 +00:00
|
|
|
if (in_vim9script() && is_export
|
|
|
|
&& SCRIPT_ID_VALID(current_sctx.sc_sid)
|
|
|
|
&& (si = SCRIPT_ITEM(current_sctx.sc_sid))
|
2022-03-03 17:05:35 +00:00
|
|
|
->sn_autoload_prefix != NULL)
|
2022-01-21 13:29:56 +00:00
|
|
|
{
|
2022-01-10 18:08:00 +00:00
|
|
|
// In a vim9 autoload script an exported variable is put in the
|
|
|
|
// global namespace with the autoload prefix.
|
|
|
|
var_in_autoload = TRUE;
|
2022-01-21 13:29:56 +00:00
|
|
|
varname = concat_str(si->sn_autoload_prefix, name);
|
2022-01-10 18:08:00 +00:00
|
|
|
if (varname == NULL)
|
|
|
|
goto failed;
|
|
|
|
name_tofree = varname;
|
|
|
|
ht = &globvarht;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ht = find_var_ht(name, &varname);
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
if (ht == NULL || *varname == NUL)
|
|
|
|
{
|
2022-01-01 14:19:49 +00:00
|
|
|
semsg(_(e_illegal_variable_name_str), name);
|
2020-09-12 19:51:42 +02:00
|
|
|
goto failed;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2022-02-07 13:56:44 +00:00
|
|
|
is_script_local = ht == get_script_local_ht() || sid != 0
|
|
|
|
|| var_in_autoload;
|
2020-06-21 15:52:59 +02:00
|
|
|
|
2020-09-14 18:15:09 +02:00
|
|
|
if (vim9script
|
2020-06-21 15:52:59 +02:00
|
|
|
&& !is_script_local
|
2021-01-01 21:05:55 +01:00
|
|
|
&& (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
|
2020-12-22 21:19:39 +01:00
|
|
|
&& (flags & (ASSIGN_CONST | ASSIGN_FINAL)) == 0
|
2020-06-21 15:52:59 +02:00
|
|
|
&& name[1] == ':')
|
2020-06-20 22:50:47 +02:00
|
|
|
{
|
2020-06-21 15:52:59 +02:00
|
|
|
vim9_declare_error(name);
|
2020-09-12 19:51:42 +02:00
|
|
|
goto failed;
|
2020-06-20 22:50:47 +02:00
|
|
|
}
|
2022-09-11 15:14:05 +01:00
|
|
|
if ((flags & ASSIGN_FOR_LOOP) && is_scoped_variable(name))
|
2021-04-13 21:48:03 +02:00
|
|
|
// Do not make g:var, w:var, b:var or t:var final.
|
|
|
|
flags &= ~ASSIGN_FINAL;
|
|
|
|
|
2021-03-31 21:07:24 +02:00
|
|
|
var_in_vim9script = is_script_local && current_script_is_vim9();
|
2021-04-10 17:18:09 +02:00
|
|
|
if (var_in_vim9script && name[0] == '_' && name[1] == NUL)
|
|
|
|
{
|
2021-04-10 22:35:43 +02:00
|
|
|
// For "[a, _] = list" the underscore is ignored.
|
|
|
|
if ((flags & ASSIGN_UNPACK) == 0)
|
|
|
|
emsg(_(e_cannot_use_underscore_here));
|
2021-04-10 17:18:09 +02:00
|
|
|
goto failed;
|
|
|
|
}
|
2020-06-20 22:50:47 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
di = find_var_in_ht(ht, 0, varname, TRUE);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-07-18 20:40:33 +02:00
|
|
|
if (di == NULL && var_in_vim9script)
|
2020-09-09 22:27:58 +02:00
|
|
|
{
|
2022-02-13 21:51:08 +00:00
|
|
|
imported_T *import = find_imported(varname, 0, FALSE);
|
2020-09-09 22:27:58 +02:00
|
|
|
|
2021-07-18 20:40:33 +02:00
|
|
|
if (import != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-06 21:10:28 +00:00
|
|
|
// imported name space cannot be used
|
2021-07-18 20:40:33 +02:00
|
|
|
if ((flags & ASSIGN_NO_DECL) == 0)
|
2021-03-31 21:47:33 +02:00
|
|
|
{
|
2021-07-18 20:40:33 +02:00
|
|
|
semsg(_(e_redefining_imported_item_str), name);
|
2021-03-31 21:47:33 +02:00
|
|
|
goto failed;
|
|
|
|
}
|
2022-01-06 21:10:28 +00:00
|
|
|
semsg(_(e_cannot_use_str_itself_it_is_imported), name);
|
|
|
|
goto failed;
|
2021-07-18 20:40:33 +02:00
|
|
|
}
|
2022-02-13 13:56:29 +00:00
|
|
|
if (!in_vim9script())
|
|
|
|
{
|
|
|
|
semsg(_(e_cannot_create_vim9_script_variable_in_function_str),
|
|
|
|
name);
|
|
|
|
goto failed;
|
|
|
|
}
|
2021-07-18 20:40:33 +02:00
|
|
|
}
|
2021-03-31 21:47:33 +02:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
// Search in parent scope which is possible to reference from lambda
|
|
|
|
if (di == NULL)
|
|
|
|
di = find_var_in_scoped_ht(name, TRUE);
|
2021-02-11 21:19:34 +01:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
|
|
|
|
&& var_wrong_func_name(name, di == NULL))
|
|
|
|
goto failed;
|
2020-06-19 18:34:15 +02:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
if (need_convert_to_bool(type, tv))
|
|
|
|
{
|
|
|
|
// Destination is a bool and the value is not, but it can be
|
|
|
|
// converted.
|
|
|
|
CLEAR_FIELD(bool_tv);
|
|
|
|
bool_tv.v_type = VAR_BOOL;
|
|
|
|
bool_tv.vval.v_number = tv2bool(tv) ? VVAL_TRUE : VVAL_FALSE;
|
|
|
|
tv = &bool_tv;
|
|
|
|
}
|
2020-12-26 20:09:15 +01:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
if (di != NULL)
|
|
|
|
{
|
|
|
|
// Item already exists. Allowed to replace when reloading.
|
|
|
|
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
|
|
|
|
&& (flags & ASSIGN_FOR_LOOP) == 0)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
emsg(_(e_cannot_modify_existing_variable));
|
|
|
|
goto failed;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2022-02-21 15:59:11 +00:00
|
|
|
|
|
|
|
if (is_script_local && vim9script
|
|
|
|
&& (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
semsg(_(e_redefining_script_item_str), name);
|
|
|
|
goto failed;
|
2021-07-18 20:40:33 +02:00
|
|
|
}
|
|
|
|
|
2023-12-21 17:18:54 +01:00
|
|
|
if (check_typval_is_value(&di->di_tv) == FAIL)
|
2023-10-27 19:35:26 +02:00
|
|
|
goto failed;
|
|
|
|
|
2024-02-06 11:03:36 +01:00
|
|
|
// List and Blob types can be modified in-place using the "+="
|
|
|
|
// compound operator. For other types, this is not allowed.
|
|
|
|
int type_inplace_modifiable =
|
|
|
|
(di->di_tv.v_type == VAR_LIST || di->di_tv.v_type == VAR_BLOB);
|
|
|
|
|
|
|
|
if (var_in_vim9script && (flags & ASSIGN_FOR_LOOP) == 0
|
|
|
|
&& ((flags & ASSIGN_COMPOUND_OP) == 0
|
|
|
|
|| !type_inplace_modifiable))
|
2021-07-18 20:40:33 +02:00
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
where_T where = WHERE_INIT;
|
2022-04-04 14:58:06 +01:00
|
|
|
svar_T *sv = find_typval_in_script(&di->di_tv, sid, TRUE);
|
2021-07-18 20:40:33 +02:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
if (sv != NULL)
|
2021-07-18 20:40:33 +02:00
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
// check the type and adjust to bool if needed
|
2023-08-19 13:02:35 +02:00
|
|
|
if (var_idx > 0)
|
|
|
|
{
|
|
|
|
where.wt_index = var_idx;
|
|
|
|
where.wt_kind = WT_VARIABLE;
|
|
|
|
}
|
2022-02-21 15:59:11 +00:00
|
|
|
if (check_script_var_type(sv, tv, name, where) == FAIL)
|
|
|
|
goto failed;
|
|
|
|
if (type == NULL)
|
|
|
|
type = sv->sv_type;
|
2022-04-05 21:40:38 +01:00
|
|
|
sv->sv_flags |= SVFLAG_ASSIGNED;
|
2021-07-18 20:40:33 +02:00
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2021-07-18 20:40:33 +02:00
|
|
|
|
2024-02-06 11:03:36 +01:00
|
|
|
// Modifying a final variable with a List value using the "+="
|
|
|
|
// operator is allowed. For other types, it is not allowed.
|
|
|
|
if (((flags & ASSIGN_FOR_LOOP) == 0
|
|
|
|
&& ((flags & ASSIGN_COMPOUND_OP) == 0
|
|
|
|
|| !type_inplace_modifiable))
|
2023-06-05 19:46:18 +01:00
|
|
|
? var_check_permission(di, name) == FAIL
|
|
|
|
: var_check_ro(di->di_flags, name, FALSE))
|
2022-02-21 15:59:11 +00:00
|
|
|
goto failed;
|
2021-07-18 20:40:33 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-21 15:59:11 +00:00
|
|
|
// can only redefine once
|
|
|
|
di->di_flags &= ~DI_FLAGS_RELOAD;
|
|
|
|
|
|
|
|
// A Vim9 script-local variable is also present in sn_all_vars
|
|
|
|
// and sn_var_vals. It may set "type" from "tv".
|
|
|
|
if (var_in_vim9script || var_in_autoload)
|
|
|
|
update_vim9_script_var(FALSE, di,
|
|
|
|
var_in_autoload ? name : di->di_key, flags,
|
|
|
|
tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// existing variable, need to clear the value
|
|
|
|
|
2023-09-24 23:13:51 +02:00
|
|
|
// Handle setting internal v: variables separately where needed to
|
2022-02-21 15:59:11 +00:00
|
|
|
// prevent changing the type.
|
2023-09-24 23:13:51 +02:00
|
|
|
int type_error = FALSE;
|
|
|
|
if (ht == &vimvarht
|
|
|
|
&& !before_set_vvar(varname, di, tv, copy, &type_error))
|
2022-02-21 15:59:11 +00:00
|
|
|
{
|
2023-09-24 23:13:51 +02:00
|
|
|
if (type_error)
|
|
|
|
semsg(_(e_setting_v_str_to_value_with_wrong_type), varname);
|
|
|
|
goto failed;
|
2022-02-21 15:59:11 +00:00
|
|
|
}
|
2021-01-01 21:05:55 +01:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
clear_tv(&di->di_tv);
|
2022-09-14 00:30:51 +01:00
|
|
|
|
|
|
|
if ((flags & ASSIGN_UPDATE_BLOCK_ID)
|
|
|
|
&& SCRIPT_ID_VALID(current_sctx.sc_sid))
|
|
|
|
{
|
|
|
|
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
|
|
|
|
|
|
|
|
update_script_var_block_id(name, si->sn_current_block_id);
|
|
|
|
}
|
2022-02-21 15:59:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Item not found, check if a function already exists.
|
|
|
|
if (is_script_local && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
|
|
|
|
&& lookup_scriptitem(name, STRLEN(name), FALSE, NULL) == OK)
|
|
|
|
{
|
|
|
|
semsg(_(e_redefining_script_item_str), name);
|
|
|
|
goto failed;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
// add a new variable
|
|
|
|
if (var_in_vim9script && (flags & ASSIGN_NO_DECL))
|
|
|
|
{
|
|
|
|
semsg(_(e_unknown_variable_str), name);
|
|
|
|
goto failed;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2022-11-25 16:31:51 +00:00
|
|
|
if (check_hashtab_frozen(ht, "add variable"))
|
|
|
|
goto failed;
|
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
// Can't add "v:" or "a:" variable.
|
|
|
|
if (ht == &vimvarht || ht == get_funccal_args_ht())
|
|
|
|
{
|
|
|
|
semsg(_(e_illegal_variable_name_str), name);
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the variable name is valid. In Vim9 script an
|
|
|
|
// autoload variable must be prefixed with "g:" unless in an
|
|
|
|
// autoload script.
|
|
|
|
if (!valid_varname(varname, -1, !vim9script
|
|
|
|
|| STRNCMP(name, "g:", 2) == 0 || var_in_autoload))
|
|
|
|
goto failed;
|
|
|
|
|
2023-02-01 13:11:15 +00:00
|
|
|
di = alloc(offsetof(dictitem_T, di_key) + STRLEN(varname) + 1);
|
2022-02-21 15:59:11 +00:00
|
|
|
if (di == NULL)
|
|
|
|
goto failed;
|
|
|
|
STRCPY(di->di_key, varname);
|
2022-11-25 16:31:51 +00:00
|
|
|
if (hash_add(ht, DI2HIKEY(di), "add variable") == FAIL)
|
2022-02-21 15:59:11 +00:00
|
|
|
{
|
|
|
|
vim_free(di);
|
|
|
|
goto failed;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2022-02-21 15:59:11 +00:00
|
|
|
di->di_flags = DI_FLAGS_ALLOC;
|
|
|
|
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
|
|
|
|
di->di_flags |= DI_FLAGS_LOCK;
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
// A Vim9 script-local variable is also added to sn_all_vars and
|
|
|
|
// sn_var_vals. It may set "type" from "tv".
|
|
|
|
if (var_in_vim9script || var_in_autoload)
|
|
|
|
update_vim9_script_var(TRUE, di,
|
|
|
|
var_in_autoload ? name : di->di_key, flags,
|
|
|
|
tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2022-02-21 15:59:11 +00:00
|
|
|
dest_tv = &di->di_tv;
|
2019-08-27 22:48:30 +02:00
|
|
|
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
|
2021-07-18 20:40:33 +02:00
|
|
|
copy_tv(tv, dest_tv);
|
2019-08-27 22:48:30 +02:00
|
|
|
else
|
|
|
|
{
|
2021-07-18 20:40:33 +02:00
|
|
|
*dest_tv = *tv;
|
|
|
|
dest_tv->v_lock = 0;
|
2019-08-27 22:48:30 +02:00
|
|
|
init_tv(tv);
|
|
|
|
}
|
2021-12-10 10:37:38 +00:00
|
|
|
free_tv_arg = FALSE;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-01-02 15:41:03 +01:00
|
|
|
if (vim9script && type != NULL)
|
2022-02-02 20:01:27 +00:00
|
|
|
set_tv_type(dest_tv, type);
|
2021-01-02 15:41:03 +01:00
|
|
|
|
2020-12-22 22:07:30 +01:00
|
|
|
// ":const var = value" locks the value
|
|
|
|
// ":final var = value" locks "var"
|
2020-09-26 15:09:30 +02:00
|
|
|
if (flags & ASSIGN_CONST)
|
2020-08-17 21:07:22 +02:00
|
|
|
// Like :lockvar! name: lock the value and what it contains, but only
|
|
|
|
// if the reference count is up to one. That locks only literal
|
|
|
|
// values.
|
2021-07-18 20:40:33 +02:00
|
|
|
item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
|
|
|
|
|
2024-03-09 15:44:19 +01:00
|
|
|
rc = OK;
|
|
|
|
|
2020-09-12 19:51:42 +02:00
|
|
|
failed:
|
2022-01-10 18:08:00 +00:00
|
|
|
vim_free(name_tofree);
|
2021-12-10 10:37:38 +00:00
|
|
|
if (free_tv_arg)
|
2020-09-12 19:51:42 +02:00
|
|
|
clear_tv(tv_arg);
|
2024-03-09 15:44:19 +01:00
|
|
|
|
|
|
|
return rc;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2020-12-22 20:35:40 +01:00
|
|
|
/*
|
|
|
|
* Check in this order for backwards compatibility:
|
|
|
|
* - Whether the variable is read-only
|
|
|
|
* - Whether the variable value is locked
|
|
|
|
* - Whether the variable is locked
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
var_check_permission(dictitem_T *di, char_u *name)
|
|
|
|
{
|
|
|
|
if (var_check_ro(di->di_flags, name, FALSE)
|
|
|
|
|| value_check_lock(di->di_tv.v_lock, name, FALSE)
|
|
|
|
|| var_check_lock(di->di_flags, name, FALSE))
|
|
|
|
return FAIL;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if di_flags "flags" indicates variable "name" is read-only.
|
|
|
|
* Also give an error message.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
var_check_ro(int flags, char_u *name, int use_gettext)
|
|
|
|
{
|
|
|
|
if (flags & DI_FLAGS_RO)
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
if (name == NULL)
|
|
|
|
emsg(_(e_cannot_change_readonly_variable));
|
|
|
|
else
|
|
|
|
semsg(_(e_cannot_change_readonly_variable_str),
|
2021-07-21 22:20:33 +02:00
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if ((flags & DI_FLAGS_RO_SBX) && sandbox)
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
if (name == NULL)
|
|
|
|
emsg(_(e_cannot_set_variable_in_sandbox));
|
|
|
|
else
|
|
|
|
semsg(_(e_cannot_set_variable_in_sandbox_str),
|
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-09-16 21:08:28 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if di_flags "flags" indicates variable "name" is locked.
|
|
|
|
* Also give an error message.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
var_check_lock(int flags, char_u *name, int use_gettext)
|
|
|
|
{
|
|
|
|
if (flags & DI_FLAGS_LOCK)
|
|
|
|
{
|
|
|
|
semsg(_(e_variable_is_locked_str),
|
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if di_flags "flags" indicates variable "name" is fixed.
|
|
|
|
* Also give an error message.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
var_check_fixed(int flags, char_u *name, int use_gettext)
|
|
|
|
{
|
|
|
|
if (flags & DI_FLAGS_FIX)
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
if (name == NULL)
|
|
|
|
emsg(_(e_cannot_delete_variable));
|
|
|
|
else
|
|
|
|
semsg(_(e_cannot_delete_variable_str),
|
2019-08-27 22:48:30 +02:00
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a funcref is assigned to a valid variable name.
|
|
|
|
* Return TRUE and give an error if not.
|
|
|
|
*/
|
|
|
|
int
|
2020-08-08 15:46:01 +02:00
|
|
|
var_wrong_func_name(
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *name, // points to start of variable name
|
|
|
|
int new_var) // TRUE when creating the variable
|
|
|
|
{
|
2021-03-28 21:14:06 +02:00
|
|
|
// Allow for w: b: s: and t:. In Vim9 script s: is not allowed, because
|
|
|
|
// the name can be used without the s: prefix.
|
2022-09-02 11:25:37 +01:00
|
|
|
// Allow autoload variable.
|
2021-03-28 21:14:06 +02:00
|
|
|
if (!((vim_strchr((char_u *)"wbt", name[0]) != NULL
|
|
|
|
|| (!in_vim9script() && name[0] == 's')) && name[1] == ':')
|
2019-08-27 22:48:30 +02:00
|
|
|
&& !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
|
2022-09-02 11:25:37 +01:00
|
|
|
? name[2] : name[0])
|
|
|
|
&& vim_strchr(name, '#') == NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-04 21:30:47 +00:00
|
|
|
semsg(_(e_funcref_variable_name_must_start_with_capital_str), name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// Don't allow hiding a function. When "v" is not NULL we might be
|
|
|
|
// assigning another function to the same var, the type is checked
|
|
|
|
// below.
|
|
|
|
if (new_var && function_exists(name, FALSE))
|
|
|
|
{
|
2022-01-04 21:30:47 +00:00
|
|
|
semsg(_(e_variable_name_conflicts_with_existing_function_str),
|
2019-08-27 22:48:30 +02:00
|
|
|
name);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-09-16 21:08:28 +02:00
|
|
|
* Return TRUE if "flags" indicates variable "name" has a locked (immutable)
|
|
|
|
* value. Also give an error message, using "name" or _("name") when
|
|
|
|
* "use_gettext" is TRUE.
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
|
|
|
int
|
2020-09-16 21:08:28 +02:00
|
|
|
value_check_lock(int lock, char_u *name, int use_gettext)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
if (lock & VAR_LOCKED)
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
if (name == NULL)
|
|
|
|
emsg(_(e_value_is_locked));
|
|
|
|
else
|
|
|
|
semsg(_(e_value_is_locked_str),
|
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (lock & VAR_FIXED)
|
|
|
|
{
|
2021-12-17 20:15:38 +00:00
|
|
|
if (name == NULL)
|
|
|
|
emsg(_(e_cannot_change_value));
|
|
|
|
else
|
|
|
|
semsg(_(e_cannot_change_value_of_str),
|
|
|
|
use_gettext ? (char_u *)_(name) : name);
|
2019-08-27 22:48:30 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2020-12-19 16:30:44 +01:00
|
|
|
* Check if a variable name is valid. When "autoload" is true "#" is allowed.
|
2021-11-22 20:10:18 +00:00
|
|
|
* If "len" is -1 use all of "varname", otherwise up to "varname[len]".
|
2019-08-27 22:48:30 +02:00
|
|
|
* Return FALSE and give an error if not.
|
|
|
|
*/
|
|
|
|
int
|
2021-11-22 20:10:18 +00:00
|
|
|
valid_varname(char_u *varname, int len, int autoload)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
char_u *p;
|
|
|
|
|
2021-11-22 20:10:18 +00:00
|
|
|
for (p = varname; len < 0 ? *p != NUL : p < varname + len; ++p)
|
2019-08-27 22:48:30 +02:00
|
|
|
if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p))
|
2020-12-19 16:30:44 +01:00
|
|
|
&& !(autoload && *p == AUTOLOAD_CHAR))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-01-01 14:19:49 +00:00
|
|
|
semsg(_(e_illegal_variable_name_str), varname);
|
2019-08-27 22:48:30 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-05-04 18:12:55 +01:00
|
|
|
* Implements the logic to retrieve local variable and option values.
|
|
|
|
* Used by "getwinvar()" "gettabvar()" "gettabwinvar()" "getbufvar()".
|
2019-08-27 22:48:30 +02:00
|
|
|
*/
|
|
|
|
static void
|
2022-05-04 18:12:55 +01:00
|
|
|
get_var_from(
|
|
|
|
char_u *varname,
|
2019-08-27 22:48:30 +02:00
|
|
|
typval_T *rettv,
|
2022-05-04 18:12:55 +01:00
|
|
|
typval_T *deftv, // Default value if not found.
|
|
|
|
int htname, // 't'ab, 'w'indow or 'b'uffer local.
|
|
|
|
tabpage_T *tp, // can be NULL
|
|
|
|
win_T *win,
|
|
|
|
buf_T *buf) // Ignored if htname is not 'b'.
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
dictitem_T *v;
|
|
|
|
int done = FALSE;
|
2022-01-06 13:24:51 +00:00
|
|
|
switchwin_T switchwin;
|
2019-08-27 22:48:30 +02:00
|
|
|
int need_switch_win;
|
2022-07-25 12:28:09 +01:00
|
|
|
int do_change_curbuf = buf != NULL && htname == 'b';
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
++emsg_off;
|
|
|
|
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
|
|
rettv->vval.v_string = NULL;
|
|
|
|
|
2022-05-04 18:12:55 +01:00
|
|
|
if (varname != NULL && tp != NULL && win != NULL
|
|
|
|
&& (htname != 'b' || buf != NULL))
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
// Set curwin to be our win, temporarily. Also set the tabpage,
|
|
|
|
// otherwise the window is not valid. Only do this when needed,
|
|
|
|
// autocommands get blocked.
|
2022-05-04 18:12:55 +01:00
|
|
|
// If we have a buffer reference avoid the switching, we're saving and
|
|
|
|
// restoring curbuf directly.
|
2022-07-25 12:28:09 +01:00
|
|
|
need_switch_win = !(tp == curtab && win == curwin) && !do_change_curbuf;
|
2022-05-04 18:12:55 +01:00
|
|
|
if (!need_switch_win || switch_win(&switchwin, win, tp, TRUE) == OK)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-05-04 18:12:55 +01:00
|
|
|
// Handle options. There are no tab-local options.
|
|
|
|
if (*varname == '&' && htname != 't')
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2022-05-04 18:12:55 +01:00
|
|
|
buf_T *save_curbuf = curbuf;
|
|
|
|
|
|
|
|
// Change curbuf so the option is read from the correct buffer.
|
2022-07-25 12:28:09 +01:00
|
|
|
if (do_change_curbuf)
|
2022-05-04 18:12:55 +01:00
|
|
|
curbuf = buf;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
if (varname[1] == NUL)
|
|
|
|
{
|
2022-07-25 12:28:09 +01:00
|
|
|
// get all window-local or buffer-local options in a dict
|
2022-05-04 18:12:55 +01:00
|
|
|
dict_T *opts = get_winbuf_options(htname == 'b');
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
if (opts != NULL)
|
|
|
|
{
|
|
|
|
rettv_dict_set(rettv, opts);
|
|
|
|
done = TRUE;
|
|
|
|
}
|
|
|
|
}
|
2022-05-04 18:12:55 +01:00
|
|
|
else if (eval_option(&varname, rettv, TRUE) == OK)
|
|
|
|
// Local option
|
2019-08-27 22:48:30 +02:00
|
|
|
done = TRUE;
|
2022-05-04 18:12:55 +01:00
|
|
|
|
|
|
|
curbuf = save_curbuf;
|
|
|
|
}
|
|
|
|
else if (*varname == NUL)
|
|
|
|
{
|
|
|
|
// Empty string: return a dict with all the local variables.
|
|
|
|
if (htname == 'b')
|
|
|
|
v = &buf->b_bufvar;
|
|
|
|
else if (htname == 'w')
|
|
|
|
v = &win->w_winvar;
|
|
|
|
else
|
|
|
|
v = &tp->tp_winvar;
|
|
|
|
copy_tv(&v->di_tv, rettv);
|
|
|
|
done = TRUE;
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-05-04 18:12:55 +01:00
|
|
|
hashtab_T *ht;
|
|
|
|
|
|
|
|
if (htname == 'b')
|
|
|
|
ht = &buf->b_vars->dv_hashtab;
|
|
|
|
else if (htname == 'w')
|
|
|
|
ht = &win->w_vars->dv_hashtab;
|
|
|
|
else
|
|
|
|
ht = &tp->tp_vars->dv_hashtab;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
// Look up the variable.
|
2022-05-04 18:12:55 +01:00
|
|
|
v = find_var_in_ht(ht, htname, varname, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (v != NULL)
|
|
|
|
{
|
|
|
|
copy_tv(&v->di_tv, rettv);
|
|
|
|
done = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (need_switch_win)
|
|
|
|
// restore previous notion of curwin
|
2022-01-06 13:24:51 +00:00
|
|
|
restore_win(&switchwin, TRUE);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2022-05-04 18:12:55 +01:00
|
|
|
if (!done && deftv->v_type != VAR_UNKNOWN)
|
|
|
|
// use the default value
|
|
|
|
copy_tv(deftv, rettv);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
|
|
|
--emsg_off;
|
|
|
|
}
|
|
|
|
|
2022-05-04 18:12:55 +01:00
|
|
|
/*
|
|
|
|
* getwinvar() and gettabwinvar()
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
getwinvar(
|
|
|
|
typval_T *argvars,
|
|
|
|
typval_T *rettv,
|
|
|
|
int off) // 1 for gettabwinvar()
|
|
|
|
{
|
|
|
|
char_u *varname;
|
|
|
|
tabpage_T *tp;
|
|
|
|
win_T *win;
|
|
|
|
|
|
|
|
if (off == 1)
|
|
|
|
tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
|
|
|
|
else
|
|
|
|
tp = curtab;
|
|
|
|
win = find_win_by_nr(&argvars[off], tp);
|
|
|
|
varname = tv_get_string_chk(&argvars[off + 1]);
|
|
|
|
|
|
|
|
get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL);
|
|
|
|
}
|
|
|
|
|
2020-08-19 21:20:49 +02:00
|
|
|
/*
|
|
|
|
* Set option "varname" to the value of "varp" for the current buffer/window.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_option_from_tv(char_u *varname, typval_T *varp)
|
|
|
|
{
|
|
|
|
long numval = 0;
|
|
|
|
char_u *strval;
|
|
|
|
char_u nbuf[NUMBUFLEN];
|
|
|
|
int error = FALSE;
|
|
|
|
|
2023-06-14 16:39:54 +01:00
|
|
|
int opt_idx = findoption(varname);
|
|
|
|
if (opt_idx < 0)
|
|
|
|
{
|
|
|
|
semsg(_(e_unknown_option_str_2), varname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int opt_p_flags = get_option_flags(opt_idx);
|
|
|
|
|
2021-01-03 14:47:25 +01:00
|
|
|
if (varp->v_type == VAR_BOOL)
|
2021-01-03 15:55:10 +01:00
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
if (opt_p_flags & P_STRING)
|
2022-07-27 12:30:13 +01:00
|
|
|
{
|
|
|
|
emsg(_(e_string_required));
|
|
|
|
return;
|
|
|
|
}
|
2021-01-03 14:47:25 +01:00
|
|
|
numval = (long)varp->vval.v_number;
|
2021-01-03 15:55:10 +01:00
|
|
|
strval = (char_u *)"0"; // avoid using "false"
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-14 16:39:54 +01:00
|
|
|
if ((opt_p_flags & (P_NUM|P_BOOL))
|
|
|
|
&& (!in_vim9script() || varp->v_type != VAR_STRING))
|
2021-01-03 15:55:10 +01:00
|
|
|
numval = (long)tv_get_number_chk(varp, &error);
|
2023-06-14 16:39:54 +01:00
|
|
|
if (!error)
|
|
|
|
strval = tv_get_string_buf_chk(varp, nbuf);
|
2021-01-03 15:55:10 +01:00
|
|
|
}
|
2020-08-19 21:20:49 +02:00
|
|
|
if (!error && strval != NULL)
|
2022-04-15 13:53:33 +01:00
|
|
|
set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
|
2020-08-19 21:20:49 +02:00
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* "setwinvar()" and "settabwinvar()" functions
|
|
|
|
*/
|
|
|
|
static void
|
2020-01-04 16:13:49 +01:00
|
|
|
setwinvar(typval_T *argvars, int off)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
win_T *win;
|
2022-01-06 13:24:51 +00:00
|
|
|
switchwin_T switchwin;
|
2019-08-27 22:48:30 +02:00
|
|
|
int need_switch_win;
|
|
|
|
char_u *varname, *winvarname;
|
|
|
|
typval_T *varp;
|
|
|
|
tabpage_T *tp = NULL;
|
|
|
|
|
|
|
|
if (check_secure())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (off == 1)
|
|
|
|
tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
|
|
|
|
else
|
|
|
|
tp = curtab;
|
|
|
|
win = find_win_by_nr(&argvars[off], tp);
|
|
|
|
varname = tv_get_string_chk(&argvars[off + 1]);
|
|
|
|
varp = &argvars[off + 2];
|
|
|
|
|
2023-01-03 10:54:09 +00:00
|
|
|
if (win == NULL || varname == NULL)
|
2023-01-02 16:54:53 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
need_switch_win = !(tp == curtab && win == curwin);
|
|
|
|
if (!need_switch_win
|
|
|
|
|| switch_win(&switchwin, win, tp, TRUE) == OK)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
if (*varname == '&')
|
|
|
|
set_option_from_tv(varname + 1, varp);
|
|
|
|
else
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
winvarname = alloc(STRLEN(varname) + 3);
|
|
|
|
if (winvarname != NULL)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
STRCPY(winvarname, "w:");
|
|
|
|
STRCPY(winvarname + 2, varname);
|
|
|
|
set_var(winvarname, varp, TRUE);
|
|
|
|
vim_free(winvarname);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-02 16:54:53 +00:00
|
|
|
if (need_switch_win)
|
|
|
|
restore_win(&switchwin, TRUE);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 22:09:46 +02:00
|
|
|
/*
|
|
|
|
* reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal,
|
|
|
|
* v:option_type, and v:option_command.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
reset_v_option_vars(void)
|
|
|
|
{
|
|
|
|
set_vim_var_string(VV_OPTION_NEW, NULL, -1);
|
|
|
|
set_vim_var_string(VV_OPTION_OLD, NULL, -1);
|
|
|
|
set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1);
|
|
|
|
set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1);
|
|
|
|
set_vim_var_string(VV_OPTION_TYPE, NULL, -1);
|
|
|
|
set_vim_var_string(VV_OPTION_COMMAND, NULL, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add an assert error to v:errors.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
assert_error(garray_T *gap)
|
|
|
|
{
|
|
|
|
struct vimvar *vp = &vimvars[VV_ERRORS];
|
|
|
|
|
2021-12-24 21:36:12 +00:00
|
|
|
if (vp->vv_tv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL)
|
2019-08-30 15:46:30 +02:00
|
|
|
// Make sure v:errors is a list.
|
2019-08-29 22:09:46 +02:00
|
|
|
set_vim_var_list(VV_ERRORS, list_alloc());
|
|
|
|
list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, gap->ga_len);
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
int
|
|
|
|
var_exists(char_u *var)
|
|
|
|
{
|
2020-08-05 10:53:21 +02:00
|
|
|
char_u *arg = var;
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *name;
|
|
|
|
char_u *tofree;
|
|
|
|
typval_T tv;
|
|
|
|
int len = 0;
|
|
|
|
int n = FALSE;
|
|
|
|
|
|
|
|
// get_name_len() takes care of expanding curly braces
|
|
|
|
name = var;
|
2020-08-05 10:53:21 +02:00
|
|
|
len = get_name_len(&arg, &tofree, TRUE, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
if (tofree != NULL)
|
|
|
|
name = tofree;
|
2022-01-06 21:10:28 +00:00
|
|
|
n = (eval_variable(name, len, 0, &tv, NULL,
|
2021-03-13 20:57:19 +01:00
|
|
|
EVAL_VAR_NOAUTOLOAD + EVAL_VAR_IMPORT) == OK);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (n)
|
|
|
|
{
|
|
|
|
// handle d.key, l[idx], f(expr)
|
2020-08-05 10:53:21 +02:00
|
|
|
arg = skipwhite(arg);
|
2022-01-07 12:45:29 +00:00
|
|
|
n = (handle_subscript(&arg, name, &tv, &EVALARG_EVALUATE,
|
|
|
|
FALSE) == OK);
|
2019-08-27 22:48:30 +02:00
|
|
|
if (n)
|
|
|
|
clear_tv(&tv);
|
|
|
|
}
|
|
|
|
}
|
2020-08-05 10:53:21 +02:00
|
|
|
if (*arg != NUL)
|
2019-08-27 22:48:30 +02:00
|
|
|
n = FALSE;
|
|
|
|
|
|
|
|
vim_free(tofree);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
static lval_T *redir_lval = NULL;
|
|
|
|
#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval)
|
|
|
|
static garray_T redir_ga; // only valid when redir_lval is not NULL
|
|
|
|
static char_u *redir_endp = NULL;
|
|
|
|
static char_u *redir_varname = NULL;
|
|
|
|
|
2021-04-19 20:50:03 +02:00
|
|
|
int
|
|
|
|
alloc_redir_lval(void)
|
|
|
|
{
|
|
|
|
redir_lval = ALLOC_CLEAR_ONE(lval_T);
|
|
|
|
if (redir_lval == NULL)
|
|
|
|
return FAIL;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
clear_redir_lval(void)
|
|
|
|
{
|
|
|
|
VIM_CLEAR(redir_lval);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
init_redir_ga(void)
|
|
|
|
{
|
2022-01-08 16:19:22 +00:00
|
|
|
ga_init2(&redir_ga, sizeof(char), 500);
|
2021-04-19 20:50:03 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 16:01:30 +02:00
|
|
|
/*
|
|
|
|
* Start recording command output to a variable
|
|
|
|
* When "append" is TRUE append to an existing variable.
|
|
|
|
* Returns OK if successfully completed the setup. FAIL otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
var_redir_start(char_u *name, int append)
|
|
|
|
{
|
2020-07-11 22:14:59 +02:00
|
|
|
int called_emsg_before;
|
2019-09-01 16:01:30 +02:00
|
|
|
typval_T tv;
|
|
|
|
|
|
|
|
// Catch a bad name early.
|
|
|
|
if (!eval_isnamec1(*name))
|
|
|
|
{
|
2021-12-31 22:49:24 +00:00
|
|
|
emsg(_(e_invalid_argument));
|
2019-09-01 16:01:30 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a copy of the name, it is used in redir_lval until redir ends.
|
|
|
|
redir_varname = vim_strsave(name);
|
|
|
|
if (redir_varname == NULL)
|
|
|
|
return FAIL;
|
|
|
|
|
2021-04-19 20:50:03 +02:00
|
|
|
if (alloc_redir_lval() == FAIL)
|
2019-09-01 16:01:30 +02:00
|
|
|
{
|
|
|
|
var_redir_stop();
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The output is stored in growarray "redir_ga" until redirection ends.
|
2021-04-19 20:50:03 +02:00
|
|
|
init_redir_ga();
|
2019-09-01 16:01:30 +02:00
|
|
|
|
|
|
|
// Parse the variable name (can be a dict or list entry).
|
|
|
|
redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0,
|
|
|
|
FNE_CHECK_START);
|
|
|
|
if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL)
|
|
|
|
{
|
|
|
|
clear_lval(redir_lval);
|
|
|
|
if (redir_endp != NULL && *redir_endp != NUL)
|
|
|
|
// Trailing characters are present after the variable name
|
2022-01-01 15:58:22 +00:00
|
|
|
semsg(_(e_trailing_characters_str), redir_endp);
|
2019-09-01 16:01:30 +02:00
|
|
|
else
|
2021-12-31 22:49:24 +00:00
|
|
|
semsg(_(e_invalid_argument_str), name);
|
2019-09-01 16:01:30 +02:00
|
|
|
redir_endp = NULL; // don't store a value, only cleanup
|
|
|
|
var_redir_stop();
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we can write to the variable: set it to or append an empty
|
|
|
|
// string
|
2020-07-11 22:14:59 +02:00
|
|
|
called_emsg_before = called_emsg;
|
2019-09-01 16:01:30 +02:00
|
|
|
tv.v_type = VAR_STRING;
|
|
|
|
tv.vval.v_string = (char_u *)"";
|
|
|
|
if (append)
|
2020-10-23 18:02:32 +02:00
|
|
|
set_var_lval(redir_lval, redir_endp, &tv, TRUE,
|
2021-02-11 21:19:34 +01:00
|
|
|
ASSIGN_NO_DECL, (char_u *)".", 0);
|
2019-09-01 16:01:30 +02:00
|
|
|
else
|
2020-10-23 18:02:32 +02:00
|
|
|
set_var_lval(redir_lval, redir_endp, &tv, TRUE,
|
2021-02-11 21:19:34 +01:00
|
|
|
ASSIGN_NO_DECL, (char_u *)"=", 0);
|
2019-09-01 16:01:30 +02:00
|
|
|
clear_lval(redir_lval);
|
2020-07-11 22:14:59 +02:00
|
|
|
if (called_emsg > called_emsg_before)
|
2019-09-01 16:01:30 +02:00
|
|
|
{
|
|
|
|
redir_endp = NULL; // don't store a value, only cleanup
|
|
|
|
var_redir_stop();
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append "value[value_len]" to the variable set by var_redir_start().
|
|
|
|
* The actual appending is postponed until redirection ends, because the value
|
|
|
|
* appended may in fact be the string we write to, changing it may cause freed
|
|
|
|
* memory to be used:
|
|
|
|
* :redir => foo
|
|
|
|
* :let foo
|
|
|
|
* :redir END
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
var_redir_str(char_u *value, int value_len)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (redir_lval == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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_ga, len) == OK)
|
|
|
|
{
|
|
|
|
mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len);
|
|
|
|
redir_ga.ga_len += len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
var_redir_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop redirecting command output to a variable.
|
|
|
|
* Frees the allocated memory.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
var_redir_stop(void)
|
|
|
|
{
|
|
|
|
typval_T tv;
|
|
|
|
|
|
|
|
if (EVALCMD_BUSY)
|
|
|
|
{
|
|
|
|
redir_lval = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redir_lval != NULL)
|
|
|
|
{
|
|
|
|
// If there was no error: assign the text to the variable.
|
|
|
|
if (redir_endp != NULL)
|
|
|
|
{
|
|
|
|
ga_append(&redir_ga, NUL); // Append the trailing NUL.
|
|
|
|
tv.v_type = VAR_STRING;
|
|
|
|
tv.vval.v_string = redir_ga.ga_data;
|
|
|
|
// Call get_lval() again, if it's inside a Dict or List it may
|
|
|
|
// have changed.
|
|
|
|
redir_endp = get_lval(redir_varname, NULL, redir_lval,
|
|
|
|
FALSE, FALSE, 0, FNE_CHECK_START);
|
|
|
|
if (redir_endp != NULL && redir_lval->ll_name != NULL)
|
2020-01-26 15:56:19 +01:00
|
|
|
set_var_lval(redir_lval, redir_endp, &tv, FALSE, 0,
|
2021-02-11 21:19:34 +01:00
|
|
|
(char_u *)".", 0);
|
2019-09-01 16:01:30 +02:00
|
|
|
clear_lval(redir_lval);
|
|
|
|
}
|
|
|
|
|
|
|
|
// free the collected output
|
|
|
|
VIM_CLEAR(redir_ga.ga_data);
|
|
|
|
|
|
|
|
VIM_CLEAR(redir_lval);
|
|
|
|
}
|
|
|
|
VIM_CLEAR(redir_varname);
|
|
|
|
}
|
|
|
|
|
2021-04-19 20:50:03 +02:00
|
|
|
/*
|
|
|
|
* Get the collected redirected text and clear redir_ga.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_clear_redir_ga(void)
|
|
|
|
{
|
|
|
|
char_u *res;
|
|
|
|
|
|
|
|
ga_append(&redir_ga, NUL); // Append the trailing NUL.
|
|
|
|
res = redir_ga.ga_data;
|
|
|
|
redir_ga.ga_data = NULL;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* "gettabvar()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_gettabvar(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
char_u *varname;
|
2022-05-04 18:12:55 +01:00
|
|
|
tabpage_T *tp;
|
|
|
|
win_T *win = NULL;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
varname = tv_get_string_chk(&argvars[1]);
|
|
|
|
tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
|
2022-05-04 18:12:55 +01:00
|
|
|
if (tp != NULL)
|
|
|
|
win = tp == curtab || tp->tp_firstwin == NULL ? firstwin
|
|
|
|
: tp->tp_firstwin;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2022-05-04 18:12:55 +01:00
|
|
|
get_var_from(varname, rettv, &argvars[2], 't', tp, win, NULL);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "gettabwinvar()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_gettabwinvar(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_number_arg(argvars, 1) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 2) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
getwinvar(argvars, rettv, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "getwinvar()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_getwinvar(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
getwinvar(argvars, rettv, 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 15:46:30 +02:00
|
|
|
/*
|
|
|
|
* "getbufvar()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_getbufvar(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
char_u *varname;
|
2022-05-04 18:12:55 +01:00
|
|
|
buf_T *buf;
|
2019-08-30 15:46:30 +02:00
|
|
|
|
2021-07-24 16:16:15 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_buffer_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-30 15:46:30 +02:00
|
|
|
varname = tv_get_string_chk(&argvars[1]);
|
2020-09-01 23:16:32 +02:00
|
|
|
buf = tv_get_buf_from_arg(&argvars[0]);
|
2019-08-30 15:46:30 +02:00
|
|
|
|
2022-05-04 18:12:55 +01:00
|
|
|
get_var_from(varname, rettv, &argvars[2], 'b', curtab, curwin, buf);
|
2019-08-30 15:46:30 +02:00
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
/*
|
|
|
|
* "settabvar()" function
|
|
|
|
*/
|
|
|
|
void
|
2020-01-04 16:13:49 +01:00
|
|
|
f_settabvar(typval_T *argvars, typval_T *rettv UNUSED)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
|
|
|
tabpage_T *save_curtab;
|
|
|
|
tabpage_T *tp;
|
2024-02-12 22:50:26 +01:00
|
|
|
tabpage_T *save_lu_tp;
|
2019-08-27 22:48:30 +02:00
|
|
|
char_u *varname, *tabvarname;
|
|
|
|
typval_T *varp;
|
|
|
|
|
|
|
|
if (check_secure())
|
|
|
|
return;
|
|
|
|
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
|
|
|
|
varname = tv_get_string_chk(&argvars[1]);
|
|
|
|
varp = &argvars[2];
|
|
|
|
|
2023-01-03 10:54:09 +00:00
|
|
|
if (varname == NULL || tp == NULL)
|
2023-01-02 16:54:53 +00:00
|
|
|
return;
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
save_curtab = curtab;
|
2024-02-12 22:50:26 +01:00
|
|
|
save_lu_tp = lastused_tabpage;
|
2023-01-02 16:54:53 +00:00
|
|
|
goto_tabpage_tp(tp, FALSE, FALSE);
|
2019-08-27 22:48:30 +02:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
tabvarname = alloc(STRLEN(varname) + 3);
|
|
|
|
if (tabvarname != NULL)
|
|
|
|
{
|
|
|
|
STRCPY(tabvarname, "t:");
|
|
|
|
STRCPY(tabvarname + 2, varname);
|
|
|
|
set_var(tabvarname, varp, TRUE);
|
|
|
|
vim_free(tabvarname);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
2023-01-02 16:54:53 +00:00
|
|
|
|
2024-02-12 22:50:26 +01:00
|
|
|
// Restore current tabpage and last accessed tabpage.
|
2023-01-02 16:54:53 +00:00
|
|
|
if (valid_tabpage(save_curtab))
|
2024-02-12 22:50:26 +01:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
goto_tabpage_tp(save_curtab, FALSE, FALSE);
|
2024-02-12 22:50:26 +01:00
|
|
|
if (valid_tabpage(save_lu_tp))
|
|
|
|
lastused_tabpage = save_lu_tp;
|
|
|
|
}
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "settabwinvar()" function
|
|
|
|
*/
|
|
|
|
void
|
2020-01-04 16:13:49 +01:00
|
|
|
f_settabwinvar(typval_T *argvars, typval_T *rettv UNUSED)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_number_arg(argvars, 1) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 2) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2020-01-04 16:13:49 +01:00
|
|
|
setwinvar(argvars, 1);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "setwinvar()" function
|
|
|
|
*/
|
|
|
|
void
|
2020-01-04 16:13:49 +01:00
|
|
|
f_setwinvar(typval_T *argvars, typval_T *rettv UNUSED)
|
2019-08-27 22:48:30 +02:00
|
|
|
{
|
2021-07-20 17:51:51 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2020-01-04 16:13:49 +01:00
|
|
|
setwinvar(argvars, 0);
|
2019-08-27 22:48:30 +02:00
|
|
|
}
|
|
|
|
|
2019-08-30 15:46:30 +02:00
|
|
|
/*
|
|
|
|
* "setbufvar()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED)
|
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
char_u *varname, *bufvarname;
|
|
|
|
typval_T *varp;
|
|
|
|
|
|
|
|
if (check_secure())
|
|
|
|
return;
|
2021-07-24 16:16:15 +02:00
|
|
|
|
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_buffer_arg(argvars, 0) == FAIL
|
|
|
|
|| check_for_string_arg(argvars, 1) == FAIL))
|
|
|
|
return;
|
|
|
|
|
2019-08-30 15:46:30 +02:00
|
|
|
varname = tv_get_string_chk(&argvars[1]);
|
2020-09-01 23:16:32 +02:00
|
|
|
buf = tv_get_buf_from_arg(&argvars[0]);
|
2019-08-30 15:46:30 +02:00
|
|
|
varp = &argvars[2];
|
|
|
|
|
2023-01-03 10:54:09 +00:00
|
|
|
if (buf == NULL || varname == NULL)
|
2023-01-02 16:54:53 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (*varname == '&')
|
2019-08-30 15:46:30 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
aco_save_T aco;
|
2024-01-16 17:22:38 +01:00
|
|
|
// safe the current window position, it could
|
|
|
|
// change because of 'scrollbind' window-local
|
|
|
|
// options
|
|
|
|
linenr_T old_topline = curwin->w_topline;
|
2019-08-30 15:46:30 +02:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
// Set curbuf to be our buf, temporarily.
|
|
|
|
aucmd_prepbuf(&aco, buf);
|
|
|
|
if (curbuf == buf)
|
|
|
|
{
|
|
|
|
// Only when it worked to set "curbuf".
|
|
|
|
set_option_from_tv(varname + 1, varp);
|
2019-08-30 15:46:30 +02:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
// reset notion of buffer
|
|
|
|
aucmd_restbuf(&aco);
|
2019-08-30 15:46:30 +02:00
|
|
|
}
|
2024-01-16 17:22:38 +01:00
|
|
|
curwin->w_topline = old_topline;
|
2023-01-02 16:54:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bufvarname = alloc(STRLEN(varname) + 3);
|
|
|
|
if (bufvarname != NULL)
|
2019-08-30 15:46:30 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
buf_T *save_curbuf = curbuf;
|
|
|
|
|
|
|
|
curbuf = buf;
|
|
|
|
STRCPY(bufvarname, "b:");
|
|
|
|
STRCPY(bufvarname + 2, varname);
|
|
|
|
set_var(bufvarname, varp, TRUE);
|
|
|
|
vim_free(bufvarname);
|
|
|
|
curbuf = save_curbuf;
|
2019-08-30 15:46:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 22:33:28 +02:00
|
|
|
/*
|
|
|
|
* Get a callback from "arg". It can be a Funcref or a function name.
|
2022-12-02 15:58:38 +00:00
|
|
|
* When "arg" is zero "res.cb_name" is set to an empty string.
|
|
|
|
* If "res.cb_name" is allocated then "res.cb_free_name" is set to TRUE.
|
|
|
|
* "res.cb_name" is set to NULL for an invalid argument.
|
2019-09-05 22:33:28 +02:00
|
|
|
*/
|
|
|
|
callback_T
|
|
|
|
get_callback(typval_T *arg)
|
|
|
|
{
|
2020-03-07 16:59:25 +01:00
|
|
|
callback_T res;
|
|
|
|
int r = OK;
|
2019-09-05 22:33:28 +02:00
|
|
|
|
2022-12-02 15:58:38 +00:00
|
|
|
CLEAR_FIELD(res);
|
2019-09-05 22:33:28 +02:00
|
|
|
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL)
|
|
|
|
{
|
|
|
|
res.cb_partial = arg->vval.v_partial;
|
|
|
|
++res.cb_partial->pt_refcount;
|
|
|
|
res.cb_name = partial_name(res.cb_partial);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-07 16:59:25 +01:00
|
|
|
if (arg->v_type == VAR_STRING && arg->vval.v_string != NULL
|
2024-01-04 21:19:04 +01:00
|
|
|
&& SAFE_isdigit(*arg->vval.v_string))
|
2020-03-07 16:59:25 +01:00
|
|
|
r = FAIL;
|
|
|
|
else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
|
2019-09-05 22:33:28 +02:00
|
|
|
{
|
2022-12-02 15:58:38 +00:00
|
|
|
res.cb_name = arg->vval.v_string;
|
2021-12-24 20:47:38 +00:00
|
|
|
if (arg->v_type == VAR_STRING)
|
|
|
|
{
|
2022-12-02 15:58:38 +00:00
|
|
|
char_u *name = get_scriptlocal_funcname(arg->vval.v_string);
|
2021-12-24 20:47:38 +00:00
|
|
|
if (name != NULL)
|
|
|
|
{
|
2022-12-02 15:58:38 +00:00
|
|
|
res.cb_name = name;
|
|
|
|
res.cb_free_name = TRUE;
|
2021-12-24 20:47:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 22:33:28 +02:00
|
|
|
func_ref(res.cb_name);
|
|
|
|
}
|
|
|
|
else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
|
|
|
|
res.cb_name = (char_u *)"";
|
|
|
|
else
|
2020-03-07 16:59:25 +01:00
|
|
|
r = FAIL;
|
|
|
|
|
|
|
|
if (r == FAIL)
|
2019-09-05 22:33:28 +02:00
|
|
|
{
|
2022-01-05 20:24:39 +00:00
|
|
|
emsg(_(e_invalid_callback_argument));
|
2019-09-05 22:33:28 +02:00
|
|
|
res.cb_name = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy a callback into a typval_T.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
put_callback(callback_T *cb, typval_T *tv)
|
|
|
|
{
|
|
|
|
if (cb->cb_partial != NULL)
|
|
|
|
{
|
|
|
|
tv->v_type = VAR_PARTIAL;
|
|
|
|
tv->vval.v_partial = cb->cb_partial;
|
|
|
|
++tv->vval.v_partial->pt_refcount;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tv->v_type = VAR_FUNC;
|
|
|
|
tv->vval.v_string = vim_strsave(cb->cb_name);
|
|
|
|
func_ref(cb->cb_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a copy of "src" into "dest", allocating the function name if needed,
|
|
|
|
* without incrementing the refcount.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_callback(callback_T *dest, callback_T *src)
|
|
|
|
{
|
|
|
|
if (src->cb_partial == NULL)
|
|
|
|
{
|
|
|
|
// just a function name, make a copy
|
|
|
|
dest->cb_name = vim_strsave(src->cb_name);
|
|
|
|
dest->cb_free_name = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// cb_name is a pointer into cb_partial
|
|
|
|
dest->cb_name = src->cb_name;
|
|
|
|
dest->cb_free_name = FALSE;
|
|
|
|
}
|
|
|
|
dest->cb_partial = src->cb_partial;
|
|
|
|
}
|
|
|
|
|
2020-07-20 21:31:32 +02:00
|
|
|
/*
|
|
|
|
* Copy callback from "src" to "dest", incrementing the refcounts.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
copy_callback(callback_T *dest, callback_T *src)
|
|
|
|
{
|
|
|
|
dest->cb_partial = src->cb_partial;
|
|
|
|
if (dest->cb_partial != NULL)
|
|
|
|
{
|
|
|
|
dest->cb_name = src->cb_name;
|
|
|
|
dest->cb_free_name = FALSE;
|
|
|
|
++dest->cb_partial->pt_refcount;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest->cb_name = vim_strsave(src->cb_name);
|
|
|
|
dest->cb_free_name = TRUE;
|
|
|
|
func_ref(src->cb_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-21 13:29:56 +00:00
|
|
|
/*
|
|
|
|
* When a callback refers to an autoload import, change the function name to
|
|
|
|
* the "path#name" form. Uses the current script context.
|
|
|
|
* Only works when the name is allocated.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
expand_autload_callback(callback_T *cb)
|
|
|
|
{
|
2022-01-24 21:28:01 +00:00
|
|
|
char_u *name;
|
2022-01-21 13:29:56 +00:00
|
|
|
char_u *p;
|
|
|
|
imported_T *import;
|
|
|
|
|
2022-01-24 21:28:01 +00:00
|
|
|
if (!in_vim9script() || cb->cb_name == NULL
|
|
|
|
|| (!cb->cb_free_name
|
|
|
|
&& (cb->cb_partial == NULL || cb->cb_partial->pt_name == NULL)))
|
2022-01-21 13:29:56 +00:00
|
|
|
return;
|
2022-01-24 21:28:01 +00:00
|
|
|
if (cb->cb_partial != NULL)
|
|
|
|
name = cb->cb_partial->pt_name;
|
|
|
|
else
|
|
|
|
name = cb->cb_name;
|
|
|
|
p = vim_strchr(name, '.');
|
2022-01-21 13:29:56 +00:00
|
|
|
if (p == NULL)
|
|
|
|
return;
|
2023-01-02 16:54:53 +00:00
|
|
|
|
2022-02-13 21:51:08 +00:00
|
|
|
import = find_imported(name, p - name, FALSE);
|
2023-01-02 16:54:53 +00:00
|
|
|
if (import == NULL || !SCRIPT_ID_VALID(import->imp_sid))
|
|
|
|
return;
|
2022-01-21 13:29:56 +00:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
|
|
|
|
if (si->sn_autoload_prefix == NULL)
|
|
|
|
return;
|
2022-01-21 13:29:56 +00:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
char_u *newname = concat_str(si->sn_autoload_prefix, p + 1);
|
|
|
|
if (newname == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cb->cb_partial != NULL)
|
|
|
|
{
|
|
|
|
if (cb->cb_name == cb->cb_partial->pt_name)
|
|
|
|
cb->cb_name = newname;
|
|
|
|
vim_free(cb->cb_partial->pt_name);
|
|
|
|
cb->cb_partial->pt_name = newname;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vim_free(cb->cb_name);
|
|
|
|
cb->cb_name = newname;
|
2022-01-21 13:29:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 22:33:28 +02:00
|
|
|
/*
|
|
|
|
* Unref/free "callback" returned by get_callback() or set_callback().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
free_callback(callback_T *callback)
|
|
|
|
{
|
|
|
|
if (callback->cb_partial != NULL)
|
|
|
|
{
|
|
|
|
partial_unref(callback->cb_partial);
|
|
|
|
callback->cb_partial = NULL;
|
|
|
|
}
|
|
|
|
else if (callback->cb_name != NULL)
|
|
|
|
func_unref(callback->cb_name);
|
|
|
|
if (callback->cb_free_name)
|
|
|
|
{
|
|
|
|
vim_free(callback->cb_name);
|
|
|
|
callback->cb_free_name = FALSE;
|
|
|
|
}
|
|
|
|
callback->cb_name = NULL;
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:48:30 +02:00
|
|
|
#endif // FEAT_EVAL
|