Compare commits

...

13 Commits

Author SHA1 Message Date
kylo252
104b2ff4d0 patch 9.0.1082: some jsonc files are not recognized
Problem:    Some jsonc files are not recognized.
Solution:   Add patterns for jsonc and move some from json to jsonc.
            (closes #11711)
2022-12-19 20:42:49 +00:00
Bram Moolenaar
34820944ed patch 9.0.1081: using "->" with split lines does not always work
Problem:    Using "->" with split lines does not always work.
Solution:   Avoid trying to get another line. (closes #11723)
2022-12-19 20:28:38 +00:00
Bram Moolenaar
afa3f1cc72 patch 9.0.1080: the "kitty" terminfo entry is not widespread
Problem:    The "kitty" terminfo entry is not widespread, resulting in the
            kitty terminal not working properly.
Solution:   Go back to using "xterm-kitty" and avoid the problems it causes in
            another way.
2022-12-19 18:56:48 +00:00
zeertzjq
33e543038b patch 9.0.1079: leaking memory when defining a user command fails
Problem:    Leaking memory when defining a user command fails.
Solution:   Free "compl_arg" when needed. (closes #11726)
2022-12-19 16:49:27 +00:00
zeertzjq
07146ad1d3 patch 9.0.1078: with the +vartabs feature indent folding may use wrong 'ts'
Problem:    With the +vartabs feature indent folding may use wrong 'tabstop'.
Solution:   Use the "buf" argument instead of "curbuf".
2022-12-19 15:51:44 +00:00
Bram Moolenaar
4ce1f99a2d patch 9.0.1077: can add text property with negative ID before virtual text
Problem:    Can add text property with negative ID before virtual text
            property.
Solution:   Remember that a text property with a negative ID was used and give
            an appropriate error message. (closes #11725)
            Fix index computation.
2022-12-19 13:31:06 +00:00
Bram Moolenaar
8efdcee02e patch 9.0.1076: ASAN complains about NULL argument
Problem:    ASAN complains about NULL argument.
Solution:   Skip memmove() when there is nothing to move.
2022-12-19 12:18:09 +00:00
Bram Moolenaar
c336ae3ce6 patch 9.0.1075: build fails if compiler doesn't allow declaration after case
Problem:    build fails if the compiler doesn't allow for a declaration right
            after "case".
Solution:   Add a block.
2022-12-18 22:01:42 +00:00
Bram Moolenaar
d505d17885 patch 9.0.1074: class members are not supported yet
Problem:    Class members are not supported yet.
Solution:   Add initial support for class members.
2022-12-18 21:42:55 +00:00
Bram Moolenaar
731d00770d patch 9.0.1073: using "xterm-kitty" for 'term' causes problems
Problem:    Using "xterm-kitty" for 'term' causes problems.
Solution:   Remove the "xterm-" part when 'term' is set from $TERM.  Detect a
            few kitty-specific properties based on the version response
            instead of the terminal name.
2022-12-18 17:47:18 +00:00
zeertzjq
ba2d191932 patch 9.0.1072: screenpos() column result in fold may be too small
Problem:    screenpos() column result in fold may be too small.
Solution:   Add space of 'number', sign column, etc. (closes #11715)
2022-12-18 12:28:59 +00:00
dundargoc
b5328b46a7 patch 9.0.1071: Codecov action version is too specific
Problem:    Codecov action version is too specific.
Solution:   Only use "v3" to automatically use the latest stable version.
            (closes #11720)
2022-12-17 15:47:45 +00:00
Bram Moolenaar
b26461715b patch 9.0.1070: reading beyond array size
Problem:    Reading beyond array size.
Solution:   Only use name[0] and name[1], do not use "name" as a string.
2022-12-17 15:35:43 +00:00
31 changed files with 903 additions and 279 deletions

View File

@@ -254,7 +254,7 @@ jobs:
- name: Codecov
if: matrix.coverage
uses: codecov/codecov-action@v3.1.1
uses: codecov/codecov-action@v3
with:
flags: linux,${{ matrix.features }}-${{ matrix.compiler }}-${{ matrix.extra }}
@@ -613,7 +613,7 @@ jobs:
- name: Codecov
if: matrix.coverage
uses: codecov/codecov-action@v3.1.1
uses: codecov/codecov-action@v3
with:
directory: src
flags: windows,${{ matrix.toolchain }}-${{ matrix.arch }}-${{ matrix.features }}

View File

@@ -294,6 +294,31 @@ When Vim receives a response to the |t_RV| (request version) sequence and it
starts with CSI, it assumes that the terminal is in 8-bit mode and will
convert all key sequences to their 8-bit variants.
*xterm-kitty* *kitty-terminal*
The Kitty terminal is a special case. Mainly because it works different from
most other terminals, but also because, instead of trying the fit in and make
it behave like other terminals by default, it dictates how applications need
to work when using Kitty. This makes it very difficult for Vim to work in a
Kitty terminal. Some exceptions have been hard coded, but it is not at all
nice to have to make exceptions for one specific terminal.
One of the problems is that the value for $TERM is set to "xterm-kitty". For
Vim this is an indication that the terminal is xterm-compatible and the
builtin xterm termcap entries should be used. Many other terminals depend on
this. However, Kitty is not fully xterm compatible. The author suggested to
ignore the "xterm-" prefix and use the terminfo entry anyway, so that is what
happens now, the builtin xterm termcap entries are not used. However, the
t_RV is set, otherwise other things would not work, such as automatically
setting 'ttymouse' to "sgr".
It is not clear why kitty sets $TERM to "xterm-kitty", the terminal isn't
really xterm compatible. "kitty" would be more appropriate, but a terminfo
entry with that name is not widespread.
Note that using the kitty keyboard protocol is a separate feature, see
|kitty-keyboard-protocol|.
==============================================================================
2. Terminal options *terminal-options* *termcap-options* *E436*

View File

@@ -993,23 +993,24 @@ au BufNewFile,BufRead *.jgr setf jgraph
" Jovial
au BufNewFile,BufRead *.jov,*.j73,*.jovial setf jovial
" JSON
au BufNewFile,BufRead *.json,*.jsonp,*.webmanifest setf json
" JSON5
au BufNewFile,BufRead *.json5 setf json5
" JSON Patch (RFC 6902)
au BufNewFile,BufRead *.json-patch setf json
au BufNewFile,BufRead *.json-patch setf json
" Jupyter Notebook is also json
au BufNewFile,BufRead *.ipynb setf json
au BufNewFile,BufRead *.ipynb setf json
" Other files that look like json
au BufNewFile,BufRead .babelrc,.eslintrc,.prettierrc,.firebaserc setf json
au BufNewFile,BufRead .prettierrc,.firebaserc setf json
" JSONC
au BufNewFile,BufRead *.jsonc setf jsonc
" JSONC (JSON with comments)
au BufNewFile,BufRead *.jsonc,.babelrc,.eslintrc,.jsfmtrc setf jsonc
au BufNewFile,BufRead .jshintc,.hintrc,.swrc,[jt]sconfig*.json setf jsonc
" JSON
au BufNewFile,BufRead *.json,*.jsonp,*.webmanifest setf json
" Jsonnet
au BufNewFile,BufRead *.jsonnet,*.libsonnet setf jsonnet

View File

@@ -1181,7 +1181,7 @@ win_lbr_chartabsize(
? col == 0
: (s[0] == NUL || s[1] == NUL)
&& cts->cts_with_trailing)))
&& tp->tp_id - 1 < gap->ga_len)
&& -tp->tp_id - 1 < gap->ga_len)
{
char_u *p = ((char_u **)gap->ga_data)[-tp->tp_id - 1];

View File

@@ -3378,16 +3378,26 @@ EXTERN char e_cannot_get_object_member_type_from_initializer_str[]
INIT(= N_("E1329: Cannot get object member type from initializer: %s"));
EXTERN char e_invalid_type_for_object_member_str[]
INIT(= N_("E1330: Invalid type for object member: %s"));
EXTERN char e_public_must_be_followed_by_this[]
INIT(= N_("E1331: Public must be followed by \"this\""));
EXTERN char e_public_object_member_name_cannot_start_with_underscore_str[]
INIT(= N_("E1332: Public object member name cannot start with underscore: %s"));
EXTERN char e_cannot_access_private_object_member_str[]
INIT(= N_("E1333: Cannot access private object member: %s"));
EXTERN char e_public_must_be_followed_by_this_or_static[]
INIT(= N_("E1331: Public must be followed by \"this\" or \"static\""));
EXTERN char e_public_member_name_cannot_start_with_underscore_str[]
INIT(= N_("E1332: Public member name cannot start with underscore: %s"));
EXTERN char e_cannot_access_private_member_str[]
INIT(= N_("E1333: Cannot access private member: %s"));
EXTERN char e_object_member_not_found_str[]
INIT(= N_("E1334: Object member not found: %s"));
EXTERN char e_object_member_is_not_writable_str[]
INIT(= N_("E1335: Object member is not writable: %s"));
EXTERN char e_member_is_not_writable_str[]
INIT(= N_("E1335: Member is not writable: %s"));
#endif
EXTERN char e_internal_error_shortmess_too_long[]
INIT(= N_("E1336: Internal error: shortmess too long"));
#ifdef FEAT_EVAL
EXTERN char e_class_member_not_found_str[]
INIT(= N_("E1337: Class member not found: %s"));
EXTERN char e_member_not_found_on_class_str_str[]
INIT(= N_("E1338: Member not found on class \"%s\": %s"));
#endif
#ifdef FEAT_PROP_POPUP
EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
INIT(= N_("E1339: Cannot add a textprop with text after using a textprop with a negative id"));
#endif

View File

@@ -1193,19 +1193,21 @@ get_lval(
var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
{
if (*p == '.' && lp->ll_tv->v_type != VAR_DICT
&& lp->ll_tv->v_type != VAR_OBJECT
&& lp->ll_tv->v_type != VAR_CLASS)
vartype_T v_type = lp->ll_tv->v_type;
if (*p == '.' && v_type != VAR_DICT
&& v_type != VAR_OBJECT
&& v_type != VAR_CLASS)
{
if (!quiet)
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
return NULL;
}
if (lp->ll_tv->v_type != VAR_LIST
&& lp->ll_tv->v_type != VAR_DICT
&& lp->ll_tv->v_type != VAR_BLOB
&& lp->ll_tv->v_type != VAR_OBJECT
&& lp->ll_tv->v_type != VAR_CLASS)
if (v_type != VAR_LIST
&& v_type != VAR_DICT
&& v_type != VAR_BLOB
&& v_type != VAR_OBJECT
&& v_type != VAR_CLASS)
{
if (!quiet)
emsg(_(e_can_only_index_list_dictionary_or_blob));
@@ -1214,9 +1216,9 @@ get_lval(
// A NULL list/blob works like an empty list/blob, allocate one now.
int r = OK;
if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
r = rettv_list_alloc(lp->ll_tv);
else if (lp->ll_tv->v_type == VAR_BLOB
else if (v_type == VAR_BLOB
&& lp->ll_tv->vval.v_blob == NULL)
r = rettv_blob_alloc(lp->ll_tv);
if (r == FAIL)
@@ -1278,7 +1280,7 @@ get_lval(
// Optionally get the second index [ :expr].
if (*p == ':')
{
if (lp->ll_tv->v_type == VAR_DICT)
if (v_type == VAR_DICT)
{
if (!quiet)
emsg(_(e_cannot_slice_dictionary));
@@ -1334,7 +1336,7 @@ get_lval(
++p;
}
if (lp->ll_tv->v_type == VAR_DICT)
if (v_type == VAR_DICT)
{
if (len == -1)
{
@@ -1435,7 +1437,7 @@ get_lval(
clear_tv(&var1);
lp->ll_tv = &lp->ll_di->di_tv;
}
else if (lp->ll_tv->v_type == VAR_BLOB)
else if (v_type == VAR_BLOB)
{
long bloblen = blob_len(lp->ll_tv->vval.v_blob);
@@ -1466,7 +1468,7 @@ get_lval(
lp->ll_tv = NULL;
break;
}
else if (lp->ll_tv->v_type == VAR_LIST)
else if (v_type == VAR_LIST)
{
/*
* Get the number and item for the only or first index of the List.
@@ -1513,7 +1515,7 @@ get_lval(
}
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
{
class_T *cl = (lp->ll_tv->v_type == VAR_OBJECT
class_T *cl = (v_type == VAR_OBJECT
&& lp->ll_tv->vval.v_object != NULL)
? lp->ll_tv->vval.v_object->obj_class
: lp->ll_tv->vval.v_class;
@@ -1521,23 +1523,28 @@ get_lval(
if (cl != NULL)
{
lp->ll_valtype = NULL;
for (int i = 0; i < cl->class_obj_member_count; ++i)
int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
: cl->class_class_member_count;
ocmember_T *members = v_type == VAR_OBJECT
? cl->class_obj_members
: cl->class_class_members;
for (int i = 0; i < count; ++i)
{
objmember_T *om = cl->class_obj_members + i;
if (STRNCMP(om->om_name, key, p - key) == 0
&& om->om_name[p - key] == NUL)
ocmember_T *om = members + i;
if (STRNCMP(om->ocm_name, key, p - key) == 0
&& om->ocm_name[p - key] == NUL)
{
switch (om->om_access)
switch (om->ocm_access)
{
case ACCESS_PRIVATE:
semsg(_(e_cannot_access_private_object_member_str),
om->om_name);
semsg(_(e_cannot_access_private_member_str),
om->ocm_name);
return NULL;
case ACCESS_READ:
if (!(flags & GLV_READ_ONLY))
{
semsg(_(e_object_member_is_not_writable_str),
om->om_name);
semsg(_(e_member_is_not_writable_str),
om->ocm_name);
return NULL;
}
break;
@@ -1545,18 +1552,22 @@ get_lval(
break;
}
lp->ll_valtype = om->om_type;
lp->ll_valtype = om->ocm_type;
if (lp->ll_tv->v_type == VAR_OBJECT)
if (v_type == VAR_OBJECT)
lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
// TODO: what about a class?
else
lp->ll_tv = &cl->class_members_tv[i];
break;
}
}
if (lp->ll_valtype == NULL)
{
semsg(_(e_object_member_not_found_str), key);
if (v_type == VAR_OBJECT)
semsg(_(e_object_member_not_found_str), key);
else
semsg(_(e_class_member_not_found_str), key);
return NULL;
}
}
@@ -4537,11 +4548,19 @@ eval_method(
if (**arg != '(' && alias == NULL
&& (paren = vim_strchr(*arg, '(')) != NULL)
{
char_u *deref;
*arg = name;
// Truncate the name a the "(". Avoid trying to get another line
// by making "getline" NULL.
*paren = NUL;
deref = deref_function_name(arg, &tofree, evalarg, verbose);
char_u *(*getline)(int, void *, int, getline_opt_T) = NULL;
if (evalarg != NULL)
{
getline = evalarg->eval_getline;
evalarg->eval_getline = NULL;
}
char_u *deref = deref_function_name(arg, &tofree, evalarg, verbose);
if (deref == NULL)
{
*arg = name + len;
@@ -4552,7 +4571,10 @@ eval_method(
name = deref;
len = (long)STRLEN(name);
}
*paren = '(';
if (getline != NULL)
evalarg->eval_getline = getline;
}
if (ret == OK)
@@ -5936,8 +5958,8 @@ echo_string_core(
{
if (i > 0)
ga_concat(&ga, (char_u *)", ");
objmember_T *m = &cl->class_obj_members[i];
ga_concat(&ga, m->om_name);
ocmember_T *m = &cl->class_obj_members[i];
ga_concat(&ga, m->ocm_name);
ga_concat(&ga, (char_u *)": ");
char_u *tf = NULL;
ga_concat(&ga, echo_string_core(

View File

@@ -420,7 +420,7 @@ get_indent_buf(buf_T *buf, linenr_T lnum)
{
# ifdef FEAT_VARTABS
return get_indent_str_vtab(ml_get_buf(buf, lnum, FALSE),
(int)curbuf->b_p_ts, buf->b_p_vts_array, FALSE);
(int)buf->b_p_ts, buf->b_p_vts_array, FALSE);
# else
return get_indent_str(ml_get_buf(buf, lnum, FALSE), (int)buf->b_p_ts, FALSE);
# endif

View File

@@ -1416,7 +1416,6 @@ textpos2screenpos(
if (pos->lnum >= wp->w_topline && pos->lnum <= wp->w_botline)
{
colnr_T off;
colnr_T col;
int width;
linenr_T lnum = pos->lnum;
@@ -1432,11 +1431,12 @@ textpos2screenpos(
row += diff_check_fill(wp, lnum);
#endif
colnr_T off = win_col_off(wp);
#ifdef FEAT_FOLDING
if (is_folded)
{
row += W_WINROW(wp);
coloff = wp->w_wincol + 1;
coloff = wp->w_wincol + 1 + off;
}
else
#endif
@@ -1445,7 +1445,6 @@ textpos2screenpos(
// similar to what is done in validate_cursor_col()
col = scol;
off = win_col_off(wp);
col += off;
width = wp->w_width - off + win_col_off2(wp);

View File

@@ -2353,6 +2353,8 @@ mch_restore_title(int which)
/*
* Return TRUE if "name" looks like some xterm name.
* This matches "xterm.*", thus "xterm-256color", "xterm-kitty", etc.
* Do not consider "xterm-kitty" an xterm, it is not fully xterm compatible,
* using the "xterm-kitty" terminfo entry should work better.
* Seiichi Sato mentioned that "mlterm" works like xterm.
*/
int
@@ -2360,7 +2362,8 @@ vim_is_xterm(char_u *name)
{
if (name == NULL)
return FALSE;
return (STRNICMP(name, "xterm", 5) == 0
return ((STRNICMP(name, "xterm", 5) == 0
&& STRNICMP(name, "xterm-kitty", 11) != 0)
|| STRNICMP(name, "nxterm", 6) == 0
|| STRNICMP(name, "kterm", 5) == 0
|| STRNICMP(name, "mlterm", 6) == 0

View File

@@ -32,6 +32,7 @@ int generate_GETITEM(cctx_T *cctx, int index, int with_op);
int generate_SLICE(cctx_T *cctx, int count);
int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
int generate_CLASSMEMBER(cctx_T *cctx, int load, class_T *cl, int idx);
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_depth, int loop_idx, type_T *type);
@@ -74,7 +75,7 @@ int generate_RANGE(cctx_T *cctx, char_u *range);
int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
int generate_undo_cmdmods(cctx_T *cctx);
int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name);
int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int vimvaridx, type_T *type, char_u *name, lhs_T *lhs);
int inside_loop_scope(cctx_T *cctx);
int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl);
void may_generate_prof_end(cctx_T *cctx, int prof_lnum);

View File

@@ -1387,6 +1387,7 @@ typedef signed char int8_T;
typedef double float_T;
typedef struct typval_S typval_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
typedef struct partial_S partial_T;
@@ -1466,14 +1467,14 @@ typedef enum {
} omacc_T;
/*
* Entry for an object member variable.
* Entry for an object or class member variable.
*/
typedef struct {
char_u *om_name; // allocated
omacc_T om_access;
type_T *om_type;
char_u *om_init; // allocated
} objmember_T;
char_u *ocm_name; // allocated
omacc_T ocm_access;
type_T *ocm_type;
char_u *ocm_init; // allocated
} ocmember_T;
// "class_T": used for v_class of typval of VAR_CLASS
struct class_S
@@ -1481,14 +1482,25 @@ struct class_S
char_u *class_name; // allocated
int class_refcount;
int class_obj_member_count;
objmember_T *class_obj_members; // allocated
// class members: "static varname"
int class_class_member_count;
ocmember_T *class_class_members; // allocated
typval_T *class_members_tv; // allocated array of class member vals
// class methods: "static def SomeMethod()"
int class_class_method_count;
ufunc_T **class_class_methods; // allocated
// object members: "this.varname"
int class_obj_member_count;
ocmember_T *class_obj_members; // allocated
// object methods: "def SomeMethod()"
int class_obj_method_count;
ufunc_T **class_obj_methods; // allocated
garray_T class_type_list; // used for type pointers
type_T class_type;
type_T class_type; // type used for the class
type_T class_object_type; // same as class_type but VAR_OBJECT
};
@@ -1513,7 +1525,7 @@ struct object_S
/*
* Structure to hold an internal variable without a name.
*/
typedef struct
struct typval_S
{
vartype_T v_type;
char v_lock; // see below: VAR_LOCKED, VAR_FIXED
@@ -1534,7 +1546,7 @@ typedef struct
class_T *v_class; // class value (can be NULL)
object_T *v_object; // object value (can be NULL)
} vval;
} typval_T;
};
// Values for "dv_scope".
#define VAR_SCOPE 1 // a:, v:, s:, etc. scope dictionaries

View File

@@ -1675,6 +1675,12 @@ static char *(key_names[]) =
#endif
#ifdef HAVE_TGETENT
/*
* Get the termcap entries we need with tgetstr(), tgetflag() and tgetnum().
* "invoke_tgetent()" must have been called before.
* If "*height" or "*width" are not zero then use the "li" and "col" entries to
* get their value.
*/
static void
get_term_entries(int *height, int *width)
{
@@ -2033,14 +2039,6 @@ set_termname(char_u *term)
#endif
parse_builtin_tcap(term);
// Use the 'keyprotocol' option to adjust the t_TE and t_TI
// termcap entries if there is an entry maching "term".
keyprot_T kpc = match_keyprotocol(term);
if (kpc == KEYPROTOCOL_KITTY)
apply_builtin_tcap(term, builtin_kitty, TRUE);
else if (kpc == KEYPROTOCOL_MOK2)
apply_builtin_tcap(term, builtin_mok2, TRUE);
#ifdef FEAT_GUI
if (term_is_gui(term))
{
@@ -2060,6 +2058,19 @@ set_termname(char_u *term)
}
#endif
#ifdef FEAT_GUI
if (!gui.in_use)
#endif
{
// Use the 'keyprotocol' option to adjust the t_TE and t_TI
// termcap entries if there is an entry maching "term".
keyprot_T kpc = match_keyprotocol(term);
if (kpc == KEYPROTOCOL_KITTY)
apply_builtin_tcap(term, builtin_kitty, TRUE);
else if (kpc == KEYPROTOCOL_MOK2)
apply_builtin_tcap(term, builtin_mok2, TRUE);
}
/*
* special: There is no info in the termcap about whether the cursor
* positioning is relative to the start of the screen or to the start of the
@@ -2071,6 +2082,12 @@ set_termname(char_u *term)
else
T_CCS = empty_option;
// Special case: "kitty" does not normally have a "RV" entry in terminfo,
// but we need to request the version for several other things to work.
if (strstr((char *)term, "kitty") != NULL
&& (T_CRV == NULL || *T_CRV == NUL))
T_CRV = (char_u *)"\033[>c";
#ifdef UNIX
/*
* Any "stty" settings override the default for t_kb from the termcap.
@@ -2599,11 +2616,10 @@ tgoto(char *cm, int x, int y)
void
termcapinit(char_u *name)
{
char_u *term;
char_u *term = name;
if (name != NULL && *name == NUL)
name = NULL; // empty name is equal to no name
term = name;
if (term != NULL && *term == NUL)
term = NULL; // empty name is equal to no name
#ifndef MSWIN
if (term == NULL)
@@ -2617,9 +2633,7 @@ termcapinit(char_u *name)
set_string_default("term", term);
set_string_default("ttytype", term);
/*
* Avoid using "term" here, because the next mch_getenv() may overwrite it.
*/
// Avoid using "term" here, because the next mch_getenv() may overwrite it.
set_termname(T_NAME != NULL ? T_NAME : term);
}
@@ -4389,7 +4403,8 @@ clear_termcodes(void)
#define ATC_FROM_TERM 55
/*
* Add a new entry to the list of terminal codes.
* Add a new entry for "name[2]" to the list of terminal codes.
* Note that "name" may not have a terminating NUL.
* The list is kept alphabetical for ":set termcap"
* "flags" is TRUE when replacing 7-bit by 8-bit controls is desired.
* "flags" can also be ATC_FROM_TERM for got_code_from_term().
@@ -4497,7 +4512,8 @@ add_termcode(char_u *name, char_u *string, int flags)
{
// They are equal but for the ";*": don't add it.
#ifdef FEAT_EVAL
ch_log(NULL, "Termcap entry %s did not change", name);
ch_log(NULL, "Termcap entry %c%c did not change",
name[0], name[1]);
#endif
vim_free(s);
return;
@@ -4507,8 +4523,8 @@ add_termcode(char_u *name, char_u *string, int flags)
{
// Replace old code.
#ifdef FEAT_EVAL
ch_log(NULL, "Termcap entry %s was: %s",
name, termcodes[i].code);
ch_log(NULL, "Termcap entry %c%c was: %s",
name[0], name[1], termcodes[i].code);
#endif
vim_free(termcodes[i].code);
--tc_len;
@@ -4528,7 +4544,7 @@ add_termcode(char_u *name, char_u *string, int flags)
}
#ifdef FEAT_EVAL
ch_log(NULL, "%s termcap entry %s to %s", action, name, s);
ch_log(NULL, "%s termcap entry %c%c to %s", action, name[0], name[1], s);
#endif
termcodes[i].name[0] = name[0];
termcodes[i].name[1] = name[1];
@@ -4997,6 +5013,9 @@ handle_version_response(int first, int *arg, int argc, char_u *tp)
{
term_props[TPR_KITTY].tpr_status = TPR_YES;
term_props[TPR_KITTY].tpr_set_by_termresponse = TRUE;
// Kitty can handle SGR mouse reporting.
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
}
// GNU screen sends 83;30600;0, 83;40500;0, etc.

View File

@@ -0,0 +1,8 @@
>o+0&#ffffff0|n|e| @56
|t|w|o| @56
|t+0#ffffff16#ff404010|h|r|e@1| +0#0000000#ffffff0@54
|~+0#4040ff13&| @58
|~| @58
|~| @58
|~| @58
| +0#0000000&@41|1|,|1| @10|A|l@1|

View File

@@ -0,0 +1,8 @@
|~+0#4040ff13#ffffff0| @58
|~| @58
|~| @58
|E+0#ffffff16#e000002|r@1|o|r| |d|e|t|e|c|t|e|d| |w|h|i|l|e| |p|r|o|c|e|s@1|i|n|g| |f|u|n|c|t|i|o|n| |A|d@1|T|e|x|t|p|r|o|p|:| +0#0000000#ffffff0@6
|l+0#af5f00255&|i|n|e| @3|5|:| +0#0000000&@49
|E+0#ffffff16#e000002|1|3@1|9|:| |C|a|n@1|o|t| |a|d@1| |a| |t|e|x|t|p|r|o|p| |w|i|t|h| |t|e|x|t| |a|f|t|e|r| |u|s|i|n|g| |a| |t|e|x|t|p|r|o
|p| |w|i|t|h| |a| |n|e|g|a|t|i|v|e| |i|d| +0#0000000#ffffff0@39
|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@20

View File

@@ -150,8 +150,12 @@ func Test_screenpos_fold()
redraw
call assert_equal(2, screenpos(1, 2, 1).row)
call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 3, 1))
call assert_equal(3, screenpos(1, 4, 1).row)
call assert_equal(3, screenpos(1, 5, 1).row)
call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 4, 1))
call assert_equal(#{col: 1, row: 3, endcol: 1, curscol: 1}, screenpos(1, 5, 1))
setlocal number
call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 3, 1))
call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 4, 1))
call assert_equal(#{col: 5, row: 3, endcol: 5, curscol: 5}, screenpos(1, 5, 1))
call assert_equal(4, screenpos(1, 6, 1).row)
bwipe!
endfunc

View File

@@ -287,9 +287,9 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'],
\ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'json5': ['file.json5'],
\ 'jsonc': ['file.jsonc'],
\ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json'],
\ 'jsonnet': ['file.jsonnet', 'file.libsonnet'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],

View File

@@ -157,6 +157,27 @@ func Test_indent_fold_max()
bw!
endfunc
func Test_indent_fold_tabstop()
call setline(1, ['0', ' 1', ' 1', "\t2", "\t2"])
setlocal shiftwidth=4
setlocal foldcolumn=1
setlocal foldlevel=2
setlocal foldmethod=indent
redraw
call assert_equal('2 2', ScreenLines(5, 10)[0])
vsplit
windo diffthis
botright new
" This 'tabstop' value should not be used for folding in other buffers.
setlocal tabstop=4
diffoff!
redraw
call assert_equal('2 2', ScreenLines(5, 10)[0])
bwipe!
bwipe!
endfunc
func Test_manual_fold_with_filter()
CheckExecutable cat
for type in ['manual', 'marker']

View File

@@ -3725,6 +3725,50 @@ func Test_text_prop_diff_mode()
call StopVimInTerminal(buf)
endfunc
func Test_error_when_using_negative_id()
call prop_type_add('test1', #{highlight: 'ErrorMsg'})
call prop_add(1, 1, #{type: 'test1', text: 'virtual'})
call assert_fails("call prop_add(1, 1, #{type: 'test1', length: 1, id: -1})", 'E1293:')
call prop_type_delete('test1')
endfunc
func Test_error_after_using_negative_id()
" This needs to run a separate Vim instance because the
" "did_use_negative_pop_id" will be set.
CheckRunVimInTerminal
let lines =<< trim END
vim9script
setline(1, ['one', 'two', 'three'])
prop_type_add('test_1', {highlight: 'Error'})
prop_type_add('test_2', {highlight: 'WildMenu'})
prop_add(3, 1, {
type: 'test_1',
length: 5,
id: -1
})
def g:AddTextprop()
prop_add(1, 0, {
type: 'test_2',
text: 'The quick fox',
text_padding_left: 2
})
enddef
END
call writefile(lines, 'XtextPropError', 'D')
let buf = RunVimInTerminal('-S XtextPropError', #{rows: 8, cols: 60})
call VerifyScreenDump(buf, 'Test_prop_negative_error_1', {})
call term_sendkeys(buf, ":call AddTextprop()\<CR>")
call VerifyScreenDump(buf, 'Test_prop_negative_error_2', {})
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -179,6 +179,60 @@ func Test_user_method()
eval 'bar'->s:addFoo()->assert_equal('barfoo')
endfunc
func Test_method_with_linebreaks()
let lines =<< trim END
vim9script
export def Scan(ll: list<number>): func(func(number))
return (Emit: func(number)) => {
for v in ll
Emit(v)
endfor
}
enddef
export def Build(Cont: func(func(number))): list<number>
var result: list<number> = []
Cont((v) => {
add(result, v)
})
return result
enddef
export def Noop(Cont: func(func(number))): func(func(number))
return (Emit: func(number)) => {
Cont(Emit)
}
enddef
END
call writefile(lines, 'Xlib.vim', 'D')
let lines =<< trim END
vim9script
import "./Xlib.vim" as lib
const x = [1, 2, 3]
var result = lib.Scan(x)->lib.Noop()->lib.Build()
assert_equal([1, 2, 3], result)
result = lib.Scan(x)->lib.Noop()
->lib.Build()
assert_equal([1, 2, 3], result)
result = lib.Scan(x)
->lib.Noop()->lib.Build()
assert_equal([1, 2, 3], result)
result = lib.Scan(x)
->lib.Noop()
->lib.Build()
assert_equal([1, 2, 3], result)
END
call v9.CheckScriptSuccess(lines)
endfunc
func Test_failed_call_in_try()
try | call UnknownFunc() | catch | endtry
endfunc

View File

@@ -342,6 +342,11 @@ func Test_CmdErrors()
call assert_fails('com DoCmd :', 'E174:')
comclear
call assert_fails('delcom DoCmd', 'E184:')
" These used to leak memory
call assert_fails('com! -complete=custom,CustomComplete _ :', 'E182:')
call assert_fails('com! -complete=custom,CustomComplete docmd :', 'E183:')
call assert_fails('com! -complete=custom,CustomComplete -xxx DoCmd :', 'E181:')
endfunc
func CustomComplete(A, L, P)

View File

@@ -306,6 +306,30 @@ def Test_class_object_member_access()
assert_fails('trip.two = 22', 'E1335')
trip.three = 33
assert_equal(33, trip.three)
assert_fails('trip.four = 4', 'E1334')
END
v9.CheckScriptSuccess(lines)
enddef
def Test_class_member_access()
var lines =<< trim END
vim9script
class TextPos
this.lnum = 1
this.col = 1
static counter = 0
def AddToCounter(nr: number)
counter += nr
enddef
endclass
assert_equal(0, TextPos.counter)
TextPos.AddToCounter(3)
assert_equal(3, TextPos.counter)
assert_fails('TextPos.counter += 5', 'E1335')
END
v9.CheckScriptSuccess(lines)
enddef

View File

@@ -424,6 +424,10 @@ get_textprop_id(buf_T *buf)
return -(buf->b_textprop_text.ga_len + 1);
}
// Flag that is set when a negative ID isused for a normal text property.
// It is then impossible to use virtual text properties.
static int did_use_negative_pop_id = FALSE;
/*
* Shared between prop_add() and popup_create().
* "dict_arg" is the function argument of a dict containing "bufnr".
@@ -576,13 +580,25 @@ prop_add_common(
if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
goto theend;
if (id < 0 && buf->b_textprop_text.ga_len > 0)
if (id < 0)
{
emsg(_(e_cannot_use_negative_id_after_adding_textprop_with_text));
goto theend;
if (buf->b_textprop_text.ga_len > 0)
{
emsg(_(e_cannot_use_negative_id_after_adding_textprop_with_text));
goto theend;
}
did_use_negative_pop_id = TRUE;
}
if (text != NULL)
{
if (did_use_negative_pop_id)
{
emsg(_(e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id));
goto theend;
}
id = get_textprop_id(buf);
}
// This must be done _before_ we add the property because property changes
// trigger buffer (memline) reorganisation, which needs this flag to be

View File

@@ -1167,7 +1167,7 @@ ex_command(exarg_T *eap)
end = skiptowhite(p);
if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl,
&compl_arg, &addr_type_arg) == FAIL)
return;
goto theend;
p = skipwhite(end);
}
@@ -1179,7 +1179,7 @@ ex_command(exarg_T *eap)
if (!ends_excmd2(eap->arg, p) && !VIM_ISWHITE(*p))
{
emsg(_(e_invalid_command_name));
return;
goto theend;
}
end = p;
name_len = (int)(end - name);
@@ -1188,13 +1188,19 @@ ex_command(exarg_T *eap)
// we are listing commands
p = skipwhite(end);
if (!has_attr && ends_excmd2(eap->arg, p))
{
uc_list(name, end - name);
}
else if (!ASCII_ISUPPER(*name))
{
emsg(_(e_user_defined_commands_must_start_with_an_uppercase_letter));
}
else if ((name_len == 1 && *name == 'X')
|| (name_len <= 4
&& STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0))
{
emsg(_(e_reserved_name_cannot_be_used_for_user_defined_command));
}
else if (compl > 0 && (argt & EX_EXTRA) == 0)
{
// Some plugins rely on silently ignoring the mistake, only make this
@@ -1215,7 +1221,12 @@ ex_command(exarg_T *eap)
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
addr_type_arg, eap->forceit);
vim_free(tofree);
return; // success
}
theend:
vim_free(compl_arg);
}
/*

View File

@@ -695,6 +695,32 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1082,
/**/
1081,
/**/
1080,
/**/
1079,
/**/
1078,
/**/
1077,
/**/
1076,
/**/
1075,
/**/
1074,
/**/
1073,
/**/
1072,
/**/
1071,
/**/
1070,
/**/
1069,
/**/

View File

@@ -36,6 +36,8 @@ typedef enum {
ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number
ISN_STORE_THIS, // store value in "this" object member, index is
// isn_arg.number
ISN_LOAD_CLASSMEMBER, // load class member, using classmember_T
ISN_STORE_CLASSMEMBER, // store in class member, using classmember_T
// get and set variables
ISN_LOAD, // push local variable isn_arg.number
@@ -476,6 +478,12 @@ typedef struct {
class_T *construct_class; // class the object is created from
} construct_T;
// arguments to ISN_STORE_CLASSMEMBER and ISN_LOAD_CLASSMEMBER
typedef struct {
class_T *cm_class;
int cm_idx;
} classmember_T;
/*
* Instruction
*/
@@ -528,6 +536,7 @@ struct isn_S {
deferins_T defer;
echowin_T echowin;
construct_T construct;
classmember_T classmember;
} isn_arg;
};
@@ -538,7 +547,9 @@ struct dfunc_S {
ufunc_T *df_ufunc; // struct containing most stuff
int df_refcount; // how many ufunc_T point to this dfunc_T
int df_idx; // index in def_functions
int df_deleted; // if TRUE function was deleted
char df_deleted; // if TRUE function was deleted
char df_delete_busy; // TRUE when in
// delete_def_function_contents()
int df_script_seq; // Value of sctx_T sc_seq when the function
// was compiled.
char_u *df_name; // name used for error messages
@@ -735,6 +746,7 @@ typedef enum {
dest_window,
dest_tab,
dest_vimvar,
dest_class_member,
dest_script,
dest_reg,
dest_expr,
@@ -766,6 +778,10 @@ typedef struct {
lvar_T lhs_local_lvar; // used for existing local destination
lvar_T lhs_arg_lvar; // used for argument destination
lvar_T *lhs_lvar; // points to destination lvar
class_T *lhs_class; // for dest_class_member
int lhs_classmember_idx; // for dest_class_member
int lhs_scriptvar_sid;
int lhs_scriptvar_idx;

View File

@@ -21,6 +21,155 @@
# include "vim9.h"
#endif
/*
* Parse a member declaration, both object and class member.
* Returns OK or FAIL. When OK then "varname_end" is set to just after the
* variable name and "type_ret" is set to the decleared or detected type.
* "init_expr" is set to the initialisation expression (allocated), if there is
* one.
*/
static int
parse_member(
exarg_T *eap,
char_u *line,
char_u *varname,
int has_public, // TRUE if "public" seen before "varname"
char_u **varname_end,
garray_T *type_list,
type_T **type_ret,
char_u **init_expr)
{
*varname_end = to_name_end(varname, FALSE);
if (*varname == '_' && has_public)
{
semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
return FAIL;
}
char_u *colon = skipwhite(*varname_end);
char_u *type_arg = colon;
type_T *type = NULL;
if (*colon == ':')
{
if (VIM_ISWHITE(**varname_end))
{
semsg(_(e_no_white_space_allowed_before_colon_str), varname);
return FAIL;
}
if (!VIM_ISWHITE(colon[1]))
{
semsg(_(e_white_space_required_after_str_str), ":", varname);
return FAIL;
}
type_arg = skipwhite(colon + 1);
type = parse_type(&type_arg, type_list, TRUE);
if (type == NULL)
return FAIL;
}
char_u *expr_start = skipwhite(type_arg);
char_u *expr_end = expr_start;
if (type == NULL && *expr_start != '=')
{
emsg(_(e_type_or_initialization_required));
return FAIL;
}
if (*expr_start == '=')
{
if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
{
semsg(_(e_white_space_required_before_and_after_str_at_str),
"=", type_arg);
return FAIL;
}
expr_start = skipwhite(expr_start + 1);
expr_end = expr_start;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, FALSE);
skip_expr(&expr_end, NULL);
if (type == NULL)
{
// No type specified, use the type of the initializer.
typval_T tv;
tv.v_type = VAR_UNKNOWN;
char_u *expr = expr_start;
int res = eval0(expr, &tv, eap, &evalarg);
if (res == OK)
type = typval2type(&tv, get_copyID(), type_list,
TVTT_DO_MEMBER);
if (type == NULL)
{
semsg(_(e_cannot_get_object_member_type_from_initializer_str),
expr_start);
clear_evalarg(&evalarg, NULL);
return FAIL;
}
}
clear_evalarg(&evalarg, NULL);
}
if (!valid_declaration_type(type))
return FAIL;
*type_ret = type;
if (expr_end > expr_start)
*init_expr = vim_strnsave(expr_start, expr_end - expr_start);
return OK;
}
/*
* Add a member to an object or a class.
* Returns OK when successful, "init_expr" will be consumed then.
* Returns FAIL otherwise, caller might need to free "init_expr".
*/
static int
add_member(
garray_T *gap,
char_u *varname,
char_u *varname_end,
int has_public,
type_T *type,
char_u *init_expr)
{
if (ga_grow(gap, 1) == FAIL)
return FAIL;
ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
m->ocm_name = vim_strnsave(varname, varname_end - varname);
m->ocm_access = has_public ? ACCESS_ALL
: *varname == '_' ? ACCESS_PRIVATE : ACCESS_READ;
m->ocm_type = type;
if (init_expr != NULL)
m->ocm_init = init_expr;
++gap->ga_len;
return OK;
}
/*
* Move the class or object members found while parsing a class into the class.
* "gap" contains the found members.
* "members" will be set to the newly allocated array of members and
* "member_count" set to the number of members.
* Returns OK or FAIL.
*/
static int
add_members_to_class(
garray_T *gap,
ocmember_T **members,
int *member_count)
{
*member_count = gap->ga_len;
*members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len);
if (gap->ga_len > 0 && *members == NULL)
return FAIL;
if (gap->ga_len > 0)
mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
VIM_CLEAR(gap->ga_data);
return OK;
}
/*
* Handle ":class" and ":abstract class" up to ":endclass".
*/
@@ -64,16 +213,23 @@ ex_class(exarg_T *eap)
// extends SomeClass
// implements SomeInterface
// specifies SomeInterface
// check nothing follows
// TODO: handle "is_export" if it is set
// check that nothing follows
// handle "is_export" if it is set
garray_T type_list; // list of pointers to allocated types
ga_init2(&type_list, sizeof(type_T *), 10);
// Growarray with class members declared in the class.
garray_T classmembers;
ga_init2(&classmembers, sizeof(ocmember_T), 10);
// Growarray with object methods declared in the class.
garray_T classmethods;
ga_init2(&classmethods, sizeof(ufunc_T *), 10);
// Growarray with object members declared in the class.
garray_T objmembers;
ga_init2(&objmembers, sizeof(objmember_T), 10);
ga_init2(&objmembers, sizeof(ocmember_T), 10);
// Growarray with object methods declared in the class.
garray_T objmethods;
@@ -92,12 +248,6 @@ ex_class(exarg_T *eap)
break;
char_u *line = skipwhite(theline);
// TODO:
// class members (public, read access, private):
// static varname
// public static varname
// static _varname
char_u *p = line;
if (checkforcmd(&p, "endclass", 4))
{
@@ -110,9 +260,6 @@ ex_class(exarg_T *eap)
break;
}
// "this._varname"
// "this.varname"
// "public this.varname"
int has_public = FALSE;
if (checkforcmd(&p, "public", 3))
{
@@ -124,12 +271,17 @@ ex_class(exarg_T *eap)
has_public = TRUE;
p = skipwhite(line + 6);
if (STRNCMP(p, "this", 4) != 0)
if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
{
emsg(_(e_public_must_be_followed_by_this));
emsg(_(e_public_must_be_followed_by_this_or_static));
break;
}
}
// object members (public, read access, private):
// "this._varname"
// "this.varname"
// "public this.varname"
if (STRNCMP(p, "this", 4) == 0)
{
if (p[4] != '.' || !eval_isnamec1(p[5]))
@@ -138,95 +290,52 @@ ex_class(exarg_T *eap)
break;
}
char_u *varname = p + 5;
char_u *varname_end = to_name_end(varname, FALSE);
if (*varname == '_' && has_public)
{
semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line);
break;
}
char_u *colon = skipwhite(varname_end);
char_u *type_arg = colon;
char_u *varname_end = NULL;
type_T *type = NULL;
if (*colon == ':')
char_u *init_expr = NULL;
if (parse_member(eap, line, varname, has_public,
&varname_end, &type_list, &type, &init_expr) == FAIL)
break;
if (add_member(&objmembers, varname, varname_end,
has_public, type, init_expr) == FAIL)
{
if (VIM_ISWHITE(*varname_end))
{
semsg(_(e_no_white_space_allowed_before_colon_str),
varname);
break;
}
if (!VIM_ISWHITE(colon[1]))
{
semsg(_(e_white_space_required_after_str_str), ":",
varname);
break;
}
type_arg = skipwhite(colon + 1);
type = parse_type(&type_arg, &type_list, TRUE);
if (type == NULL)
break;
}
char_u *expr_start = skipwhite(type_arg);
char_u *expr_end = expr_start;
if (type == NULL && *expr_start != '=')
{
emsg(_(e_type_or_initialization_required));
vim_free(init_expr);
break;
}
}
if (*expr_start == '=')
// class members and methods
else if (checkforcmd(&p, "static", 6))
{
p = skipwhite(p);
if (checkforcmd(&p, "def", 3))
{
if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
// TODO: class method
// static def someMethod()
// enddef
// static def <Tval> someMethod()
// enddef
}
else
{
// class members (public, read access, private):
// "static _varname"
// "static varname"
// "public static varname"
char_u *varname = p;
char_u *varname_end = NULL;
type_T *type = NULL;
char_u *init_expr = NULL;
if (parse_member(eap, line, varname, has_public,
&varname_end, &type_list, &type, &init_expr) == FAIL)
break;
if (add_member(&classmembers, varname, varname_end,
has_public, type, init_expr) == FAIL)
{
semsg(_(e_white_space_required_before_and_after_str_at_str),
"=", type_arg);
vim_free(init_expr);
break;
}
expr_start = skipwhite(expr_start + 1);
expr_end = expr_start;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, FALSE);
skip_expr(&expr_end, NULL);
if (type == NULL)
{
// No type specified, use the type of the initializer.
typval_T tv;
tv.v_type = VAR_UNKNOWN;
char_u *expr = expr_start;
int res = eval0(expr, &tv, eap, &evalarg);
if (res == OK)
type = typval2type(&tv, get_copyID(), &type_list,
TVTT_DO_MEMBER);
if (type == NULL)
{
semsg(_(e_cannot_get_object_member_type_from_initializer_str),
expr_start);
clear_evalarg(&evalarg, NULL);
break;
}
}
clear_evalarg(&evalarg, NULL);
}
if (!valid_declaration_type(type))
break;
if (ga_grow(&objmembers, 1) == FAIL)
break;
objmember_T *m = ((objmember_T *)objmembers.ga_data)
+ objmembers.ga_len;
m->om_name = vim_strnsave(varname, varname_end - varname);
m->om_access = has_public ? ACCESS_ALL
: *varname == '_' ? ACCESS_PRIVATE
: ACCESS_READ;
m->om_type = type;
if (expr_end > expr_start)
m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
++objmembers.ga_len;
}
// constructors:
@@ -238,12 +347,8 @@ ex_class(exarg_T *eap)
// def someMethod()
// enddef
// TODO:
// static def someMethod()
// enddef
// def <Tval> someMethod()
// enddef
// static def <Tval> someMethod()
// enddef
else if (checkforcmd(&p, "def", 3))
{
exarg_T ea;
@@ -282,22 +387,52 @@ ex_class(exarg_T *eap)
class_T *cl = NULL;
if (success)
{
// "endclass" encountered without failures: Create the class.
cl = ALLOC_CLEAR_ONE(class_T);
if (cl == NULL)
goto cleanup;
cl->class_refcount = 1;
cl->class_name = vim_strnsave(arg, name_end - arg);
// Members are used by the new() function, add them here.
cl->class_obj_member_count = objmembers.ga_len;
cl->class_obj_members = objmembers.ga_len == 0 ? NULL
: ALLOC_MULT(objmember_T, objmembers.ga_len);
if (cl->class_name == NULL
|| (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
if (cl->class_name == NULL)
goto cleanup;
mch_memmove(cl->class_obj_members, objmembers.ga_data,
sizeof(objmember_T) * objmembers.ga_len);
vim_free(objmembers.ga_data);
// Add class and object members to "cl".
if (add_members_to_class(&classmembers,
&cl->class_class_members,
&cl->class_class_member_count) == FAIL
|| add_members_to_class(&objmembers,
&cl->class_obj_members,
&cl->class_obj_member_count) == FAIL)
goto cleanup;
if (cl->class_class_member_count > 0)
{
// Allocate a typval for each class member and initialize it.
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
cl->class_class_member_count);
if (cl->class_members_tv != NULL)
for (int i = 0; i < cl->class_class_member_count; ++i)
{
ocmember_T *m = &cl->class_class_members[i];
typval_T *tv = &cl->class_members_tv[i];
if (m->ocm_init != NULL)
{
typval_T *etv = eval_expr(m->ocm_init, eap);
if (etv != NULL)
{
*tv = *etv;
vim_free(etv);
}
}
else
{
// TODO: proper default value
tv->v_type = m->ocm_type->tt_type;
tv->vval.v_string = NULL;
}
}
}
int have_new = FALSE;
for (int i = 0; i < objmethods.ga_len; ++i)
@@ -318,8 +453,8 @@ ex_class(exarg_T *eap)
if (i > 0)
ga_concat(&fga, (char_u *)", ");
ga_concat(&fga, (char_u *)"this.");
objmember_T *m = cl->class_obj_members + i;
ga_concat(&fga, (char_u *)m->om_name);
ocmember_T *m = cl->class_obj_members + i;
ga_concat(&fga, (char_u *)m->ocm_name);
ga_concat(&fga, (char_u *)" = v:none");
}
ga_concat(&fga, (char_u *)")\nenddef\n");
@@ -355,6 +490,7 @@ ex_class(exarg_T *eap)
}
}
// TODO: class methods
cl->class_obj_method_count = objmethods.ga_len;
cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
if (cl->class_obj_methods == NULL)
@@ -378,13 +514,7 @@ ex_class(exarg_T *eap)
cl->class_type_list = type_list;
// TODO:
// - Add the methods to the class
// - array with ufunc_T pointers
// - Fill hashtab with object members and methods
// - Generate the default new() method, if needed.
// Later:
// - class members
// - class methods
// - Fill hashtab with object members and methods ?
// Add the class to the script-local variables.
typval_T tv;
@@ -404,13 +534,20 @@ cleanup:
vim_free(cl);
}
for (int i = 0; i < objmembers.ga_len; ++i)
for (int round = 1; round <= 2; ++round)
{
objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
vim_free(m->om_name);
vim_free(m->om_init);
garray_T *gap = round == 1 ? &classmembers : &objmembers;
if (gap->ga_len == 0 || gap->ga_data == NULL)
continue;
for (int i = 0; i < gap->ga_len; ++i)
{
ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
vim_free(m->ocm_name);
vim_free(m->ocm_init);
}
ga_clear(gap);
}
ga_clear(&objmembers);
for (int i = 0; i < objmethods.ga_len; ++i)
{
@@ -437,11 +574,11 @@ class_member_type(
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
objmember_T *m = cl->class_obj_members + i;
if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
ocmember_T *m = cl->class_obj_members + i;
if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
{
*member_idx = i;
return m->om_type;
return m->ocm_type;
}
}
return &t_any;
@@ -572,13 +709,12 @@ class_object_index(
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
objmember_T *m = &cl->class_obj_members[i];
if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
ocmember_T *m = &cl->class_obj_members[i];
if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
{
if (*name == '_')
{
semsg(_(e_cannot_access_private_object_member_str),
m->om_name);
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL;
}
@@ -597,7 +733,31 @@ class_object_index(
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
}
// TODO: class member
else if (rettv->v_type == VAR_CLASS)
{
// class member
for (int i = 0; i < cl->class_class_member_count; ++i)
{
ocmember_T *m = &cl->class_class_members[i];
if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
{
if (*name == '_')
{
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL;
}
typval_T *tv = &cl->class_members_tv[i];
copy_tv(tv, rettv);
class_unref(cl);
*arg = name_end;
return OK;
}
}
semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
}
return FAIL;
}
@@ -708,15 +868,29 @@ copy_class(typval_T *from, typval_T *to)
void
class_unref(class_T *cl)
{
if (cl != NULL && --cl->class_refcount <= 0)
if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
{
vim_free(cl->class_name);
// Freeing what the class contains may recursively come back here.
// Clear "class_name" first, if it is NULL the class does not need to
// be freed.
VIM_CLEAR(cl->class_name);
for (int i = 0; i < cl->class_class_member_count; ++i)
{
ocmember_T *m = &cl->class_class_members[i];
vim_free(m->ocm_name);
vim_free(m->ocm_init);
if (cl->class_members_tv != NULL)
clear_tv(&cl->class_members_tv[i]);
}
vim_free(cl->class_class_members);
vim_free(cl->class_members_tv);
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
objmember_T *m = &cl->class_obj_members[i];
vim_free(m->om_name);
vim_free(m->om_init);
ocmember_T *m = &cl->class_obj_members[i];
vim_free(m->ocm_name);
vim_free(m->ocm_init);
}
vim_free(cl->class_obj_members);

View File

@@ -1013,7 +1013,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
if (dest != dest_local)
{
if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
0, 0, type, name) == FAIL)
type, name, NULL) == FAIL)
goto failed;
}
else if (varlen == 1 && *arg == '_')

View File

@@ -301,6 +301,28 @@ script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack)
return FAIL;
}
/*
* If "name" is a class member in cctx->ctx_ufunc->uf_class return the index in
* class.class_class_members[].
* Otherwise return -1;
*/
static int
class_member_index(char_u *name, size_t len, cctx_T *cctx)
{
if (cctx == NULL || cctx->ctx_ufunc == NULL
|| cctx->ctx_ufunc->uf_class == NULL)
return -1;
class_T *cl = cctx->ctx_ufunc->uf_class;
for (int i = 0; i < cl->class_class_member_count; ++i)
{
ocmember_T *m = &cl->class_class_members[i];
if (STRNCMP(name, m->ocm_name, len) == 0
&& m->ocm_name[len] == NUL)
return i;
}
return -1;
}
/*
* Return TRUE if "name" is a local variable, argument, script variable or
* imported.
@@ -316,6 +338,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
&& (cctx->ctx_ufunc->uf_flags & FC_OBJECT)
&& STRNCMP(name, "this", 4) == 0)))
|| script_var_exists(name, len, cctx, NULL) == OK
|| class_member_index(name, len, cctx) >= 0
|| find_imported(name, len, FALSE) != NULL;
}
@@ -353,6 +376,9 @@ check_defined(
if (len == 1 && *p == '_')
return OK;
if (class_member_index(p, len, cctx) >= 0)
return OK;
if (script_var_exists(p, len, cctx, cstack) == OK)
{
if (is_arg)
@@ -1195,14 +1221,12 @@ assignment_len(char_u *p, int *heredoc)
* Generate the load instruction for "name".
*/
static void
generate_loadvar(
cctx_T *cctx,
assign_dest_T dest,
char_u *name,
lvar_T *lvar,
type_T *type)
generate_loadvar(cctx_T *cctx, lhs_T *lhs)
{
switch (dest)
char_u *name = lhs->lhs_name;
type_T *type = lhs->lhs_type;
switch (lhs->lhs_dest)
{
case dest_option:
case dest_func_option:
@@ -1245,6 +1269,7 @@ generate_loadvar(
case dest_local:
if (cctx->ctx_skip != SKIP_YES)
{
lvar_T *lvar = lhs->lhs_lvar;
if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
lvar->lv_loop_depth, lvar->lv_loop_idx, type);
@@ -1252,6 +1277,10 @@ generate_loadvar(
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
}
break;
case dest_class_member:
generate_CLASSMEMBER(cctx, TRUE, lhs->lhs_class,
lhs->lhs_classmember_idx);
break;
case dest_expr:
// list or dict value should already be on the stack.
break;
@@ -1533,7 +1562,9 @@ compile_lhs(
if (lookup_local(var_start, lhs->lhs_varlen,
&lhs->lhs_local_lvar, cctx) == OK)
{
lhs->lhs_lvar = &lhs->lhs_local_lvar;
}
else
{
CLEAR_FIELD(lhs->lhs_arg_lvar);
@@ -1549,6 +1580,7 @@ compile_lhs(
lhs->lhs_lvar = &lhs->lhs_arg_lvar;
}
}
if (lhs->lhs_lvar != NULL)
{
if (is_decl)
@@ -1557,6 +1589,12 @@ compile_lhs(
return FAIL;
}
}
else if ((lhs->lhs_classmember_idx = class_member_index(
var_start, lhs->lhs_varlen, cctx)) >= 0)
{
lhs->lhs_dest = dest_class_member;
lhs->lhs_class = cctx->ctx_ufunc->uf_class;
}
else
{
int script_namespace = lhs->lhs_varlen > 1
@@ -1965,8 +2003,7 @@ compile_load_lhs(
return FAIL;
}
else
generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name,
lhs->lhs_lvar, lhs->lhs_type);
generate_loadvar(cctx, lhs);
return OK;
}
@@ -2998,20 +3035,20 @@ compile_def_function(
for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
{
objmember_T *m = &ufunc->uf_class->class_obj_members[i];
if (m->om_init != NULL)
ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
if (m->ocm_init != NULL)
{
char_u *expr = m->om_init;
char_u *expr = m->ocm_init;
if (compile_expr0(&expr, &cctx) == FAIL)
goto erret;
if (!ends_excmd2(m->om_init, expr))
if (!ends_excmd2(m->ocm_init, expr))
{
semsg(_(e_trailing_characters_str), expr);
goto erret;
}
}
else
push_default_value(&cctx, m->om_type->tt_type,
push_default_value(&cctx, m->ocm_type->tt_type,
FALSE, NULL);
generate_STORE_THIS(&cctx, i);
}
@@ -3792,6 +3829,13 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
{
int idx;
// In same cases the instructions may refer to a class in which the
// function is defined and unreferencing the class may call back here
// recursively. Set the df_delete_busy to avoid problems.
if (dfunc->df_delete_busy)
return;
dfunc->df_delete_busy = TRUE;
ga_clear(&dfunc->df_def_args_isn);
ga_clear_strings(&dfunc->df_var_names);
@@ -3800,14 +3844,12 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
for (idx = 0; idx < dfunc->df_instr_count; ++idx)
delete_instr(dfunc->df_instr + idx);
VIM_CLEAR(dfunc->df_instr);
dfunc->df_instr = NULL;
}
if (dfunc->df_instr_debug != NULL)
{
for (idx = 0; idx < dfunc->df_instr_debug_count; ++idx)
delete_instr(dfunc->df_instr_debug + idx);
VIM_CLEAR(dfunc->df_instr_debug);
dfunc->df_instr_debug = NULL;
}
#ifdef FEAT_PROFILE
if (dfunc->df_instr_prof != NULL)
@@ -3815,7 +3857,6 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
for (idx = 0; idx < dfunc->df_instr_prof_count; ++idx)
delete_instr(dfunc->df_instr_prof + idx);
VIM_CLEAR(dfunc->df_instr_prof);
dfunc->df_instr_prof = NULL;
}
#endif
@@ -3823,6 +3864,8 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
dfunc->df_deleted = TRUE;
if (dfunc->df_ufunc != NULL)
dfunc->df_ufunc->uf_def_status = UF_NOT_COMPILED;
dfunc->df_delete_busy = FALSE;
}
/*

View File

@@ -3817,6 +3817,27 @@ exec_instructions(ectx_T *ectx)
goto on_error;
break;
case ISN_LOAD_CLASSMEMBER:
{
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend;
classmember_T *cm = &iptr->isn_arg.classmember;
*STACK_TV_BOT(0) =
cm->cm_class->class_members_tv[cm->cm_idx];
++ectx->ec_stack.ga_len;
}
break;
case ISN_STORE_CLASSMEMBER:
{
classmember_T *cm = &iptr->isn_arg.classmember;
tv = &cm->cm_class->class_members_tv[cm->cm_idx];
clear_tv(tv);
*tv = *STACK_TV_BOT(-1);
--ectx->ec_stack.ga_len;
}
break;
// Load or store variable or argument from outer scope.
case ISN_LOADOUTER:
case ISN_STOREOUTER:
@@ -6403,6 +6424,19 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
smsg("%s%4d STORERANGE", pfx, current);
break;
case ISN_LOAD_CLASSMEMBER:
case ISN_STORE_CLASSMEMBER:
{
class_T *cl = iptr->isn_arg.classmember.cm_class;
int idx = iptr->isn_arg.classmember.cm_idx;
ocmember_T *ocm = &cl->class_class_members[idx];
smsg("%s%4d %s CLASSMEMBER %s.%s", pfx, current,
iptr->isn_type == ISN_LOAD_CLASSMEMBER
? "LOAD" : "STORE",
cl->class_name, ocm->ocm_name);
}
break;
// constants
case ISN_PUSHNR:
smsg("%s%4d PUSHNR %lld", pfx, current,

View File

@@ -278,17 +278,16 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
objmember_T *m = &cl->class_obj_members[i];
if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
ocmember_T *m = &cl->class_obj_members[i];
if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
{
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
{
semsg(_(e_cannot_access_private_object_member_str),
m->om_name);
semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL;
}
generate_GET_OBJ_MEMBER(cctx, i, m->om_type);
generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
*arg = name_end;
return OK;

View File

@@ -956,6 +956,38 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
return OK;
}
/*
* Generate an ISN_LOAD_CLASSMEMBER ("load" == TRUE) or ISN_STORE_CLASSMEMBER
* ("load" == FALSE) instruction.
*/
int
generate_CLASSMEMBER(
cctx_T *cctx,
int load,
class_T *cl,
int idx)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if (load)
{
ocmember_T *m = &cl->class_class_members[idx];
isn = generate_instr_type(cctx, ISN_LOAD_CLASSMEMBER, m->ocm_type);
}
else
{
isn = generate_instr_drop(cctx, ISN_STORE_CLASSMEMBER, 1);
}
if (isn == NULL)
return FAIL;
isn->isn_arg.classmember.cm_class = cl;
++cl->class_refcount;
isn->isn_arg.classmember.cm_idx = idx;
return OK;
}
/*
* Generate an ISN_STOREOUTER instruction.
*/
@@ -2114,6 +2146,7 @@ generate_undo_cmdmods(cctx_T *cctx)
/*
* Generate a STORE instruction for "dest", not being "dest_local".
* "lhs" might be NULL.
* Return FAIL when out of memory.
*/
int
@@ -2122,10 +2155,9 @@ generate_store_var(
assign_dest_T dest,
int opt_flags,
int vimvaridx,
int scriptvar_idx,
int scriptvar_sid,
type_T *type,
char_u *name)
char_u *name,
lhs_T *lhs)
{
switch (dest)
{
@@ -2156,27 +2188,36 @@ generate_store_var(
case dest_vimvar:
return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
case dest_script:
if (scriptvar_idx < 0)
{
isntype_T isn_type = ISN_STORES;
if (SCRIPT_ID_VALID(scriptvar_sid)
&& SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
&& SCRIPT_ITEM(scriptvar_sid)->sn_autoload_prefix
== NULL)
int scriptvar_idx = lhs->lhs_scriptvar_idx;
int scriptvar_sid = lhs->lhs_scriptvar_sid;
if (scriptvar_idx < 0)
{
// "import autoload './dir/script.vim'" - load script first
if (generate_SOURCE(cctx, scriptvar_sid) == FAIL)
return FAIL;
isn_type = ISN_STOREEXPORT;
}
isntype_T isn_type = ISN_STORES;
// "s:" may be included in the name.
return generate_OLDSCRIPT(cctx, isn_type, name,
scriptvar_sid, type);
}
return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
if (SCRIPT_ID_VALID(scriptvar_sid)
&& SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
&& SCRIPT_ITEM(scriptvar_sid)->sn_autoload_prefix
== NULL)
{
// "import autoload './dir/script.vim'" - load script
// first
if (generate_SOURCE(cctx, scriptvar_sid) == FAIL)
return FAIL;
isn_type = ISN_STOREEXPORT;
}
// "s:" may be included in the name.
return generate_OLDSCRIPT(cctx, isn_type, name,
scriptvar_sid, type);
}
return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
scriptvar_sid, scriptvar_idx, type);
}
case dest_class_member:
return generate_CLASSMEMBER(cctx, FALSE,
lhs->lhs_class, lhs->lhs_classmember_idx);
case dest_local:
case dest_expr:
// cannot happen
@@ -2210,8 +2251,7 @@ generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
if (lhs->lhs_dest != dest_local)
return generate_store_var(cctx, lhs->lhs_dest,
lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
lhs->lhs_scriptvar_idx, lhs->lhs_scriptvar_sid,
lhs->lhs_type, lhs->lhs_name);
lhs->lhs_type, lhs->lhs_name, lhs);
if (lhs->lhs_lvar != NULL)
{
@@ -2422,6 +2462,11 @@ delete_instr(isn_T *isn)
vim_free(isn->isn_arg.script.scriptref);
break;
case ISN_LOAD_CLASSMEMBER:
case ISN_STORE_CLASSMEMBER:
class_unref(isn->isn_arg.classmember.cm_class);
break;
case ISN_TRY:
vim_free(isn->isn_arg.tryref.try_ref);
break;