To: vim_dev@googlegroups.com Subject: Patch 8.2.2209 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2209 Problem: Vim9: return type of => lambda not parsed. Solution: Parse and use the return type. Files: src/vim9compile.c, src/userfunc.c, src/vim9type.c, src/proto/vim9type.pro, src/vim9script.c, src/eval.c src/testdir/test_vim9_expr.vim *** ../vim-8.2.2208/src/vim9compile.c 2020-12-24 21:56:37.647479568 +0100 --- src/vim9compile.c 2020-12-25 12:33:08.507858416 +0100 *************** *** 4118,4128 **** // Recognize if (**arg == '<' && eval_isnamec1((*arg)[1])) { - int called_emsg_before = called_emsg; - ++*arg; ! want_type = parse_type(arg, cctx->ctx_type_list); ! if (called_emsg != called_emsg_before) return FAIL; if (**arg != '>') --- 4118,4126 ---- // Recognize if (**arg == '<' && eval_isnamec1((*arg)[1])) { ++*arg; ! want_type = parse_type(arg, cctx->ctx_type_list, TRUE); ! if (want_type == NULL) return FAIL; if (**arg != '>') *************** *** 4809,4815 **** * compile "return [expr]" */ static char_u * ! compile_return(char_u *arg, int set_return_type, cctx_T *cctx) { char_u *p = arg; garray_T *stack = &cctx->ctx_type_stack; --- 4807,4813 ---- * compile "return [expr]" */ static char_u * ! compile_return(char_u *arg, int check_return_type, cctx_T *cctx) { char_u *p = arg; garray_T *stack = &cctx->ctx_type_stack; *************** *** 4824,4831 **** if (cctx->ctx_skip != SKIP_YES) { stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! if (set_return_type) cctx->ctx_ufunc->uf_ret_type = stack_type; else { if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID --- 4822,4831 ---- if (cctx->ctx_skip != SKIP_YES) { stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! if (check_return_type && cctx->ctx_ufunc->uf_ret_type == NULL) ! { cctx->ctx_ufunc->uf_ret_type = stack_type; + } else { if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID *************** *** 4843,4849 **** } else { ! // "set_return_type" cannot be TRUE, only used for a lambda which // always has an argument. if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID && cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_UNKNOWN) --- 4843,4849 ---- } else { ! // "check_return_type" cannot be TRUE, only used for a lambda which // always has an argument. if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID && cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_UNKNOWN) *************** *** 5636,5642 **** goto theend; } p = skipwhite(var_end + 1); ! type = parse_type(&p, cctx->ctx_type_list); has_type = TRUE; } else if (lvar != NULL) --- 5636,5644 ---- goto theend; } p = skipwhite(var_end + 1); ! type = parse_type(&p, cctx->ctx_type_list, TRUE); ! if (type == NULL) ! goto theend; has_type = TRUE; } else if (lvar != NULL) *************** *** 7417,7431 **** * After ex_function() has collected all the function lines: parse and compile * the lines into instructions. * Adds the function to "def_functions". ! * When "set_return_type" is set then set ufunc->uf_ret_type to the type of the ! * return statement (used for lambda). * "outer_cctx" is set for a nested function. * This can be used recursively through compile_lambda(), which may reallocate * "def_functions". * Returns OK or FAIL. */ int ! compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) { char_u *line = NULL; char_u *p; --- 7419,7434 ---- * After ex_function() has collected all the function lines: parse and compile * the lines into instructions. * Adds the function to "def_functions". ! * When "check_return_type" is set then set ufunc->uf_ret_type to the type of ! * the return statement (used for lambda). When uf_ret_type is already set ! * then check that it matches. * "outer_cctx" is set for a nested function. * This can be used recursively through compile_lambda(), which may reallocate * "def_functions". * Returns OK or FAIL. */ int ! compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx) { char_u *line = NULL; char_u *p; *************** *** 7797,7803 **** goto erret; case CMD_return: ! line = compile_return(p, set_return_type, &cctx); cctx.ctx_had_return = TRUE; break; --- 7800,7806 ---- goto erret; case CMD_return: ! line = compile_return(p, check_return_type, &cctx); cctx.ctx_had_return = TRUE; break; *** ../vim-8.2.2208/src/userfunc.c 2020-12-24 16:05:54.339860228 +0100 --- src/userfunc.c 2020-12-25 12:09:31.763975556 +0100 *************** *** 349,355 **** // will get the type from the default value type = &t_unknown; else ! type = parse_type(&p, &fp->uf_type_list); if (type == NULL) return FAIL; fp->uf_arg_types[i] = type; --- 349,355 ---- // will get the type from the default value type = &t_unknown; else ! type = parse_type(&p, &fp->uf_type_list, TRUE); if (type == NULL) return FAIL; fp->uf_arg_types[i] = type; *************** *** 369,375 **** // todo: get type from default value fp->uf_va_type = &t_any; else ! fp->uf_va_type = parse_type(&p, &fp->uf_type_list); if (fp->uf_va_type == NULL) return FAIL; } --- 369,375 ---- // todo: get type from default value fp->uf_va_type = &t_any; else ! fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); if (fp->uf_va_type == NULL) return FAIL; } *************** *** 460,476 **** /* * Skip over "->" or "=>" after the arguments of a lambda. * Return NULL if no valid arrow found. */ static char_u * ! skip_arrow(char_u *start, int equal_arrow) { char_u *s = start; if (equal_arrow) { if (*s == ':') ! s = skip_type(skipwhite(s + 1), TRUE); s = skipwhite(s); if (*s != '=') return NULL; --- 460,481 ---- /* * Skip over "->" or "=>" after the arguments of a lambda. + * If ": type" is found make "ret_type" point to "type". * Return NULL if no valid arrow found. */ static char_u * ! skip_arrow(char_u *start, int equal_arrow, char_u **ret_type) { char_u *s = start; if (equal_arrow) { if (*s == ':') ! { ! s = skipwhite(s + 1); ! *ret_type = s; ! s = skip_type(s, TRUE); ! } s = skipwhite(s); if (*s != '=') return NULL; *************** *** 503,508 **** --- 508,514 ---- ufunc_T *fp = NULL; partial_T *pt = NULL; int varargs; + char_u *ret_type = NULL; int ret; char_u *s; char_u *start, *end; *************** *** 517,535 **** ga_init(&newargs); ga_init(&newlines); ! // First, check if this is a lambda expression. "->" or "=>" must exist. s = skipwhite(*arg + 1); ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, types_optional ? &argtypes : NULL, types_optional, NULL, NULL, TRUE, NULL, NULL); ! if (ret == FAIL || skip_arrow(s, equal_arrow) == NULL) { if (types_optional) ga_clear_strings(&argtypes); return NOTDONE; } ! // Parse the arguments again. if (evaluate) pnewargs = &newargs; else --- 523,542 ---- ga_init(&newargs); ga_init(&newlines); ! // First, check if this is really a lambda expression. "->" or "=>" must ! // be found after the arguments. s = skipwhite(*arg + 1); ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, types_optional ? &argtypes : NULL, types_optional, NULL, NULL, TRUE, NULL, NULL); ! if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type) == NULL) { if (types_optional) ga_clear_strings(&argtypes); return NOTDONE; } ! // Parse the arguments for real. if (evaluate) pnewargs = &newargs; else *************** *** 538,544 **** ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, &varargs, NULL, FALSE, NULL, NULL); ! if (ret == FAIL || (*arg = skip_arrow(*arg, equal_arrow)) == NULL) { if (types_optional) ga_clear_strings(&argtypes); --- 545,552 ---- ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, &varargs, NULL, FALSE, NULL, NULL); ! if (ret == FAIL ! || (*arg = skip_arrow(*arg, equal_arrow, &ret_type)) == NULL) { if (types_optional) ga_clear_strings(&argtypes); *************** *** 551,561 **** *arg = skipwhite_and_linebreak(*arg, evalarg); ! // Only recognize "{" as the start of a function body when followed by ! // white space, "{key: val}" is a dict. ! if (equal_arrow && **arg == '{' && IS_WHITE_OR_NUL((*arg)[1])) { // TODO: process the function body upto the "}". emsg("Lambda function body not supported yet"); goto errret; } --- 559,569 ---- *arg = skipwhite_and_linebreak(*arg, evalarg); ! // Recognize "{" as the start of a function body. ! if (equal_arrow && **arg == '{') { // TODO: process the function body upto the "}". + // Return type is required then. emsg("Lambda function body not supported yet"); goto errret; } *************** *** 619,627 **** hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; ga_init(&fp->uf_def_args); ! if (types_optional ! && parse_argument_types(fp, &argtypes, FALSE) == FAIL) ! goto errret; fp->uf_lines = newlines; if (current_funccal != NULL && eval_lavars) --- 627,644 ---- hash_add(&func_hashtab, UF2HIKEY(fp)); fp->uf_args = newargs; ga_init(&fp->uf_def_args); ! if (types_optional) ! { ! if (parse_argument_types(fp, &argtypes, FALSE) == FAIL) ! goto errret; ! if (ret_type != NULL) ! { ! fp->uf_ret_type = parse_type(&ret_type, ! &fp->uf_type_list, TRUE); ! if (fp->uf_ret_type == NULL) ! goto errret; ! } ! } fp->uf_lines = newlines; if (current_funccal != NULL && eval_lavars) *************** *** 3752,3758 **** else { p = ret_type; ! fp->uf_ret_type = parse_type(&p, &fp->uf_type_list); } SOURCING_LNUM = lnum_save; } --- 3769,3775 ---- else { p = ret_type; ! fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE); } SOURCING_LNUM = lnum_save; } *** ../vim-8.2.2208/src/vim9type.c 2020-11-16 20:08:32.395713947 +0100 --- src/vim9type.c 2020-12-25 12:09:26.867986864 +0100 *************** *** 596,623 **** * Returns NULL in case of failure. */ static type_T * ! parse_type_member(char_u **arg, type_T *type, garray_T *type_gap) { type_T *member_type; int prev_called_emsg = called_emsg; if (**arg != '<') { ! if (*skipwhite(*arg) == '<') ! semsg(_(e_no_white_space_allowed_before_str), "<"); ! else ! emsg(_(e_missing_type)); ! return type; } *arg = skipwhite(*arg + 1); ! member_type = parse_type(arg, type_gap); *arg = skipwhite(*arg); if (**arg != '>' && called_emsg == prev_called_emsg) { ! emsg(_(e_missing_gt_after_type)); ! return type; } ++*arg; --- 596,633 ---- * Returns NULL in case of failure. */ static type_T * ! parse_type_member( ! char_u **arg, ! type_T *type, ! garray_T *type_gap, ! int give_error) { type_T *member_type; int prev_called_emsg = called_emsg; if (**arg != '<') { ! if (give_error) ! { ! if (*skipwhite(*arg) == '<') ! semsg(_(e_no_white_space_allowed_before_str), "<"); ! else ! emsg(_(e_missing_type)); ! } ! return NULL; } *arg = skipwhite(*arg + 1); ! member_type = parse_type(arg, type_gap, give_error); ! if (member_type == NULL) ! return NULL; *arg = skipwhite(*arg); if (**arg != '>' && called_emsg == prev_called_emsg) { ! if (give_error) ! emsg(_(e_missing_gt_after_type)); ! return NULL; } ++*arg; *************** *** 628,637 **** /* * Parse a type at "arg" and advance over it. ! * Return &t_any for failure. */ type_T * ! parse_type(char_u **arg, garray_T *type_gap) { char_u *p = *arg; size_t len; --- 638,648 ---- /* * Parse a type at "arg" and advance over it. ! * When "give_error" is TRUE give error messages, otherwise be quiet. ! * Return NULL for failure. */ type_T * ! parse_type(char_u **arg, garray_T *type_gap, int give_error) { char_u *p = *arg; size_t len; *************** *** 673,679 **** if (len == 4 && STRNCMP(*arg, "dict", len) == 0) { *arg += len; ! return parse_type_member(arg, &t_dict_any, type_gap); } break; case 'f': --- 684,691 ---- if (len == 4 && STRNCMP(*arg, "dict", len) == 0) { *arg += len; ! return parse_type_member(arg, &t_dict_any, ! type_gap, give_error); } break; case 'f': *************** *** 683,690 **** *arg += len; return &t_float; #else ! emsg(_(e_this_vim_is_not_compiled_with_float_support)); ! return &t_any; #endif } if (len == 4 && STRNCMP(*arg, "func", len) == 0) --- 695,703 ---- *arg += len; return &t_float; #else ! if (give_error) ! emsg(_(e_this_vim_is_not_compiled_with_float_support)); ! return NULL; #endif } if (len == 4 && STRNCMP(*arg, "func", len) == 0) *************** *** 721,731 **** } else if (first_optional != -1) { ! emsg(_(e_mandatory_argument_after_optional_argument)); ! return &t_any; } ! arg_type[argcount++] = parse_type(&p, type_gap); // Nothing comes after "...{type}". if (flags & TTFLAG_VARARGS) --- 734,748 ---- } else if (first_optional != -1) { ! if (give_error) ! emsg(_(e_mandatory_argument_after_optional_argument)); ! return NULL; } ! type = parse_type(&p, type_gap, give_error); ! if (type == NULL) ! return NULL; ! arg_type[argcount++] = type; // Nothing comes after "...{type}". if (flags & TTFLAG_VARARGS) *************** *** 733,763 **** if (*p != ',' && *skipwhite(p) == ',') { ! semsg(_(e_no_white_space_allowed_before_str), ","); ! return &t_any; } if (*p == ',') { ++p; if (!VIM_ISWHITE(*p)) { ! semsg(_(e_white_space_required_after_str), ","); ! return &t_any; } } p = skipwhite(p); if (argcount == MAX_FUNC_ARGS) { ! emsg(_(e_too_many_argument_types)); ! return &t_any; } } p = skipwhite(p); if (*p != ')') { ! emsg(_(e_missing_close)); ! return &t_any; } *arg = p + 1; } --- 750,784 ---- if (*p != ',' && *skipwhite(p) == ',') { ! if (give_error) ! semsg(_(e_no_white_space_allowed_before_str), ","); ! return NULL; } if (*p == ',') { ++p; if (!VIM_ISWHITE(*p)) { ! if (give_error) ! semsg(_(e_white_space_required_after_str), ","); ! return NULL; } } p = skipwhite(p); if (argcount == MAX_FUNC_ARGS) { ! if (give_error) ! emsg(_(e_too_many_argument_types)); ! return NULL; } } p = skipwhite(p); if (*p != ')') { ! if (give_error) ! emsg(_(e_missing_close)); ! return NULL; } *arg = p + 1; } *************** *** 765,774 **** { // parse return type ++*arg; ! if (!VIM_ISWHITE(**arg)) semsg(_(e_white_space_required_after_str), ":"); *arg = skipwhite(*arg); ! ret_type = parse_type(arg, type_gap); } if (flags == 0 && first_optional == -1 && argcount <= 0) type = get_func_type(ret_type, argcount, type_gap); --- 786,797 ---- { // parse return type ++*arg; ! if (!VIM_ISWHITE(**arg) && give_error) semsg(_(e_white_space_required_after_str), ":"); *arg = skipwhite(*arg); ! ret_type = parse_type(arg, type_gap, give_error); ! if (ret_type == NULL) ! return NULL; } if (flags == 0 && first_optional == -1 && argcount <= 0) type = get_func_type(ret_type, argcount, type_gap); *************** *** 783,789 **** ? argcount : first_optional; if (func_type_add_arg_types(type, argcount, type_gap) == FAIL) ! return &t_any; mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); } --- 806,812 ---- ? argcount : first_optional; if (func_type_add_arg_types(type, argcount, type_gap) == FAIL) ! return NULL; mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); } *************** *** 802,808 **** if (len == 4 && STRNCMP(*arg, "list", len) == 0) { *arg += len; ! return parse_type_member(arg, &t_list_any, type_gap); } break; case 'n': --- 825,832 ---- if (len == 4 && STRNCMP(*arg, "list", len) == 0) { *arg += len; ! return parse_type_member(arg, &t_list_any, ! type_gap, give_error); } break; case 'n': *************** *** 828,835 **** break; } ! semsg(_(e_type_not_recognized_str), *arg); ! return &t_any; } /* --- 852,860 ---- break; } ! if (give_error) ! semsg(_(e_type_not_recognized_str), *arg); ! return NULL; } /* *************** *** 1016,1024 **** char * type_name(type_T *type, char **tofree) { ! char *name = vartype_name(type->tt_type); *tofree = NULL; if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) { char *member_free; --- 1041,1052 ---- char * type_name(type_T *type, char **tofree) { ! char *name; *tofree = NULL; + if (type == NULL) + return "[unknown]"; + name = vartype_name(type->tt_type); if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) { char *member_free; *** ../vim-8.2.2208/src/proto/vim9type.pro 2020-11-16 20:08:32.395713947 +0100 --- src/proto/vim9type.pro 2020-12-25 12:04:20.552586070 +0100 *************** *** 17,23 **** int check_type(type_T *expected, type_T *actual, int give_msg, int argidx); int check_arg_type(type_T *expected, type_T *actual, int argidx); char_u *skip_type(char_u *start, int optional); ! type_T *parse_type(char_u **arg, garray_T *type_gap); void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap); type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap); char *vartype_name(vartype_T type); --- 17,23 ---- int check_type(type_T *expected, type_T *actual, int give_msg, int argidx); int check_arg_type(type_T *expected, type_T *actual, int argidx); char_u *skip_type(char_u *start, int optional); ! type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error); void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap); type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap); char *vartype_name(vartype_T type); *** ../vim-8.2.2208/src/vim9script.c 2020-12-05 14:44:33.512669314 +0100 --- src/vim9script.c 2020-12-25 12:01:59.572766021 +0100 *************** *** 511,517 **** char_u *name; scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); type_T *type; - int called_emsg_before = called_emsg; typval_T init_tv; if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const) --- 511,516 ---- *************** *** 548,555 **** // parse type p = skipwhite(p + 1); ! type = parse_type(&p, &si->sn_type_list); ! if (called_emsg != called_emsg_before) { vim_free(name); return p; --- 547,554 ---- // parse type p = skipwhite(p + 1); ! type = parse_type(&p, &si->sn_type_list, TRUE); ! if (type == NULL) { vim_free(name); return p; *** ../vim-8.2.2208/src/eval.c 2020-12-24 17:15:49.909113037 +0100 --- src/eval.c 2020-12-25 11:53:57.617883182 +0100 *************** *** 868,874 **** char_u *tp = skipwhite(p + 1); // parse the type after the name ! lp->ll_type = parse_type(&tp, &si->sn_type_list); lp->ll_name_end = tp; } } --- 868,876 ---- char_u *tp = skipwhite(p + 1); // parse the type after the name ! lp->ll_type = parse_type(&tp, &si->sn_type_list, !quiet); ! if (lp->ll_type == NULL && !quiet) ! return NULL; lp->ll_name_end = tp; } } *** ../vim-8.2.2208/src/testdir/test_vim9_expr.vim 2020-12-24 15:13:35.850860411 +0100 --- src/testdir/test_vim9_expr.vim 2020-12-25 12:34:12.367693002 +0100 *************** *** 1951,1957 **** assert_equal([1, 3, 5], res) # Lambda returning a dict ! var Lmb = () => {key: 42} assert_equal({key: 42}, Lmb()) END CheckDefSuccess(lines) --- 1951,1957 ---- assert_equal([1, 3, 5], res) # Lambda returning a dict ! var Lmb = () => ({key: 42}) assert_equal({key: 42}, Lmb()) END CheckDefSuccess(lines) *************** *** 1960,1970 **** CheckDefFailure(["var Ref = (a)=> a + 1"], 'E1001:') CheckDefFailure(["var Ref = (a) =>a + 1"], 'E1001:') CheckDefFailure(["filter([1, 2], (k,v) => 1)"], 'E1069:', 1) # error is in first line of the lambda CheckDefFailure(["var L = (a) -> a + b"], 'E1001:', 1) ! # TODO: lambda after -> doesn't work yet # assert_equal('xxxyyy', 'xxx'->((a, b) => a .. b)('yyy')) # CheckDefExecFailure(["var s = 'asdf'->{a -> a}('x')"], --- 1960,1975 ---- CheckDefFailure(["var Ref = (a)=> a + 1"], 'E1001:') CheckDefFailure(["var Ref = (a) =>a + 1"], 'E1001:') + CheckDefSuccess(["var Ref: func(number): string = (a: number): string => 'x'"]) + CheckDefSuccess(["var Ref: func(number): any = (a: number): any => 'x'"]) + CheckDefFailure(["var Ref: func(number): number = (a: number): string => 'x'"], 'E1012:') + CheckDefFailure(["var Ref: func(number): string = (a: number): string => 99"], 'E1012:') + CheckDefFailure(["filter([1, 2], (k,v) => 1)"], 'E1069:', 1) # error is in first line of the lambda CheckDefFailure(["var L = (a) -> a + b"], 'E1001:', 1) ! # TODO: ->(lambda)() doesn't work yet # assert_equal('xxxyyy', 'xxx'->((a, b) => a .. b)('yyy')) # CheckDefExecFailure(["var s = 'asdf'->{a -> a}('x')"], *************** *** 1973,1981 **** # 'E1106: 2 arguments too many') # CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1) ! CheckDefSuccess(['var Fx = (a) => {k1: 0,', ' k2: 1}']) ! CheckDefFailure(['var Fx = (a) => {k1: 0', ' k2: 1}'], 'E722:', 2) ! CheckDefFailure(['var Fx = (a) => {k1: 0,', ' k2 1}'], 'E720:', 2) CheckDefSuccess(['var Fx = (a) => [0,', ' 1]']) CheckDefFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) --- 1978,1986 ---- # 'E1106: 2 arguments too many') # CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1) ! CheckDefSuccess(['var Fx = (a) => ({k1: 0,', ' k2: 1})']) ! CheckDefFailure(['var Fx = (a) => ({k1: 0', ' k2: 1})'], 'E722:', 2) ! CheckDefFailure(['var Fx = (a) => ({k1: 0,', ' k2 1})'], 'E720:', 2) CheckDefSuccess(['var Fx = (a) => [0,', ' 1]']) CheckDefFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) *** ../vim-8.2.2208/src/version.c 2020-12-24 21:56:37.647479568 +0100 --- src/version.c 2020-12-25 12:37:04.083228394 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2209, /**/ -- "Making it up? Why should I want to make anything up? Life's bad enough as it is without wanting to invent any more of it." -- Marvin, the Paranoid Android in Douglas Adams' "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///