diff options
Diffstat (limited to 'src')
57 files changed, 1890 insertions, 1065 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e4d7115654..3b2207f80b 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -29,7 +29,7 @@ set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua) set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) -set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c) +set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) @@ -56,6 +56,8 @@ set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua) set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua) +set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) +set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") @@ -305,11 +307,11 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} add_custom_command( OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} - ${API_METADATA} ${MSGPACK_LUA_C_BINDINGS} + ${API_METADATA} ${LUA_API_C_BINDINGS} COMMAND ${LUA_PRG} ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${API_METADATA} - ${MSGPACK_LUA_C_BINDINGS} + ${LUA_API_C_BINDINGS} ${API_HEADERS} DEPENDS ${API_HEADERS} @@ -325,15 +327,19 @@ add_custom_command( ${LUA_VIM_MODULE_SOURCE} vim_module ${LUA_SHARED_MODULE_SOURCE} shared_module ${LUA_INSPECT_MODULE_SOURCE} inspect_module + ${LUA_F_MODULE_SOURCE} lua_F_module + ${LUA_META_MODULE_SOURCE} lua_meta_module DEPENDS ${CHAR_BLOB_GENERATOR} ${LUA_VIM_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} + ${LUA_F_MODULE_SOURCE} + ${LUA_META_MODULE_SOURCE} ) list(APPEND NVIM_GENERATED_SOURCES - "${MSGPACK_LUA_C_BINDINGS}" + "${LUA_API_C_BINDINGS}" ) add_custom_command( diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index eedcfd69b8..0ed5e6408b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -37,7 +37,7 @@ /// Helper structure for vim_to_object typedef struct { - kvec_t(Object) stack; ///< Object stack. + kvec_withinit_t(Object, 2) stack; ///< Object stack. } EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -418,28 +418,25 @@ void set_option_to(uint64_t channel_id, void *to, int type, #define TYPVAL_ENCODE_ALLOW_SPECIALS false #define TYPVAL_ENCODE_CONV_NIL(tv) \ - kv_push(edata->stack, NIL) + kvi_push(edata->stack, NIL) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) + kvi_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - kv_push(edata->stack, INTEGER_OBJ((Integer)(num))) + kvi_push(edata->stack, INTEGER_OBJ((Integer)(num))) #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - kv_push(edata->stack, FLOAT_OBJ((Float)(flt))) + kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ const size_t len_ = (size_t)(len); \ const char *const str_ = (const char *)(str); \ assert(len_ == 0 || str_ != NULL); \ - kv_push(edata->stack, STRING_OBJ(((String) { \ - .data = xmemdupz((len_?str_:""), len_), \ - .size = len_ \ - }))); \ + kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_?str_:""), len_))); \ } while (0) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -458,17 +455,17 @@ void set_option_to(uint64_t channel_id, void *to, int type, #define TYPVAL_ENCODE_CONV_FUNC_END(tv) #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) + kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - kv_push(edata->stack, \ - DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) + kvi_push(edata->stack, \ + DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kv_push(edata->stack, ARRAY_OBJ(((Array) { + kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = len, .size = 0, .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)), @@ -510,7 +507,7 @@ static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { + kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { .capacity = len, .size = 0, .items = xmalloc(len * sizeof( @@ -618,14 +615,15 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { - EncodedData edata = { .stack = KV_INITIAL_VALUE }; + EncodedData edata; + kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument"); (void)evo_ret; assert(evo_ret == OK); Object ret = kv_A(edata.stack, 0); assert(kv_size(edata.stack) == 1); - kv_destroy(edata.stack); + kvi_destroy(edata.stack); return ret; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 90c43a1b04..5f48a26a29 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -608,12 +608,15 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) recursive++; try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.selfdict = self; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (see above) to capture abort-causing non-exception errors. (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, - vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, self); + vim_args, &funcexe); if (!try_end(err)) { rv = vim_to_object(&rettv); } @@ -2054,27 +2057,28 @@ void nvim_set_client_info(uint64_t channel_id, String name, rpc_set_client_info(channel_id, info); } -/// Get information about a channel. +/// Gets information about a channel. /// /// @returns Dictionary describing a channel, with these keys: -/// - "stream" the stream underlying the channel +/// - "id" Channel id. +/// - "argv" (optional) Job arguments list. +/// - "stream" Stream underlying the channel. /// - "stdio" stdin and stdout of this Nvim instance /// - "stderr" stderr of this Nvim instance /// - "socket" TCP/IP socket or named pipe -/// - "job" job with communication over its stdio -/// - "mode" how data received on the channel is interpreted -/// - "bytes" send and receive raw bytes -/// - "terminal" a |terminal| instance interprets ASCII sequences -/// - "rpc" |RPC| communication on the channel is active -/// - "pty" Name of pseudoterminal, if one is used (optional). -/// On a POSIX system, this will be a device path like -/// /dev/pts/1. Even if the name is unknown, the key will -/// still be present to indicate a pty is used. This is -/// currently the case when using winpty on windows. -/// - "buffer" buffer with connected |terminal| instance (optional) -/// - "client" information about the client on the other end of the -/// RPC channel, if it has added it using -/// |nvim_set_client_info()|. (optional) +/// - "job" Job with communication over its stdio. +/// - "mode" How data received on the channel is interpreted. +/// - "bytes" Send and receive raw bytes. +/// - "terminal" |terminal| instance interprets ASCII sequences. +/// - "rpc" |RPC| communication on the channel is active. +/// - "pty" (optional) Name of pseudoterminal. On a POSIX system this +/// is a device path like "/dev/pts/1". If the name is unknown, +/// the key will still be present if a pty is used (e.g. for +/// winpty on Windows). +/// - "buffer" (optional) Buffer with connected |terminal| instance. +/// - "client" (optional) Info about the peer (client on the other end of +/// the RPC channel), if provided by it via +/// |nvim_set_client_info()|. /// Dictionary nvim_get_chan_info(Integer chan, Error *err) FUNC_API_SINCE(4) diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index 32c77fa288..802fc9de57 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -8,6 +8,7 @@ #include "nvim/ui.h" #include "nvim/aucmd.h" #include "nvim/eval.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/buffer.h" @@ -35,6 +36,29 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) recursive = false; } +void init_default_autocmds(void) +{ + // open terminals when opening files that start with term:// +#define PROTO "term://" + do_cmdline_cmd("augroup nvim_terminal"); + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* ++nested " + "if !exists('b:term_title')|call termopen(" + // Capture the command string + "matchstr(expand(\"<amatch>\"), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + // capture the working directory + "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), " + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" + "|endif"); + do_cmdline_cmd("augroup END"); +#undef PROTO + + // limit syntax synchronization in the command window + do_cmdline_cmd("augroup nvim_cmdwin"); + do_cmdline_cmd("autocmd! CmdwinEnter [:>] syntax sync minlines=1 maxlines=1"); + do_cmdline_cmd("augroup END"); +} + static void focusgained_event(void **argv) { bool *gainedp = argv[0]; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index fdb3ffdc7e..81f8b9073e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1218,8 +1218,8 @@ do_buffer( return FAIL; } - if (!forceit && (buf->terminal || bufIsChanged(buf))) { - if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) { + if (!forceit && bufIsChanged(buf)) { + if ((p_confirm || cmdmod.confirm) && p_write) { dialog_changed(buf, false); if (!bufref_valid(&bufref)) { // Autocommand deleted buffer, oops! It's not changed now. @@ -1231,22 +1231,22 @@ do_buffer( return FAIL; } } else { - if (buf->terminal) { - if (p_confirm || cmdmod.confirm) { - if (!dialog_close_terminal(buf)) { - return FAIL; - } - } else { - EMSG2(_("E89: %s will be killed (add ! to override)"), - (char *)buf->b_fname); - return FAIL; - } - } else { - EMSGN(_("E89: No write since last change for buffer %" PRId64 - " (add ! to override)"), - buf->b_fnum); + EMSGN(_("E89: No write since last change for buffer %" PRId64 + " (add ! to override)"), + buf->b_fnum); + return FAIL; + } + } + + if (!forceit && buf->terminal && terminal_running(buf->terminal)) { + if (p_confirm || cmdmod.confirm) { + if (!dialog_close_terminal(buf)) { return FAIL; } + } else { + EMSG2(_("E89: %s will be killed (add ! to override)"), + (char *)buf->b_fname); + return FAIL; } } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 6f12d2eab8..94db7fb3b9 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -240,6 +240,10 @@ static void free_channel_event(void **argv) rpc_free(chan); } + if (chan->streamtype == kChannelStreamProc) { + process_free(&chan->stream.proc); + } + callback_reader_free(&chan->on_data); callback_reader_free(&chan->on_stderr); callback_free(&chan->on_exit); @@ -847,13 +851,24 @@ Dictionary channel_info(uint64_t id) const char *stream_desc, *mode_desc; switch (chan->streamtype) { - case kChannelStreamProc: + case kChannelStreamProc: { stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); } + + char **p = chan->stream.proc.argv; + Array argv = ARRAY_DICT_INIT; + if (p != NULL) { + while (*p != NULL) { + ADD(argv, STRING_OBJ(cstr_to_string(*p))); + p++; + } + } + PUT(info, "argv", ARRAY_OBJ(argv)); break; + } case kChannelStreamStdio: stream_desc = "stdio"; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5e18a77b6d..5603fbb082 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -65,6 +65,8 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); +static char *e_nowhitespace + = N_("E274: No white space allowed before parenthesis"); static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); @@ -736,15 +738,15 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; if (expr->v_type == VAR_FUNC) { const char_u *const s = expr->vval.v_string; if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { + funcexe.evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else if (expr->v_type == VAR_PARTIAL) { @@ -753,8 +755,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, partial, NULL) == FAIL) { + funcexe.evaluate = true; + funcexe.partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else { @@ -1050,7 +1053,6 @@ int call_vim_function( ) FUNC_ATTR_NONNULL_ALL { - int doesrange; int ret; int len = (int)STRLEN(func); partial_T *pt = NULL; @@ -1066,9 +1068,12 @@ int call_vim_function( } rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. - ret = call_func(func, len, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, pt, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = pt; + ret = call_func(func, len, rettv, argc, argv, &funcexe); fail: if (ret == FAIL) { @@ -1724,7 +1729,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true) == FAIL) { + if (handle_subscript(&arg, &tv, true, true, (const char_u *)name, + (const char_u **)&name) + == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -3142,6 +3149,65 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) return matches; } +/// Handle a name followed by "(". Both for just "name(arg)" and for +/// "expr->name(arg)". +// +/// @param arg Points to "(", will be advanced +/// @param basetv "expr" for "expr->name(arg)" +// +/// @return OK or FAIL. +static int eval_func(char_u **const arg, char_u *const name, const int name_len, + typval_T *const rettv, const bool evaluate, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + char_u *s = name; + int len = name_len; + + if (!evaluate) { + check_vars((const char *)s, len); + } + + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. + partial_T *partial; + s = deref_func_name((const char *)s, &len, &partial, !evaluate); + + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, len); + + // Invoke the function. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + int ret = get_func_tv(s, len, rettv, arg, &funcexe); + + xfree(s); + + // If evaluate is false rettv->v_type was not set in + // get_func_tv, but it's needed in handle_subscript() to parse + // what follows. So set it here. + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { + rettv->vval.v_string = (char_u *)tv_empty_string; + rettv->v_type = VAR_FUNC; + } + + // Stop the expression evaluation when immediately + // aborting on error, or when an interrupt occurred or + // an exception was thrown but not caught. + if (evaluate && aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + return ret; +} + // TODO(ZyX-I): move to eval/expressions /* @@ -3161,6 +3227,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); @@ -3170,8 +3238,10 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) } // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an - // exception. - if (!aborting()) { + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { emsgf(_(e_invexpr2), arg); } ret = FAIL; @@ -3801,6 +3871,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) // + in front unary plus (ignored) // trailing [] subscript in String or List // trailing .name entry in Dictionary +// trailing ->name() method call // // "arg" must point to the first non-white of the expression. // "arg" is advanced to the next non-white after the recognized expression. @@ -3815,10 +3886,10 @@ static int eval7( { varnumber_T n; int len; - char_u *s; - char_u *start_leader, *end_leader; + char_u *s; + const char_u *start_leader, *end_leader; int ret = OK; - char_u *alias; + char_u *alias; // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. @@ -3968,44 +4039,7 @@ static int eval7( ret = FAIL; } else { if (**arg == '(') { // recursive! - partial_T *partial; - - if (!evaluate) { - check_vars((const char *)s, len); - } - - // If "s" is the name of a variable of type VAR_FUNC - // use its contents. - s = deref_func_name((const char *)s, &len, &partial, !evaluate); - - // Need to make a copy, in case evaluating the arguments makes - // the name invalid. - s = xmemdupz(s, len); - - // Invoke the function. - ret = get_func_tv(s, len, rettv, arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, partial, NULL); - - xfree(s); - - // If evaluate is false rettv->v_type was not set in - // get_func_tv, but it's needed in handle_subscript() to parse - // what follows. So set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = (char_u *)tv_empty_string; - rettv->v_type = VAR_FUNC; - } - - // Stop the expression evaluation when immediately - // aborting on error, or when an interrupt occurred or - // an exception was thrown but not caught. - if (evaluate && aborting()) { - if (ret == OK) { - tv_clear(rettv); - } - ret = FAIL; - } + ret = eval_func(arg, s, len, rettv, evaluate, NULL); } else if (evaluate) { ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); } else { @@ -4019,53 +4053,232 @@ static int eval7( *arg = skipwhite(*arg); // Handle following '[', '(' and '.' for expr[expr], expr.name, - // expr(expr). + // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, evaluate, true); + ret = handle_subscript((const char **)arg, rettv, evaluate, true, + start_leader, &end_leader); } // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - bool error = false; - varnumber_T val = 0; - float_T f = 0.0; + ret = eval7_leader(rettv, start_leader, &end_leader); + } + return ret; +} +/// Apply the leading "!" and "-" before an eval7 expression to "rettv". +/// Adjusts "end_leaderp" until it is at "start_leader". +/// @return OK on success, FAIL on failure. +static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, + const char_u **const end_leaderp) + FUNC_ATTR_NONNULL_ALL +{ + const char_u *end_leader = *end_leaderp; + int ret = OK; + bool error = false; + varnumber_T val = 0; + float_T f = 0.0; + + if (rettv->v_type == VAR_FLOAT) { + f = rettv->vval.v_float; + } else { + val = tv_get_number_chk(rettv, &error); + } + if (error) { + tv_clear(rettv); + ret = FAIL; + } else { + while (end_leader > start_leader) { + end_leader--; + if (*end_leader == '!') { + if (rettv->v_type == VAR_FLOAT) { + f = !f; + } else { + val = !val; + } + } else if (*end_leader == '-') { + if (rettv->v_type == VAR_FLOAT) { + f = -f; + } else { + val = -val; + } + } + } if (rettv->v_type == VAR_FLOAT) { - f = rettv->vval.v_float; + tv_clear(rettv); + rettv->vval.v_float = f; } else { - val = tv_get_number_chk(rettv, &error); - } - if (error) { tv_clear(rettv); - ret = FAIL; + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = val; + } + } + + *end_leaderp = end_leader; + return ret; +} + +/// Call the function referred to in "rettv". +/// @param lua_funcname If `rettv` refers to a v:lua function, this must point +/// to the name of the Lua function to call (after the +/// "v:lua." prefix). +/// @return OK on success, FAIL on failure. +static int call_func_rettv(char_u **const arg, + typval_T *const rettv, + const bool evaluate, + dict_T *const selfdict, + typval_T *const basetv, + const char_u *const lua_funcname) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + partial_T *pt = NULL; + typval_T functv; + const char_u *funcname; + bool is_lua = false; + + // need to copy the funcref so that we can clear rettv + if (evaluate) { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Invoke the function. Recursive! + if (functv.v_type == VAR_PARTIAL) { + pt = functv.vval.v_partial; + is_lua = is_luafunc(pt); + funcname = is_lua ? lua_funcname : partial_name(pt); } else { - while (end_leader > start_leader) { - --end_leader; - if (*end_leader == '!') { - if (rettv->v_type == VAR_FLOAT) { - f = !f; - } else { - val = !val; - } - } else if (*end_leader == '-') { - if (rettv->v_type == VAR_FLOAT) { - f = -f; - } else { - val = -val; - } - } + funcname = functv.vval.v_string; + } + } else { + funcname = (char_u *)""; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + funcexe.basetv = basetv; + const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv, + (char_u **)arg, &funcexe); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } + + return ret; +} + +/// Evaluate "->method()". +/// @param verbose if true, give error messages. +/// @note "*arg" points to the '-'. +/// @return FAIL or OK. @note "*arg" is advanced to after the ')'. +static int eval_lambda(char_u **const arg, typval_T *const rettv, + const bool evaluate, const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + int ret = get_lambda_tv(arg, rettv, evaluate); + if (ret == NOTDONE) { + return FAIL; + } else if (**arg != '(') { + if (verbose) { + if (*skipwhite(*arg) == '(') { + EMSG(_(e_nowhitespace)); + } else { + EMSG2(_(e_missingparen), "lambda"); } - if (rettv->v_type == VAR_FLOAT) { - tv_clear(rettv); - rettv->vval.v_float = f; + } + tv_clear(rettv); + ret = FAIL; + } else { + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +/// Evaluate "->method()" or "->v:lua.method()". +/// @note "*arg" points to the '-'. +/// @return FAIL or OK. "*arg" is advanced to after the ')'. +static int eval_method(char_u **const arg, typval_T *const rettv, + const bool evaluate, const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Locate the method name. + int len; + char_u *name = *arg; + char_u *lua_funcname = NULL; + if (STRNCMP(name, "v:lua.", 6) == 0) { + lua_funcname = name + 6; + *arg = (char_u *)skip_luafunc_name((const char *)lua_funcname); + *arg = skipwhite(*arg); // to detect trailing whitespace later + len = *arg - lua_funcname; + } else { + char_u *alias; + len = get_name_len((const char **)arg, (char **)&alias, evaluate, true); + if (alias != NULL) { + name = alias; + } + } + + int ret; + if (len <= 0) { + if (verbose) { + if (lua_funcname == NULL) { + EMSG(_("E260: Missing name after ->")); } else { - tv_clear(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = val; + EMSG2(_(e_invexpr2), name); + } + } + ret = FAIL; + } else { + if (**arg != '(') { + if (verbose) { + EMSG2(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + EMSG(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; } + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, name, len, rettv, evaluate, &base); } } + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + return ret; } @@ -7255,10 +7468,12 @@ bool callback_call(Callback *const callback, const int argcount_in, abort(); } - int dummy; - return call_func(name, -1, rettv, argcount_in, argvars_in, - NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, - true, partial, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); } static bool set_ref_in_callback(Callback *callback, int copyID, @@ -8393,13 +8608,23 @@ static bool tv_is_luafunc(typval_T *tv) return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); } -/// check the function name after "v:lua." -int check_luafunc_name(const char *str, bool paren) +/// Skips one character past the end of the name of a v:lua function. +/// @param p Pointer to the char AFTER the "v:lua." prefix. +/// @return Pointer to the char one past the end of the function's name. +const char *skip_luafunc_name(const char *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - const char *p = str; while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { p++; } + return p; +} + +/// check the function name after "v:lua." +int check_luafunc_name(const char *const str, const bool paren) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const p = skip_luafunc_name(str); if (*p != (paren ? '(' : NUL)) { return 0; } else { @@ -8407,24 +8632,26 @@ int check_luafunc_name(const char *str, bool paren) } } -/// Handle expr[expr], expr[expr:expr] subscript and .name lookup. -/// Also handle function call with Funcref variable: func(expr) -/// Can all be combined: dict.func(expr)[idx]['func'](expr) +/// Handle: +/// - expr[expr], expr[expr:expr] subscript +/// - ".name" lookup +/// - function call with Funcref variable: func(expr) +/// - method call: var->method() +/// +/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() int handle_subscript( const char **const arg, typval_T *rettv, - int evaluate, // do more than finding the end - int verbose // give error messages + int evaluate, // do more than finding the end + int verbose, // give error messages + const char_u *const start_leader, // start of '!' and '-' prefixes + const char_u **const end_leaderp // end of '!' and '-' prefixes ) { int ret = OK; - dict_T *selfdict = NULL; - const char_u *s; - int len; - typval_T functv; - int slen = 0; - bool lua = false; + dict_T *selfdict = NULL; + const char_u *lua_funcname = NULL; if (tv_is_luafunc(rettv)) { if (**arg != '.') { @@ -8433,55 +8660,28 @@ handle_subscript( } else { (*arg)++; - lua = true; - s = (char_u *)(*arg); - slen = check_luafunc_name(*arg, true); - if (slen == 0) { + lua_funcname = (char_u *)(*arg); + const int len = check_luafunc_name(*arg, true); + if (len == 0) { tv_clear(rettv); ret = FAIL; } - (*arg) += slen; + (*arg) += len; } } - while (ret == OK - && (**arg == '[' - || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) - && !ascii_iswhite(*(*arg - 1))) { + && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) + && !ascii_iswhite(*(*arg - 1))) + || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') { - partial_T *pt = NULL; - // need to copy the funcref so that we can clear rettv - if (evaluate) { - functv = *rettv; - rettv->v_type = VAR_UNKNOWN; - - // Invoke the function. Recursive! - if (functv.v_type == VAR_PARTIAL) { - pt = functv.vval.v_partial; - if (!lua) { - s = partial_name(pt); - } - } else { - s = functv.vval.v_string; - } - } else { - s = (char_u *)""; - } - ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, pt, selfdict); - - // Clear the funcref afterwards, so that deleting it while - // evaluating the arguments is possible (see test55). - if (evaluate) { - tv_clear(&functv); - } + ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL, + lua_funcname); - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ + // Stop the expression evaluation when immediately aborting on + // error, or when an interrupt occurred or an exception was thrown + // but not caught. if (aborting()) { if (ret == OK) { tv_clear(rettv); @@ -8490,6 +8690,21 @@ handle_subscript( } tv_dict_unref(selfdict); selfdict = NULL; + } else if (**arg == '-') { + // Expression "-1.0->method()" applies the leader "-" before + // applying ->. + if (evaluate && *end_leaderp > start_leader) { + ret = eval7_leader(rettv, start_leader, end_leaderp); + } + if (ret == OK) { + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char_u **)arg, rettv, evaluate, verbose); + } + } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { @@ -9274,6 +9489,7 @@ void ex_echo(exarg_T *eap) bool atstart = true; bool need_clear = true; const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; if (eap->skip) ++emsg_skip; @@ -9288,7 +9504,8 @@ void ex_echo(exarg_T *eap) // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. - if (!aborting() && did_emsg == did_emsg_before) { + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { EMSG2(_(e_invexpr2), p); } need_clr_eos = false; @@ -10409,19 +10626,11 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; tv_list_ref(arguments); - int dummy; - (void)call_func((const char_u *)func, - name_len, - &rettv, - 2, - argvars, - NULL, - curwin->w_cursor.lnum, - curwin->w_cursor.lnum, - &dummy, - true, - NULL, - NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe); tv_list_unref(arguments); // Restore caller scope information @@ -10779,7 +10988,9 @@ bool var_exists(const char *var) n = get_var_tv(name, len, &tv, NULL, false, true) == OK; if (n) { // Handle d.key, l[idx], f(expr). - n = handle_subscript(&var, &tv, true, false) == OK; + n = handle_subscript(&var, &tv, true, false, (const char_u *)name, + (const char_u **)&name) + == OK; if (n) { tv_clear(&tv); } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index eb20cd1bc8..faff29b268 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5,6 +5,9 @@ -- args Number of arguments, list with maximum and minimum number of arguments -- or list with a minimum number of arguments only. Defaults to zero -- arguments. +-- base For methods: the argument to use as the base argument (1-indexed): +-- base->method() +-- Defaults to BASE_NONE (function cannot be used as a method). -- func Name of the C function which implements the VimL function. Defaults to -- `f_{funcname}`. @@ -12,111 +15,115 @@ local varargs = function(nr) return {nr} end +-- Usable with the base key: use the last function argument as the method base. +-- Value is from funcs.h file. "BASE_" prefix is omitted. +local LAST = "BASE_LAST" + return { funcs={ - abs={args=1}, - acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc - add={args=2}, - ['and']={args=2}, + abs={args=1, base=1}, + acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc + add={args=2, base=1}, + ['and']={args=2, base=1}, api_info={}, - append={args=2}, - appendbufline={args=3}, + append={args=2, base=LAST}, + appendbufline={args=3, base=LAST}, argc={args={0, 1}}, argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, - asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc - assert_beeps={args={1}}, - assert_equal={args={2, 3}}, - assert_equalfile={args={2, 3}}, + asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc + assert_beeps={args={1}, base=1}, + assert_equal={args={2, 3}, base=2}, + assert_equalfile={args={2, 3}, base=1}, assert_exception={args={1, 2}}, - assert_fails={args={1, 3}}, - assert_false={args={1, 2}}, - assert_inrange={args={3, 4}}, - assert_match={args={2, 3}}, + assert_fails={args={1, 3}, base=1}, + assert_false={args={1, 2}, base=1}, + assert_inrange={args={3, 4}, base=3}, + assert_match={args={2, 3}, base=2}, assert_nobeep={args={1}}, - assert_notequal={args={2, 3}}, - assert_notmatch={args={2, 3}}, - assert_report={args=1}, - assert_true={args={1, 2}}, - atan={args=1, func="float_op_wrapper", data="&atan"}, - atan2={args=2}, + assert_notequal={args={2, 3}, base=2}, + assert_notmatch={args={2, 3}, base=2}, + assert_report={args=1, base=1}, + assert_true={args={1, 2}, base=1}, + atan={args=1, base=1, func="float_op_wrapper", data="&atan"}, + atan2={args=2, base=1}, browse={args=4}, browsedir={args=2}, - bufadd={args=1}, - bufexists={args=1}, - buffer_exists={args=1, func='f_bufexists'}, -- obsolete + bufadd={args=1, base=1}, + bufexists={args=1, base=1}, + buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete - buflisted={args=1}, - bufload={args=1}, - bufloaded={args=1}, - bufname={args={0, 1}}, - bufnr={args={0, 2}}, - bufwinid={args=1}, - bufwinnr={args=1}, - byte2line={args=1}, - byteidx={args=2}, - byteidxcomp={args=2}, - call={args={2, 3}}, - ceil={args=1, func="float_op_wrapper", data="&ceil"}, + buflisted={args=1, base=1}, + bufload={args=1, base=1}, + bufloaded={args=1, base=1}, + bufname={args={0, 1}, base=1}, + bufnr={args={0, 2}, base=1}, + bufwinid={args=1, base=1}, + bufwinnr={args=1, base=1}, + byte2line={args=1, base=1}, + byteidx={args=2, base=1}, + byteidxcomp={args=2, base=1}, + call={args={2, 3}, base=1}, + ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"}, changenr={}, chanclose={args={1, 2}}, chansend={args=2}, - char2nr={args={1, 2}}, + char2nr={args={1, 2}, base=1}, charidx={args={2, 3}}, - cindent={args=1}, - clearmatches={args={0, 1}}, - col={args=1}, - complete={args=2}, - complete_add={args=1}, + cindent={args=1, base=1}, + clearmatches={args={0, 1}, base=1}, + col={args=1, base=1}, + complete={args=2, base=2}, + complete_add={args=1, base=1}, complete_check={}, - complete_info={args={0, 1}}, - confirm={args={1, 4}}, - copy={args=1}, - cos={args=1, func="float_op_wrapper", data="&cos"}, - cosh={args=1, func="float_op_wrapper", data="&cosh"}, - count={args={2, 4}}, + complete_info={args={0, 1}, base=1}, + confirm={args={1, 4}, base=1}, + copy={args=1, base=1}, + cos={args=1, base=1, func="float_op_wrapper", data="&cos"}, + cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"}, + count={args={2, 4}, base=1}, cscope_connection={args={0, 3}}, ctxget={args={0, 1}}, ctxpop={}, ctxpush={args={0, 1}}, ctxset={args={1, 2}}, ctxsize={}, - cursor={args={1, 3}}, - debugbreak={args={1, 1}}, - deepcopy={args={1, 2}}, - delete={args={1,2}}, - deletebufline={args={2,3}}, + cursor={args={1, 3}, base=1}, + debugbreak={args={1, 1}, base=1}, + deepcopy={args={1, 2}, base=1}, + delete={args={1,2}, base=1}, + deletebufline={args={2,3}, base=1}, dictwatcheradd={args=3}, dictwatcherdel={args=3}, did_filetype={}, - diff_filler={args=1}, - diff_hlID={args=2}, - empty={args=1}, + diff_filler={args=1, base=1}, + diff_hlID={args=2, base=1}, + empty={args=1, base=1}, environ={}, escape={args=2}, - eval={args=1}, + eval={args=1, base=1}, eventhandler={}, executable={args=1}, execute={args={1, 2}}, exepath={args=1}, exists={args=1}, - exp={args=1, func="float_op_wrapper", data="&exp"}, + exp={args=1, base=1, func="float_op_wrapper", data="&exp"}, expand={args={1, 3}}, expandcmd={args=1}, - extend={args={2, 3}}, + extend={args={2, 3}, base=1}, feedkeys={args={1, 2}}, file_readable={args=1, func='f_filereadable'}, -- obsolete filereadable={args=1}, filewritable={args=1}, - filter={args=2}, + filter={args=2, base=1}, finddir={args={1, 3}}, findfile={args={1, 3}}, flatten={args={1, 2}}, - float2nr={args=1}, - floor={args=1, func="float_op_wrapper", data="&floor"}, - fmod={args=2}, + float2nr={args=1, base=1}, + floor={args=1, base=1, func="float_op_wrapper", data="&floor"}, + fmod={args=2, base=1}, fnameescape={args=1}, fnamemodify={args=2}, foldclosed={args=1}, @@ -128,7 +135,7 @@ return { funcref={args={1, 3}}, ['function']={args={1, 3}}, garbagecollect={args={0, 1}}, - get={args={2, 3}}, + get={args={2, 3}, base=1}, getbufinfo={args={0, 1}}, getbufline={args={2, 3}}, getbufvar={args={2, 3}}, @@ -173,7 +180,7 @@ return { glob2regpat={args=1}, globpath={args={2, 5}}, has={args=1}, - has_key={args=2}, + has_key={args=2, base=1}, haslocaldir={args={0,2}}, hasmapto={args={1, 3}}, highlightID={args=1, func='f_hlID'}, -- obsolete @@ -187,22 +194,22 @@ return { hostname={}, iconv={args=3}, indent={args=1}, - index={args={2, 4}}, + index={args={2, 4}, base=1}, input={args={1, 3}}, inputdialog={args={1, 3}}, inputlist={args=1}, inputrestore={}, inputsave={}, inputsecret={args={1, 2}}, - insert={args={2, 3}}, + insert={args={2, 3}, base=1}, interrupt={args=0}, - invert={args=1}, + invert={args=1, base=1}, isdirectory={args=1}, - isinf={args=1}, + isinf={args=1, base=1}, islocked={args=1}, - isnan={args=1}, + isnan={args=1, base=1}, id={args=1}, - items={args=1}, + items={args=1, base=1}, jobclose={args={1, 2}, func="f_chanclose"}, jobpid={args=1}, jobresize={args=3}, @@ -210,12 +217,12 @@ return { jobstart={args={1, 2}}, jobstop={args=1}, jobwait={args={1, 2}}, - join={args={1, 2}}, + join={args={1, 2}, base=1}, json_decode={args=1}, json_encode={args=1}, - keys={args=1}, + keys={args=1, base=1}, last_buffer_nr={}, -- obsolete - len={args=1}, + len={args=1, base=1}, libcall={args=3}, libcallnr={args=3}, line={args={1, 2}}, @@ -223,10 +230,10 @@ return { lispindent={args=1}, list2str={args={1, 2}}, localtime={}, - log={args=1, func="float_op_wrapper", data="&log"}, - log10={args=1, func="float_op_wrapper", data="&log10"}, + log={args=1, base=1, func="float_op_wrapper", data="&log"}, + log10={args=1, base=1, func="float_op_wrapper", data="&log10"}, luaeval={args={1, 2}}, - map={args=2}, + map={args=2, base=1}, maparg={args={1, 4}}, mapcheck={args={1, 3}}, match={args={2, 4}}, @@ -238,20 +245,20 @@ return { matchlist={args={2, 4}}, matchstr={args={2, 4}}, matchstrpos={args={2,4}}, - max={args=1}, + max={args=1, base=1}, menu_get={args={1, 2}}, - min={args=1}, + min={args=1, base=1}, mkdir={args={1, 3}}, mode={args={0, 1}}, msgpackdump={args=1}, msgpackparse={args=1}, nextnonblank={args=1}, nr2char={args={1, 2}}, - ['or']={args=2}, + ['or']={args=2, base=1}, pathshorten={args=1}, - pow={args=2}, + pow={args=2, base=1}, prevnonblank={args=1}, - printf={args=varargs(1)}, + printf={args=varargs(1), base=2}, prompt_getprompt={args=1}, prompt_setcallback={args={2, 2}}, prompt_setinterrupt={args={2, 2}}, @@ -270,12 +277,12 @@ return { reltime={args={0, 2}}, reltimefloat={args=1}, reltimestr={args=1}, - remove={args={2, 3}}, + remove={args={2, 3}, base=1}, rename={args=2}, - ['repeat']={args=2}, + ['repeat']={args=2, base=1}, resolve={args=1}, - reverse={args=1}, - round={args=1, func="float_op_wrapper", data="&round"}, + reverse={args=1, base=1}, + round={args=1, base=1, func="float_op_wrapper", data="&round"}, rpcnotify={args=varargs(2)}, rpcrequest={args=varargs(2)}, rpcstart={args={1, 2}}, @@ -324,19 +331,19 @@ return { sign_unplace={args={1, 2}}, sign_unplacelist={args={1}}, simplify={args=1}, - sin={args=1, func="float_op_wrapper", data="&sin"}, - sinh={args=1, func="float_op_wrapper", data="&sinh"}, + sin={args=1, base=1, func="float_op_wrapper", data="&sin"}, + sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, - sort={args={1, 3}}, + sort={args={1, 3}, base=1}, soundfold={args=1}, stdioopen={args=1}, spellbadword={args={0, 1}}, spellsuggest={args={1, 3}}, - split={args={1, 3}}, - sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, + split={args={1, 3}, base=1}, + sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, - str2float={args=1}, - str2list={args={1, 2}}, + str2float={args=1, base=1}, + str2list={args={1, 2}, base=1}, str2nr={args={1, 2}}, strcharpart={args={2, 3}}, strchars={args={1,2}}, @@ -344,31 +351,31 @@ return { strftime={args={1, 2}}, strgetchar={args={2, 2}}, stridx={args={2, 3}}, - string={args=1}, - strlen={args=1}, + string={args=1, base=1}, + strlen={args=1, base=1}, strpart={args={2, 4}}, strptime={args=2}, strridx={args={2, 3}}, - strtrans={args=1}, - strwidth={args=1}, + strtrans={args=1, base=1}, + strwidth={args=1, base=1}, submatch={args={1, 2}}, - substitute={args=4}, + substitute={args=4, base=1}, swapinfo={args={1}}, swapname={args={1}}, synID={args=3}, - synIDattr={args={2, 3}}, - synIDtrans={args=1}, + synIDattr={args={2, 3}, base=1}, + synIDtrans={args=1, base=1}, synconcealed={args=2}, synstack={args=2}, - system={args={1, 2}}, - systemlist={args={1, 3}}, + system={args={1, 2}, base=1}, + systemlist={args={1, 3}, base=1}, tabpagebuflist={args={0, 1}}, tabpagenr={args={0, 1}}, tabpagewinnr={args={1, 2}}, tagfiles={}, taglist={args={1, 2}}, - tan={args=1, func="float_op_wrapper", data="&tan"}, - tanh={args=1, func="float_op_wrapper", data="&tanh"}, + tan={args=1, base=1, func="float_op_wrapper", data="&tan"}, + tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"}, tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, @@ -382,12 +389,12 @@ return { toupper={args=1}, tr={args=3}, trim={args={1,3}}, - trunc={args=1, func="float_op_wrapper", data="&trunc"}, - type={args=1}, + trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"}, + type={args=1, base=1}, undofile={args=1}, undotree={}, - uniq={args={1, 3}}, - values={args=1}, + uniq={args={1, 3}, base=1}, + values={args=1, base=1}, virtcol={args=1}, visualmode={args={0, 1}}, wait={args={2,3}}, @@ -401,7 +408,7 @@ return { win_id2win={args=1}, win_screenpos={args=1}, win_splitmove={args={2, 3}}, - winbufnr={args=1}, + winbufnr={args=1, base=1}, wincol={}, windowsversion={}, winheight={args=1}, @@ -414,6 +421,6 @@ return { winwidth={args=1}, wordcount={}, writefile={args={2, 3}}, - xor={args=2}, + xor={args=2, base=1}, }, } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4e409cca50..99f9f17e0a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -175,6 +175,53 @@ const VimLFuncDef *find_internal_func(const char *const name) return find_internal_func_gperf(name, len); } +int call_internal_func(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (argcount < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount > fdef->max_argc) { + return ERROR_TOOMANY; + } + argvars[argcount].v_type = VAR_UNKNOWN; + fdef->func(argvars, rettv, fdef->data); + return ERROR_NONE; +} + +/// Invoke a method for base->method(). +int call_internal_method(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (fdef->base_arg == BASE_NONE) { + return ERROR_NOTMETHOD; + } else if (argcount + 1 < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount + 1 > fdef->max_argc) { + return ERROR_TOOMANY; + } + + typval_T argv[MAX_FUNC_ARGS + 1]; + const ptrdiff_t base_index + = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, base_index * sizeof(typval_T)); + argv[base_index] = *basetv; + memcpy(argv + base_index + 1, argvars + base_index, + (argcount - base_index) * sizeof(typval_T)); + argv[argcount + 1].v_type = VAR_UNKNOWN; + + fdef->func(argv, rettv, fdef->data); + return ERROR_NONE; +} + /* * Return TRUE for a non-zero Number and a non-empty String. */ @@ -5357,14 +5404,19 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { + || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || chan->streamtype != kChannelStreamProc) { + jobs[i] = NULL; // Invalid job. + } else if (process_is_stopped(&chan->stream.proc)) { + // Job is stopped but not fully destroyed. + // Ensure all callbacks on its event queue are executed. #15402 + process_wait(&chan->stream.proc, -1, NULL); jobs[i] = NULL; // Invalid job. } else { jobs[i] = chan; channel_incref(chan); if (chan->stream.proc.status < 0) { - // Process any pending events on the job's queue before temporarily - // replacing it. + // Flush any events in the job's queue before temporarily replacing it. multiqueue_process_events(chan->events); multiqueue_replace_parent(chan->events, waiting_jobs); } @@ -9420,7 +9472,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) int res; typval_T rettv; typval_T argv[3]; - int dummy; const char *func_name; partial_T *partial = sortinfo->item_compare_partial; @@ -9444,10 +9495,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func((const char_u *)func_name, - -1, - &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, - partial, sortinfo->item_compare_selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index a343290734..c6a0cb959e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -9,11 +9,16 @@ typedef void (*FunPtr)(void); /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); +/// Special flags for base_arg @see VimLFuncDef +#define BASE_NONE 0 ///< Not a method (no base argument). +#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base. + /// Structure holding VimL function definition typedef struct fst { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal number of arguments. uint8_t max_argc; ///< Maximal number of arguments. + uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. VimLFunc func; ///< Function implementation. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index deddec413b..4184e4d922 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -414,12 +414,7 @@ get_func_tv( int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // return: function handled range - int evaluate, - partial_T *partial, // for extra arguments - dict_T *selfdict // Dictionary for "self" + funcexe_T *funcexe // various values ) { char_u *argp; @@ -431,12 +426,13 @@ get_func_tv( * Get the arguments. */ argp = *arg; - while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { + while (argcount < MAX_FUNC_ARGS + - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { + if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) { ret = FAIL; break; } @@ -463,9 +459,7 @@ get_func_tv( ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } } - ret = call_func(name, len, rettv, argcount, argvars, NULL, - firstline, lastline, doesrange, evaluate, - partial, selfdict); + ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; } else if (!aborting()) { @@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, { typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; - int dummy; int r = 0; TV_LIST_ITER(args->vval.v_list, item, { @@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); }); - r = call_func(name, -1, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = selfdict; + r = call_func(name, -1, rettv, argc, argv, &funcexe); func_call_skip_call: // Free the arguments. @@ -1402,6 +1399,9 @@ static void user_func_error(int error, const char_u *name) case ERROR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; + case ERROR_NOTMETHOD: + emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); + break; case ERROR_DELETED: emsg_funcname(N_("E933: Function was deleted: %s"), name); break; @@ -1423,12 +1423,25 @@ static void user_func_error(int error, const char_u *name) } } +/// Used by call_func to add a method base (if any) to a function argument list +/// as the first argument. @see call_func +static void argv_add_base(typval_T *const basetv, typval_T **const argvars, + int *const argcount, typval_T *const new_argvars, + int *const argv_base) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +{ + if (basetv != NULL) { + // Method call: base->Method() + memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount)); + new_argvars[0] = *basetv; + (*argcount)++; + *argvars = new_argvars; + *argv_base = 1; + } +} + /// Call a function with its resolved parameters /// -/// "argv_func", when not NULL, can be used to fill in arguments only when the -/// invoked function uses them. It is called like this: -/// new_argcount = argv_func(current_argcount, argv, called_func_argcount) -/// /// @return FAIL if function cannot be called, else OK (even if an error /// occurred while executing the function! Set `msg_list` to capture /// the error, see do_cmdline()). @@ -1440,15 +1453,9 @@ call_func( int argcount_in, // number of "argvars" typval_T *argvars_in, // vars for arguments, must have "argcount" // PLUS ONE elements! - ArgvFunc argv_func, // function to fill in argvars - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // [out] function handled range - bool evaluate, - partial_T *partial, // optional, can be NULL - dict_T *selfdict_in // Dictionary for "self" + funcexe_T *funcexe // more arguments ) - FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) + FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6) { int ret = FAIL; int error = ERROR_NONE; @@ -1459,9 +1466,12 @@ call_func( char_u *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; - dict_T *selfdict = selfdict_in; - typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL + dict_T *selfdict = funcexe->selfdict; + typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or + // "funcexe->basetv" is not NULL int argv_clear = 0; + int argv_base = 0; + partial_T *partial = funcexe->partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. @@ -1480,14 +1490,15 @@ call_func( fname = fname_trans_sid(name, fname_buf, &tofree, &error); } - *doesrange = false; + if (funcexe->doesrange != NULL) { + *funcexe->doesrange = false; + } if (partial != NULL) { // When the function has a partial with a dict and there is a dict // argument, use the dict argument. That is backwards compatible. // When the dict was bound explicitly use the one from the partial. - if (partial->pt_dict != NULL - && (selfdict_in == NULL || !partial->pt_auto)) { + if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) { selfdict = partial->pt_dict; } if (error == ERROR_NONE && partial->pt_argc > 0) { @@ -1506,7 +1517,7 @@ call_func( } } - if (error == ERROR_NONE && evaluate) { + if (error == ERROR_NONE && funcexe->evaluate) { char_u *rfname = fname; // Ignore "g:" before a function name. @@ -1521,7 +1532,12 @@ call_func( if (is_luafunc(partial)) { if (len > 0) { error = ERROR_NONE; + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); + } else { + // v:lua was called directly; show its name in the emsg + XFREE_CLEAR(name); + funcname = (const char_u *)"v:lua"; } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. @@ -1549,13 +1565,16 @@ call_func( cfunc_T cb = fp->uf_cb; error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); } else if (fp != NULL) { - if (argv_func != NULL) { + if (funcexe->argv_func != NULL) { // postponed filling in the arguments, do it now - argcount = argv_func(argcount, argvars, argv_clear, - fp->uf_args.ga_len); + argcount = funcexe->argv_func(argcount, argvars, argv_clear, + fp->uf_args.ga_len); } - if (fp->uf_flags & FC_RANGE) { - *doesrange = true; + + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); + + if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { + *funcexe->doesrange = true; } if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { error = ERROR_TOOFEW; @@ -1565,25 +1584,20 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, firstline, lastline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } } + } else if (funcexe->basetv != NULL) { + // expr->method(): Find the method name in the table, call its + // implementation with the base as one of the arguments. + error = call_internal_method(fname, argcount, argvars, rettv, + funcexe->basetv); } else { // Find the function name in the table, call its implementation. - const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef != NULL) { - if (argcount < fdef->min_argc) { - error = ERROR_TOOFEW; - } else if (argcount > fdef->max_argc) { - error = ERROR_TOOMANY; - } else { - argvars[argcount].v_type = VAR_UNKNOWN; - fdef->func(argvars, rettv, fdef->data); - error = ERROR_NONE; - } - } + error = call_internal_func(fname, argcount, argvars, rettv); } /* * The function call (or "FuncUndefined" autocommand sequence) might @@ -1607,9 +1621,11 @@ theend: user_func_error(error, (name != NULL) ? name : funcname); } + // clear the copies made from the partial while (argv_clear > 0) { - tv_clear(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear + argv_base]); } + xfree(tofree); xfree(name); @@ -2901,7 +2917,7 @@ void ex_call(exarg_T *eap) int len; typval_T rettv; linenr_T lnum; - int doesrange; + bool doesrange; bool failed = false; funcdict_T fudi; partial_T *partial = NULL; @@ -2947,7 +2963,7 @@ void ex_call(exarg_T *eap) rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); + EMSG2(_(e_missingparen), eap->arg); goto end; } @@ -2965,15 +2981,22 @@ void ex_call(exarg_T *eap) curwin->w_cursor.coladd = 0; } arg = startarg; - if (get_func_tv(name, -1, &rettv, &arg, - eap->line1, eap->line2, &doesrange, - true, partial, fudi.fd_dict) == FAIL) { + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = eap->line1; + funcexe.lastline = eap->line2; + funcexe.doesrange = &doesrange; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = fudi.fd_dict; + if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { failed = true; break; } // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript((const char **)&arg, &rettv, true, true) + if (handle_subscript((const char **)&arg, &rettv, true, true, + (const char_u *)name, (const char_u **)&name) == FAIL) { failed = true; break; diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index e8ad0bf1da..3f111343d2 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -28,11 +28,37 @@ typedef enum { ERROR_OTHER, ERROR_BOTH, ERROR_DELETED, + ERROR_NOTMETHOD, } FnameTransError; +/// Used in funcexe_T. Returns the new argcount. typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, int called_func_argcount); +/// Structure passed between functions dealing with function call execution. +typedef struct { + ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only + ///< when the invoked function uses them + linenr_T firstline; ///< first line of range + linenr_T lastline; ///< last line of range + bool *doesrange; ///< [out] if not NULL: function handled range + bool evaluate; ///< actually evaluate expressions + partial_T *partial; ///< for extra arguments + dict_T *selfdict; ///< Dictionary for "self" + typval_T *basetv; ///< base for base->method() +} funcexe_T; + +#define FUNCEXE_INIT (funcexe_T) { \ + .argv_func = NULL, \ + .firstline = 0, \ + .lastline = 0, \ + .doesrange = NULL, \ + .evaluate = false, \ + .partial = NULL, \ + .selfdict = NULL, \ + .basetv = NULL, \ +} + #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index b93d6cc0ab..8b366d0f3c 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -88,7 +88,7 @@ int process_spawn(Process *proc, bool in, bool out, bool err) } else { process_close(proc); } - shell_free_argv(proc->argv); + process_free(proc); proc->status = -1; return status; } @@ -200,9 +200,9 @@ int process_wait(Process *proc, int ms, MultiQueue *events) if (proc->refcount == 1) { // Job exited, free its resources. decref(proc); - if (events) { - // the decref call created an exit event, process it now - multiqueue_process_events(events); + if (proc->events) { + // decref() created an exit event, process it now. + multiqueue_process_events(proc->events); } } else { proc->refcount--; @@ -239,6 +239,15 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL KILL_TIMEOUT_MS, 0); } +// Frees process-owned resources. +void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL +{ + if (proc->argv != NULL) { + shell_free_argv(proc->argv); + proc->argv = NULL; + } +} + /// Sends SIGKILL (or SIGTERM..SIGKILL for PTY jobs) to processes that did /// not terminate after process_stop(). static void children_kill_cb(uv_timer_t *handle) @@ -269,9 +278,12 @@ static void children_kill_cb(uv_timer_t *handle) static void process_close_event(void **argv) { Process *proc = argv[0]; - shell_free_argv(proc->argv); - if (proc->cb) { // "on_exit" for jobstart(). See channel_job_start(). + if (proc->cb) { + // User (hint: channel_job_start) is responsible for calling + // process_free(). proc->cb(proc, proc->status, proc->data); + } else { + process_free(proc); } } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 24debdb276..20c02e4900 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -26,6 +26,7 @@ struct process { char **argv; dict_T *env; Stream in, out, err; + /// Exit handler. If set, user must call process_free(). process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; bool closed, detach, overlapped; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 3d058e1d09..92b48c36cb 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2688,9 +2688,22 @@ buf_write( /* * Isolate one directory name, using an entry in 'bdir'. */ - (void)copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + STRLEN(IObuff); - if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) { + size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); + p = IObuff + dir_len; + bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]; + if (trailing_pathseps) { + IObuff[dir_len - 2] = NUL; + } + if (*dirp == NUL && !os_isdir(IObuff)) { + int ret; + char *failed_dir; + if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) { + EMSG3(_("E303: Unable to create directory \"%s\" for backup file: %s"), + failed_dir, os_strerror(ret)); + xfree(failed_dir); + } + } + if (trailing_pathseps) { // Ends with '//', Use Full path if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname)) != NULL) { @@ -2840,9 +2853,22 @@ nobackup: /* * Isolate one directory name and make the backup file name. */ - (void)copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + STRLEN(IObuff); - if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) { + size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); + p = IObuff + dir_len; + bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]; + if (trailing_pathseps) { + IObuff[dir_len - 2] = NUL; + } + if (*dirp == NUL && !os_isdir(IObuff)) { + int ret; + char *failed_dir; + if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) { + EMSG3(_("E303: Unable to create directory \"%s\" for backup file: %s"), + failed_dir, os_strerror(ret)); + xfree(failed_dir); + } + } + if (trailing_pathseps) { // path ends with '//', use full path if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname)) != NULL) { diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 95c4b0c1dc..37fab4da60 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -21,6 +21,8 @@ typedef struct foldinfo { long fi_lines; } foldinfo_T; +#define FOLDINFO_INIT { 0, 0, 0, 0 } + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "fold.h.generated.h" diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index c14b465f92..99d80cdebc 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -7,7 +7,7 @@ if arg[1] == '--help' then print(' 2: dispatch output file (dispatch_wrappers.generated.h)') print(' 3: functions metadata output file (funcs_metadata.generated.h)') print(' 4: API metadata output file (api_metadata.mpack)') - print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' 5: lua C bindings output file (lua_api_c_bindings.generated.c)') print(' rest: C files where API functions are defined') end assert(#arg >= 4) @@ -378,7 +378,7 @@ output:write('\n') local lua_c_functions = {} local function process_function(fn) - local lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + local lua_c_function_name = ('nlua_api_%s'):format(fn.name) write_shifted_output(output, string.format([[ static int %s(lua_State *lstate) diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index 679895421a..945fa5099f 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -42,7 +42,7 @@ gperfpipe:write([[ %language=ANSI-C %global-table %readonly-tables -%define initializer-suffix ,0,0,NULL,NULL +%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL %define word-array-name functions %define hash-function-name hash_internal_func_gperf %define lookup-function-name find_internal_func_gperf @@ -59,9 +59,10 @@ for name, def in pairs(funcs) do elseif #args == 1 then args[2] = 'MAX_FUNC_ARGS' end + local base = def.base or "BASE_NONE" local func = def.func or ('f_' .. name) local data = def.data or "NULL" - gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') - :format(name, args[1], args[2], func, data)) + gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n') + :format(name, args[1], args[2], base, func, data)) end gperfpipe:close() diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 96acca4ac7..2a72dbcd09 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -972,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing")); EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required")); EXTERN char_u e_usingsid[] INIT(= N_( "E81: Using <SID> not in a script context")); +EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); EXTERN char_u e_maxmempat[] INIT(= N_( "E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index ed4aefb577..a0e8bad11f 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -63,7 +63,9 @@ typedef enum { , HLF_M // "--More--" message , HLF_CM // Mode (e.g., "-- INSERT --") , HLF_N // line number for ":number" and ":#" commands - , HLF_CLN // current line number + , HLF_LNA // LineNrAbove + , HLF_LNB // LineNrBelow + , HLF_CLN // current line number when 'cursorline' is set , HLF_R // return to continue message and yes/no questions , HLF_S // status lines , HLF_SNC // status lines of not-current windows @@ -118,6 +120,8 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_M] = "MoreMsg", [HLF_CM] = "ModeMsg", [HLF_N] = "LineNr", + [HLF_LNA] = "LineNrAbove", + [HLF_LNB] = "LineNrBelow", [HLF_CLN] = "CursorLineNr", [HLF_R] = "Question", [HLF_S] = "StatusLine", diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index ce8c9b0d06..1a59cd94ae 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -196,8 +196,9 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) { bool ret = true; const int initial_size = lua_gettop(lstate); - kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -234,7 +235,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(kv_pair, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + kvi_push(stack, cur); tv_list_append_list(cur.tv->vval.v_list, kv_pair); cur = (TVPopStackItem) { .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)), @@ -247,7 +248,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { abort(); } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; } } else { @@ -265,7 +266,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + kvi_push(stack, cur); // TODO(ZyX-I): Use indexes, here list item *will* be reallocated. cur = (TVPopStackItem) { .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)), @@ -343,7 +344,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); - kv_push(stack, cur); + kvi_push(stack, cur); } break; } @@ -373,7 +374,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } cur.container = true; cur.idx = lua_gettop(lstate); - kv_push(stack, cur); + kvi_push(stack, cur); lua_pushnil(lstate); } break; @@ -434,7 +435,7 @@ nlua_pop_typval_table_processing_end: lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (!ret) { tv_clear(ret_tv); *ret_tv = (typval_T) { @@ -1060,15 +1061,16 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) { Object ret = NIL; const int initial_size = lua_gettop(lstate); - kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((ObjPopStackItem) { &ret, false })); + kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((ObjPopStackItem) { &ret, false })); while (!ERROR_SET(err) && kv_size(stack)) { - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); - break; - } ObjPopStackItem cur = kv_pop(stack); if (cur.container) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); + break; + } if (cur.obj->type == kObjectTypeDictionary) { // stack: …, dict, key if (cur.obj->data.dictionary.size @@ -1095,7 +1097,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) .data = xmemdupz(s, len), .size = len, }; - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.dictionary.items[idx].value, .container = false, @@ -1117,7 +1119,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) lua_pop(lstate, 2); continue; } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.array.items[idx], .container = false, @@ -1169,7 +1171,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) sizeof(cur.obj->data.array.items[0])); cur.obj->data.array.capacity = table_props.maxidx; cur.container = true; - kv_push(stack, cur); + kvi_push(stack, cur); } break; } @@ -1185,7 +1187,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) sizeof(cur.obj->data.dictionary.items[0])); cur.obj->data.dictionary.capacity = table_props.string_keys_num; cur.container = true; - kv_push(stack, cur); + kvi_push(stack, cur); lua_pushnil(lstate); } break; @@ -1239,7 +1241,7 @@ type_error: lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (ERROR_SET(err)) { api_free_object(ret); ret = NIL; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index cbc2273bc9..5cd9894f9d 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -544,8 +544,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } // [package, loaded, inspect] - lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] + + code = (char *)&lua_F_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + lua_pop(lstate, 2); // [] } @@ -558,6 +567,22 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } } + { + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "loaded"); // [package, loaded] + + const char *code = (char *)&lua_meta_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim._meta"); // [package, loaded] + + lua_pop(lstate, 2); // [] + } + return 0; } @@ -785,13 +810,13 @@ int nlua_call(lua_State *lstate) try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (TRY_WRAP) to capture abort-causing non-exception errors. - (void)call_func(name, (int)name_len, &rettv, nargs, - vim_args, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, NULL); + (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); if (!try_end(&err)) { nlua_push_typval(lstate, &rettv, false); } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index ed435439a4..34b314b40d 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -273,8 +273,8 @@ end ---@see |vim.in_fast_event()| function vim.schedule_wrap(cb) return (function (...) - local args = {...} - vim.schedule(function() cb(unpack(args)) end) + local args = vim.F.pack_len(...) + vim.schedule(function() cb(vim.F.unpack_len(args)) end) end) end @@ -642,6 +642,4 @@ vim._expand_pat_get_parts = function(lua_string) return parts, search_index end -pcall(require, 'vim._meta') - return module diff --git a/src/nvim/main.c b/src/nvim/main.c index 9fc82a75af..b73f5aad76 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -309,9 +309,6 @@ int main(int argc, char **argv) init_highlight(true, false); // Default highlight groups. TIME_MSG("init highlight"); - init_default_mappings(); // Default mappings. - TIME_MSG("init default mappings"); - // Set the break level after the terminal is initialized. debug_break_level = params.use_debug_break_level; @@ -340,21 +337,11 @@ int main(int argc, char **argv) TIME_MSG("initialized screen early for UI"); } - // open terminals when opening files that start with term:// -#define PROTO "term://" - do_cmdline_cmd("augroup nvim_terminal"); - do_cmdline_cmd("autocmd!"); - do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " - ":if !exists('b:term_title')|call termopen( " - // Capture the command string - "matchstr(expand(\"<amatch>\"), " - "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " - // capture the working directory - "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), " - "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" - "|endif"); - do_cmdline_cmd("augroup END"); -#undef PROTO + init_default_mappings(); // Default mappings. + TIME_MSG("init default mappings"); + + init_default_autocmds(); + TIME_MSG("init default autocommands"); // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. diff --git a/src/nvim/math.c b/src/nvim/math.c index b51f335ed7..63309b6f7a 100644 --- a/src/nvim/math.c +++ b/src/nvim/math.c @@ -2,6 +2,8 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <math.h> +#include <stdint.h> +#include <string.h> #include "nvim/math.h" @@ -9,34 +11,26 @@ # include "math.c.generated.h" #endif -#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6 -// Workaround glibc + Clang 6+ bug. #8274 -// https://bugzilla.redhat.com/show_bug.cgi?id=1472437 -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wconversion" -#endif int xfpclassify(double d) { -#if defined(__MINGW32__) - // Workaround mingw warning. #7863 - return __fpclassify(d); -#else - return fpclassify(d); -#endif + uint64_t m; + int e; + + memcpy(&m, &d, sizeof(m)); + e = 0x7ff & (m >> 52); + m = 0xfffffffffffffULL & m; + + switch (e) { + default: return FP_NORMAL; + case 0x000: return m ? FP_SUBNORMAL : FP_ZERO; + case 0x7ff: return m ? FP_NAN : FP_INFINITE; + } } int xisinf(double d) { - return isinf(d); + return FP_INFINITE == xfpclassify(d); } int xisnan(double d) { -#if defined(__MINGW32__) - // Workaround mingw warning. #7863 - return _isnan(d); -#else - return isnan(d); -#endif + return FP_NAN == xfpclassify(d); } -#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6 -# pragma clang diagnostic pop -#endif diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 4d435bc99f..230361b997 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1442,7 +1442,7 @@ recover_names ( * Append the full path to name with path separators made into percent * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") */ -char *make_percent_swname(const char *dir, char *name) +char *make_percent_swname(const char *dir, const char *name) FUNC_ATTR_NONNULL_ARG(1) { char *d = NULL; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index a4a36e5ebf..35f126eab1 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -86,8 +86,9 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) FUNC_ATTR_NONNULL_ALL { bool ret = true; - kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((MPToAPIObjectStackItem) { + kvec_withinit_t(MPToAPIObjectStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = obj, .aobj = arg, .container = false, @@ -155,7 +156,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) const size_t idx = cur.idx; cur.idx++; kv_last(stack) = cur; - kv_push(stack, ((MPToAPIObjectStackItem) { + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = &cur.mobj->via.array.ptr[idx], .aobj = &cur.aobj->data.array.items[idx], .container = false, @@ -209,7 +210,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) } } if (ret) { - kv_push(stack, ((MPToAPIObjectStackItem) { + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = &cur.mobj->via.map.ptr[idx].val, .aobj = &cur.aobj->data.dictionary.items[idx].value, .container = false, @@ -265,7 +266,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) (void)kv_pop(stack); } } - kv_destroy(stack); + kvi_destroy(stack); return ret; } @@ -375,8 +376,9 @@ typedef struct { void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) FUNC_ATTR_NONNULL_ARG(2) { - kvec_t(APIToMPObjectStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); + kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); while (kv_size(stack)) { APIToMPObjectStackItem cur = kv_last(stack); STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 @@ -428,7 +430,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) const size_t idx = cur.idx; cur.idx++; kv_last(stack) = cur; - kv_push(stack, ((APIToMPObjectStackItem) { + kvi_push(stack, ((APIToMPObjectStackItem) { .aobj = &cur.aobj->data.array.items[idx], .container = false, })); @@ -451,7 +453,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) kv_last(stack) = cur; msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res); - kv_push(stack, ((APIToMPObjectStackItem) { + kvi_push(stack, ((APIToMPObjectStackItem) { .aobj = &cur.aobj->data.dictionary.items[idx].value, .container = false, })); @@ -468,7 +470,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) (void)kv_pop(stack); } } - kv_destroy(stack); + kvi_destroy(stack); } void msgpack_rpc_from_array(Array result, msgpack_packer *res) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7b2f77a6f9..4c3d7d58de 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2531,12 +2531,12 @@ do_mouse ( } }; typval_T rettv; - int doesrange; - (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, - -1, - &rettv, ARRAY_SIZE(argv), argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, NULL, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1, + &rettv, ARRAY_SIZE(argv), argv, &funcexe); tv_clear(&rettv); break; } @@ -3277,6 +3277,7 @@ static void clearop(oparg_T *oap) oap->regname = 0; oap->motion_force = NUL; oap->use_reg_one = false; + motion_force = NUL; } static void clearopbeep(oparg_T *oap) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 178b454e4e..a06db4a551 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3444,8 +3444,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) (int)y_size-1, lastsize, totsize, kExtmarkUndo); } else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) { - extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0, 0, - (int)y_size+1, 0, totsize+1, kExtmarkUndo); + // Account for last pasted NL + last NL + extmark_splice(curbuf, (int)new_cursor.lnum-1, col + 1, 0, 0, 0, + (int)y_size+1, 0, totsize+2, kExtmarkUndo); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 0595776f79..d11bbc8ecc 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -260,15 +260,13 @@ typedef struct vimoption { #define P_MLE 0x80000000U ///< under control of 'modelineexpr' #define HIGHLIGHT_INIT \ - "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ - "d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr," \ - "N:CursorLineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title," \ - "v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \ - "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ - "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ - "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ - "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine," \ - "0:Whitespace,I:NormalNC" + "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ + "i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \ + "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \ + "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \ + "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \ + "X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \ + "q:QuickFixLine,0:Whitespace,I:NormalNC" /* * options[] is initialized here. @@ -485,17 +483,17 @@ void set_init_1(bool clean_arg) #endif false); - char *backupdir = stdpaths_user_data_subpath("backup", 0, true); + char *backupdir = stdpaths_user_data_subpath("backup", 2, true); const size_t backupdir_len = strlen(backupdir); backupdir = xrealloc(backupdir, backupdir_len + 3); memmove(backupdir + 2, backupdir, backupdir_len + 1); memmove(backupdir, ".,", 2); - set_string_default("viewdir", stdpaths_user_data_subpath("view", 0, true), - true); set_string_default("backupdir", backupdir, true); + set_string_default("viewdir", stdpaths_user_data_subpath("view", 2, true), + true); set_string_default("directory", stdpaths_user_data_subpath("swap", 2, true), true); - set_string_default("undodir", stdpaths_user_data_subpath("undo", 0, true), + set_string_default("undodir", stdpaths_user_data_subpath("undo", 2, true), true); // Set default for &runtimepath. All necessary expansions are performed in // this function. diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6379174938..98a46cf781 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6726,26 +6726,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; - int dummy; typval_T rettv; staticList10_T matchList = TV_LIST_STATIC10_INIT; - rettv.v_type = VAR_STRING; rettv.vval.v_string = NULL; argv[0].v_type = VAR_LIST; argv[0].vval.v_list = &matchList.sl_list; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.argv_func = fill_submatch_list; + funcexe.evaluate = true; if (expr->v_type == VAR_FUNC) { s = expr->vval.v_string; - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, NULL, NULL); + call_func(s, -1, &rettv, 1, argv, &funcexe); } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; s = partial_name(partial); - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, partial, NULL); + funcexe.partial = partial; + call_func(s, -1, &rettv, 1, argv, &funcexe); } if (tv_list_len(&matchList.sl_list) > 0) { // fill_submatch_list() was called. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 208ae3488f..5a995b9a40 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -65,46 +65,50 @@ #include <stdbool.h> #include <string.h> -#include "nvim/log.h" -#include "nvim/vim.h" -#include "nvim/ascii.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/arabic.h" -#include "nvim/screen.h" +#include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" +#include "nvim/decoration.h" #include "nvim/diff.h" +#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" -#include "nvim/edit.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/indent.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/indent.h" +#include "nvim/lib/kvec.h" +#include "nvim/log.h" +#include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/extmark.h" -#include "nvim/decoration.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/sign.h" #include "nvim/spell.h" @@ -116,12 +120,8 @@ #include "nvim/ui_compositor.h" #include "nvim/undo.h" #include "nvim/version.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/time.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" -#include "nvim/lua/executor.h" -#include "nvim/lib/kvec.h" #define MB_FILLER_CHAR '<' /* character used when a double-width character * doesn't fit. */ @@ -135,7 +135,7 @@ static size_t linebuf_size = 0; static schar_T *linebuf_char = NULL; static sattr_T *linebuf_attr = NULL; -static match_T search_hl; /* used for 'hlsearch' highlight matching */ +static match_T search_hl; // used for 'hlsearch' highlight matching StlClickDefinition *tab_page_click_defs = NULL; @@ -168,8 +168,7 @@ static bool resizing = false; static char * provider_err = NULL; -static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, - Array args, bool default_true) +static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true) { Error err = ERROR_INIT; @@ -208,10 +207,12 @@ void redraw_later(win_T *wp, int type) { if (!exiting && wp->w_redr_type < type) { wp->w_redr_type = type; - if (type >= NOT_VALID) + if (type >= NOT_VALID) { wp->w_lines_valid = 0; - if (must_redraw < type) /* must_redraw is the maximum of all windows */ + } + if (must_redraw < type) { // must_redraw is the maximum of all windows must_redraw = type; + } } } @@ -270,10 +271,10 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) if (wp->w_buffer == buf && lastline >= wp->w_topline && firstline < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) { - wp->w_redraw_top = firstline; + wp->w_redraw_top = firstline; } if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) { - wp->w_redraw_bot = lastline; + wp->w_redraw_bot = lastline; } redraw_later(wp, VALID); } @@ -288,20 +289,16 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot * may become invalid and the whole window will have to be redrawn. */ -void -redrawWinline( - win_T *wp, - linenr_T lnum -) +void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL { if (lnum >= wp->w_topline && lnum < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { - wp->w_redraw_top = lnum; + wp->w_redraw_top = lnum; } if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { - wp->w_redraw_bot = lnum; + wp->w_redraw_bot = lnum; } redraw_later(wp, VALID); } @@ -339,8 +336,9 @@ int update_screen(int type) } if (must_redraw) { - if (type < must_redraw) /* use maximal type */ + if (type < must_redraw) { // use maximal type type = must_redraw; + } /* must_redraw is reset here, so that when we run into some weird * reason to redraw while busy redrawing (e.g., asynchronous @@ -349,9 +347,10 @@ int update_screen(int type) must_redraw = 0; } - /* Need to update w_lines[]. */ - if (curwin->w_lines_valid == 0 && type < NOT_VALID) + // Need to update w_lines[]. + if (curwin->w_lines_valid == 0 && type < NOT_VALID) { type = NOT_VALID; + } /* Postpone the redrawing when it's not needed and when being called * recursively. */ @@ -448,12 +447,13 @@ int update_screen(int type) win_ui_flush(); msg_ext_check_clear(); - /* reset cmdline_row now (may have been changed temporarily) */ + // reset cmdline_row now (may have been changed temporarily) compute_cmdrow(); - /* Check for changed highlighting */ - if (need_highlight_changed) + // Check for changed highlighting + if (need_highlight_changed) { highlight_changed(); + } if (type == CLEAR) { // first clear screen screenclear(); // will reset clear_cmdline @@ -503,21 +503,24 @@ int update_screen(int type) redraw_tabline = true; } - if (clear_cmdline) /* going to clear cmdline (done below) */ + if (clear_cmdline) { // going to clear cmdline (done below) check_for_delay(FALSE); + } /* Force redraw when width of 'number' or 'relativenumber' column * changes. */ if (curwin->w_redr_type < NOT_VALID && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu) - ? number_width(curwin) : 0)) + ? number_width(curwin) : 0)) { curwin->w_redr_type = NOT_VALID; + } /* * Only start redrawing if there is really something to do. */ - if (type == INVERTED) + if (type == INVERTED) { update_curswant(); + } if (curwin->w_redr_type < type && !((type == VALID && curwin->w_lines[0].wl_valid @@ -530,8 +533,9 @@ int update_screen(int type) && curwin->w_old_visual_mode == VIsual_mode && (curwin->w_valid & VALID_VIRTCOL) && curwin->w_old_curswant == curwin->w_curswant) - )) + )) { curwin->w_redr_type = type; + } // Redraw the tab pages line if needed. if (redraw_tabline || type >= NOT_VALID) { @@ -602,7 +606,7 @@ int update_screen(int type) win_update(wp, &providers); } - /* redraw status line after the window to minimize cursor movement */ + // redraw status line after the window to minimize cursor movement if (wp->w_redr_status) { win_redr_status(wp); } @@ -631,9 +635,10 @@ int update_screen(int type) showmode(); } - /* May put up an introductory message when not editing a file */ - if (!did_intro) + // May put up an introductory message when not editing a file + if (!did_intro) { maybe_intro_message(); + } did_intro = true; for (size_t i = 0; i < kv_size(providers); i++) { @@ -704,7 +709,7 @@ bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul - || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); + || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); } /* @@ -715,22 +720,22 @@ bool win_cursorline_standout(const win_T *wp) * * How the window is redrawn depends on wp->w_redr_type. Each type also * implies the one below it. - * NOT_VALID redraw the whole window - * SOME_VALID redraw the whole window but do scroll when possible - * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID - * INVERTED redraw the changed part of the Visual area - * INVERTED_ALL redraw the whole Visual area - * VALID 1. scroll up/down to adjust for a changed w_topline - * 2. update lines at the top when scrolled down - * 3. redraw changed text: - * - if wp->w_buffer->b_mod_set set, update lines between - * b_mod_top and b_mod_bot. - * - if wp->w_redraw_top non-zero, redraw lines between - * wp->w_redraw_top and wp->w_redr_bot. - * - continue redrawing when syntax status is invalid. - * 4. if scrolled up, update lines at the bottom. + * NOT_VALID redraw the whole window + * SOME_VALID redraw the whole window but do scroll when possible + * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID + * INVERTED redraw the changed part of the Visual area + * INVERTED_ALL redraw the whole Visual area + * VALID 1. scroll up/down to adjust for a changed w_topline + * 2. update lines at the top when scrolled down + * 3. redraw changed text: + * - if wp->w_buffer->b_mod_set set, update lines between + * b_mod_top and b_mod_bot. + * - if wp->w_redraw_top non-zero, redraw lines between + * wp->w_redraw_top and wp->w_redr_bot. + * - continue redrawing when syntax status is invalid. + * 4. if scrolled up, update lines at the bottom. * This results in three areas that may need updating: - * top: from first row to top_end (when scrolled down) + * top: from first row to top_end (when scrolled down) * mid: from mid_start to mid_end (update inversion or changed text) * bot: from bot_start to last row (when scrolled up) */ @@ -749,10 +754,10 @@ static void win_update(win_T *wp, Providers *providers) bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit bool top_to_mod = false; // redraw above mod_top - int row; /* current window row to display */ - linenr_T lnum; /* current buffer lnum to display */ - int idx; /* current index in w_lines[] */ - int srow; /* starting row of the current line */ + int row; // current window row to display + linenr_T lnum; // current buffer lnum to display + int idx; // current index in w_lines[] + int srow; // starting row of the current line bool eof = false; // if true, we hit the end of the file bool didline = false; // if true, we finished the last line @@ -767,7 +772,7 @@ static void win_update(win_T *wp, Providers *providers) #define DID_LINE 2 // updated a normal line #define DID_FOLD 3 // updated a folded line int did_update = DID_NONE; - linenr_T syntax_last_parsed = 0; /* last parsed text line */ + linenr_T syntax_last_parsed = 0; // last parsed text line linenr_T mod_top = 0; linenr_T mod_bot = 0; int save_got_int; @@ -824,10 +829,11 @@ static void win_update(win_T *wp, Providers *providers) * changes. Set mod_bot to the first line after the changes. */ mod_top = wp->w_redraw_top; - if (wp->w_redraw_bot != 0) + if (wp->w_redraw_bot != 0) { mod_bot = wp->w_redraw_bot + 1; - else + } else { mod_bot = 0; + } if (buf->b_mod_set) { if (mod_top == 0 || mod_top > buf->b_mod_top) { mod_top = buf->b_mod_top; @@ -835,12 +841,14 @@ static void win_update(win_T *wp, Providers *providers) * in a pattern match. */ if (syntax_present(wp)) { mod_top -= buf->b_s.b_syn_sync_linebreaks; - if (mod_top < 1) + if (mod_top < 1) { mod_top = 1; + } } } - if (mod_bot == 0 || mod_bot < buf->b_mod_bot) + if (mod_bot == 0 || mod_bot < buf->b_mod_bot) { mod_bot = buf->b_mod_bot; + } // When 'hlsearch' is on and using a multi-line search pattern, a // change in one line may make the Search highlighting in a @@ -879,10 +887,11 @@ static void win_update(win_T *wp, Providers *providers) * to this line. If there is no valid entry, use MAXLNUM. */ lnumt = wp->w_topline; lnumb = MAXLNUM; - for (i = 0; i < wp->w_lines_valid; ++i) + for (i = 0; i < wp->w_lines_valid; ++i) { if (wp->w_lines[i].wl_valid) { - if (wp->w_lines[i].wl_lastlnum < mod_top) + if (wp->w_lines[i].wl_lastlnum < mod_top) { lnumt = wp->w_lines[i].wl_lastlnum + 1; + } if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) { lnumb = wp->w_lines[i].wl_lnum; // When there is a fold column it might need updating @@ -892,6 +901,7 @@ static void win_update(win_T *wp, Providers *providers) } } } + } (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); if (mod_top > lnumt) { @@ -912,16 +922,18 @@ static void win_update(win_T *wp, Providers *providers) * If the end of the change is above w_topline: do like no change was * made, but redraw the first line to find changes in syntax. */ if (mod_top != 0 && mod_top < wp->w_topline) { - if (mod_bot > wp->w_topline) + if (mod_bot > wp->w_topline) { mod_top = wp->w_topline; - else if (syntax_present(wp)) + } else if (syntax_present(wp)) { top_end = 1; + } } /* When line numbers are displayed need to redraw all lines below * inserted/deleted lines. */ - if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) + if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) { mod_bot = MAXLNUM; + } } wp->w_redraw_top = 0; // reset for next time wp->w_redraw_bot = 0; @@ -939,12 +951,13 @@ static void win_update(win_T *wp, Providers *providers) break; } } - if (top_end == 0) - /* not found (cannot happen?): redraw everything */ + if (top_end == 0) { + // not found (cannot happen?): redraw everything type = NOT_VALID; - else - /* top area defined, the rest is VALID */ + } else { + // top area defined, the rest is VALID type = VALID; + } } /* @@ -957,8 +970,7 @@ static void win_update(win_T *wp, Providers *providers) */ if ((type == VALID || type == SOME_VALID || type == INVERTED || type == INVERTED_ALL) - && !wp->w_botfill && !wp->w_old_botfill - ) { + && !wp->w_botfill && !wp->w_old_botfill) { if (mod_top != 0 && wp->w_topline == mod_top && (!wp->w_lines[0].wl_valid @@ -986,14 +998,16 @@ static void win_update(win_T *wp, Providers *providers) } (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); } - } else + } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; + } if (j < wp->w_grid.Rows - 2) { // not too far off i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); - /* insert extra lines for previously invisible filler lines */ - if (wp->w_lines[0].wl_lnum != wp->w_topline) + // insert extra lines for previously invisible filler lines + if (wp->w_lines[0].wl_lnum != wp->w_topline) { i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; + } if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. @@ -1030,7 +1044,7 @@ static void win_update(win_T *wp, Providers *providers) * needs updating. */ - /* try to find wp->w_topline in wp->w_lines[].wl_lnum */ + // try to find wp->w_topline in wp->w_lines[].wl_lnum j = -1; row = 0; for (i = 0; i < wp->w_lines_valid; i++) { @@ -1052,11 +1066,12 @@ static void win_update(win_T *wp, Providers *providers) */ /* If the topline didn't change, delete old filler lines, * otherwise delete filler lines of the new topline... */ - if (wp->w_lines[0].wl_lnum == wp->w_topline) + if (wp->w_lines[0].wl_lnum == wp->w_topline) { row += wp->w_old_topfill; - else + } else { row += diff_check_fill(wp, wp->w_topline); - /* ... but don't delete new filler lines. */ + } + // ... but don't delete new filler lines. row -= wp->w_topfill; if (row > 0) { win_scroll_lines(wp, 0, -row); @@ -1065,7 +1080,7 @@ static void win_update(win_T *wp, Providers *providers) if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) { /* * Skip the lines (below the deleted lines) that are still - * valid and don't need redrawing. Copy their info + * valid and don't need redrawing. Copy their info * upwards, to compensate for the deleted lines. Set * bot_start to the first row that needs redrawing. */ @@ -1082,7 +1097,7 @@ static void win_update(win_T *wp, Providers *providers) } bot_start += wp->w_lines[idx++].wl_size; - /* stop at the last valid entry in w_lines[].wl_size */ + // stop at the last valid entry in w_lines[].wl_size if (++j >= wp->w_lines_valid) { wp->w_lines_valid = idx; break; @@ -1090,10 +1105,11 @@ static void win_update(win_T *wp, Providers *providers) } /* Correct the first entry for filler lines at the top * when it won't get updated below. */ - if (wp->w_p_diff && bot_start > 0) + if (wp->w_p_diff && bot_start > 0) { wp->w_lines[0].wl_size = plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill; + } } } } @@ -1103,19 +1119,19 @@ static void win_update(win_T *wp, Providers *providers) mid_end = wp->w_grid.Rows; } } else { - /* Not VALID or INVERTED: redraw all lines. */ + // Not VALID or INVERTED: redraw all lines. mid_start = 0; mid_end = wp->w_grid.Rows; } if (type == SOME_VALID) { - /* SOME_VALID: redraw all lines. */ + // SOME_VALID: redraw all lines. mid_start = 0; mid_end = wp->w_grid.Rows; type = NOT_VALID; } - /* check if we are updating or removing the inverted part */ + // check if we are updating or removing the inverted part if ((VIsual_active && buf == curwin->w_buffer) || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) { linenr_T from, to; @@ -1132,15 +1148,19 @@ static void win_update(win_T *wp, Providers *providers) from = VIsual.lnum; to = curwin->w_cursor.lnum; } - /* redraw more when the cursor moved as well */ - if (wp->w_old_cursor_lnum < from) + // redraw more when the cursor moved as well + if (wp->w_old_cursor_lnum < from) { from = wp->w_old_cursor_lnum; - if (wp->w_old_cursor_lnum > to) + } + if (wp->w_old_cursor_lnum > to) { to = wp->w_old_cursor_lnum; - if (wp->w_old_visual_lnum < from) + } + if (wp->w_old_visual_lnum < from) { from = wp->w_old_visual_lnum; - if (wp->w_old_visual_lnum > to) + } + if (wp->w_old_visual_lnum > to) { to = wp->w_old_visual_lnum; + } } else { /* * Find the line numbers that need to be updated: The lines @@ -1153,21 +1173,26 @@ static void win_update(win_T *wp, Providers *providers) } else { from = wp->w_old_cursor_lnum; to = curwin->w_cursor.lnum; - if (from == 0) /* Visual mode just started */ + if (from == 0) { // Visual mode just started from = to; + } } if (VIsual.lnum != wp->w_old_visual_lnum || VIsual.col != wp->w_old_visual_col) { if (wp->w_old_visual_lnum < from - && wp->w_old_visual_lnum != 0) + && wp->w_old_visual_lnum != 0) { from = wp->w_old_visual_lnum; - if (wp->w_old_visual_lnum > to) + } + if (wp->w_old_visual_lnum > to) { to = wp->w_old_visual_lnum; - if (VIsual.lnum < from) + } + if (VIsual.lnum < from) { from = VIsual.lnum; - if (VIsual.lnum > to) + } + if (VIsual.lnum > to) { to = VIsual.lnum; + } } } @@ -1180,8 +1205,9 @@ static void win_update(win_T *wp, Providers *providers) colnr_T fromc, toc; int save_ve_flags = ve_flags; - if (curwin->w_p_lbr) + if (curwin->w_p_lbr) { ve_flags = VE_ALL; + } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); ve_flags = save_ve_flags; @@ -1194,16 +1220,18 @@ static void win_update(win_T *wp, Providers *providers) if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { - if (from > VIsual.lnum) + if (from > VIsual.lnum) { from = VIsual.lnum; - if (to < VIsual.lnum) + } + if (to < VIsual.lnum) { to = VIsual.lnum; + } } wp->w_old_cursor_fcol = fromc; wp->w_old_cursor_lcol = toc; } } else { - /* Use the line numbers of the old Visual area. */ + // Use the line numbers of the old Visual area. if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) { from = wp->w_old_cursor_lnum; to = wp->w_old_visual_lnum; @@ -1216,18 +1244,21 @@ static void win_update(win_T *wp, Providers *providers) /* * There is no need to update lines above the top of the window. */ - if (from < wp->w_topline) + if (from < wp->w_topline) { from = wp->w_topline; + } /* * If we know the value of w_botline, use it to restrict the update to * the lines that are visible in the window. */ if (wp->w_valid & VALID_BOTLINE) { - if (from >= wp->w_botline) + if (from >= wp->w_botline) { from = wp->w_botline - 1; - if (to >= wp->w_botline) + } + if (to >= wp->w_botline) { to = wp->w_botline - 1; + } } /* @@ -1243,27 +1274,30 @@ static void win_update(win_T *wp, Providers *providers) lnum = wp->w_topline; idx = 0; srow = 0; - if (scrolled_down) + if (scrolled_down) { mid_start = top_end; - else + } else { mid_start = 0; - while (lnum < from && idx < wp->w_lines_valid) { /* find start */ - if (wp->w_lines[idx].wl_valid) + } + while (lnum < from && idx < wp->w_lines_valid) { // find start + if (wp->w_lines[idx].wl_valid) { mid_start += wp->w_lines[idx].wl_size; - else if (!scrolled_down) + } else if (!scrolled_down) { srow += wp->w_lines[idx].wl_size; + } ++idx; - if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) + if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) { lnum = wp->w_lines[idx].wl_lnum; - else + } else { ++lnum; + } } srow += mid_start; mid_end = wp->w_grid.Rows; for (; idx < wp->w_lines_valid; idx++) { // find end if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { - /* Only update until first row of this line */ + // Only update until first row of this line mid_end = srow; break; } @@ -1285,7 +1319,7 @@ static void win_update(win_T *wp, Providers *providers) wp->w_old_visual_col = 0; } - /* reset got_int, otherwise regexp won't work */ + // reset got_int, otherwise regexp won't work save_got_int = got_int; got_int = 0; // Set the time limit to 'redrawtime'. @@ -1295,7 +1329,7 @@ static void win_update(win_T *wp, Providers *providers) /* * Update all the window rows. */ - idx = 0; /* first entry in w_lines[].wl_size */ + idx = 0; // first entry in w_lines[].wl_size row = 0; srow = 0; lnum = wp->w_topline; // first line shown in window @@ -1335,7 +1369,7 @@ static void win_update(win_T *wp, Providers *providers) break; } - /* stop updating when hit the end of the file */ + // stop updating when hit the end of the file if (lnum > buf->b_ml.ml_line_count) { eof = true; break; @@ -1400,8 +1434,9 @@ static void win_update(win_T *wp, Providers *providers) /* Only valid lines have a meaningful wl_lnum. Invalid * lines are part of the changed area. */ if (wp->w_lines[i].wl_valid - && wp->w_lines[i].wl_lnum == mod_bot) + && wp->w_lines[i].wl_lnum == mod_bot) { break; + } old_rows += wp->w_lines[i].wl_size; if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { @@ -1409,8 +1444,9 @@ static void win_update(win_T *wp, Providers *providers) * Add following invalid entries. */ ++i; while (i < wp->w_lines_valid - && !wp->w_lines[i].wl_valid) + && !wp->w_lines[i].wl_valid) { old_rows += wp->w_lines[i++].wl_size; + } break; } } @@ -1473,15 +1509,15 @@ static void win_update(win_T *wp, Providers *providers) if (j < i) { int x = row + new_rows; - /* move entries in w_lines[] upwards */ + // move entries in w_lines[] upwards for (;; ) { - /* stop at last valid entry in w_lines[] */ + // stop at last valid entry in w_lines[] if (i >= wp->w_lines_valid) { wp->w_lines_valid = j; break; } wp->w_lines[j] = wp->w_lines[i]; - /* stop at a line that won't fit */ + // stop at a line that won't fit if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.Rows) { wp->w_lines_valid = j + 1; @@ -1490,10 +1526,11 @@ static void win_update(win_T *wp, Providers *providers) x += wp->w_lines[j++].wl_size; ++i; } - if (bot_start > x) + if (bot_start > x) { bot_start = x; - } else { /* j > i */ - /* move entries in w_lines[] downwards */ + } + } else { // j > i + // move entries in w_lines[] downwards j -= i; wp->w_lines_valid += j; if (wp->w_lines_valid > wp->w_grid.Rows) { @@ -1529,17 +1566,17 @@ static void win_update(win_T *wp, Providers *providers) && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows - && diff_check_fill(wp, lnum) == 0 - ) { + && diff_check_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.Rows + 1; } else { prepare_search_hl(wp, lnum); - /* Let the syntax stuff know we skipped a few lines. */ + // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum - && syntax_present(wp)) + && syntax_present(wp)) { syntax_end_parsing(syntax_last_parsed + 1); + } // Display one line row = win_line(wp, lnum, srow, @@ -1603,14 +1640,16 @@ static void win_update(win_T *wp, Providers *providers) */ - if (idx > wp->w_lines_valid) + if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; + } /* * Let the syntax stuff know we stop parsing here. */ - if (syntax_last_parsed != 0 && syntax_present(wp)) + if (syntax_last_parsed != 0 && syntax_present(wp)) { syntax_end_parsing(syntax_last_parsed + 1); + } /* * If we didn't hit the end of the file, and we didn't finish the last @@ -1656,20 +1695,15 @@ static void win_update(win_T *wp, Providers *providers) wp->w_botline = buf->b_ml.ml_line_count + 1; j = diff_check_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { - // display filler lines at the end of the file - if (char2cells(wp->w_p_fcs_chars.diff) > 1) { - i = '-'; - } else { - i = wp->w_p_fcs_chars.diff; - } - if (row + j > wp->w_grid.Rows) { - j = wp->w_grid.Rows - row; - } - win_draw_end(wp, i, i, true, row, row + (int)j, HLF_DED); - row += j; + // Display filler text below last line. win_line() will check + // for ml_line_count+1 and only draw filler lines + foldinfo_T info = FOLDINFO_INIT; + row = win_line(wp, wp->w_botline, row, wp->w_grid.Rows, + false, false, info, &line_providers); } - } else if (dollar_vcol == -1) + } else if (dollar_vcol == -1) { wp->w_botline = lnum; + } // make sure the rest of the screen is blank // write the 'eob' character to rows that aren't part of the file. @@ -1684,7 +1718,7 @@ static void win_update(win_T *wp, Providers *providers) } syn_set_timeout(NULL); - /* Reset the type of redrawing required, the window has been updated. */ + // Reset the type of redrawing required, the window has been updated. wp->w_redr_type = 0; wp->w_old_topfill = wp->w_topfill; wp->w_old_botfill = wp->w_botfill; @@ -1754,9 +1788,10 @@ static void win_update(win_T *wp, Providers *providers) } - /* restore got_int, unless CTRL-C was hit while redrawing */ - if (!got_int) + // restore got_int, unless CTRL-C was hit while redrawing + if (!got_int) { got_int = save_got_int; + } } // NOLINT(readability/fn_size) /// Returns width of the signcolumn that should be used for the whole window @@ -1776,8 +1811,8 @@ int win_signcol_width(win_T *wp) /// Call grid_fill() with columns adjusted for 'rightleft' if needed. /// Return the new offset. -static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, - int endrow, int attr) +static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow, + int attr) { int nn = off + width; @@ -1798,8 +1833,7 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, /// Clear lines near the end of the window and mark the unused lines with "c1". /// Use "c2" as filler character. /// When "draw_margin" is true, then draw the sign/fold/number columns. -static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, - int endrow, hlf_T hl) +static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl) { assert(hl >= 0 && hl < HLF_COUNT); int n = 0; @@ -1844,8 +1878,9 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, /// @return true when there are columns to draw. static bool advance_color_col(int vcol, int **color_cols) { - while (**color_cols >= 0 && vcol > **color_cols) + while (**color_cols >= 0 && vcol > **color_cols) { ++*color_cols; + } return **color_cols >= 0; } @@ -1923,13 +1958,7 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) /// /// Assume monocell characters /// @return number of chars added to \param p -static size_t -fill_foldcolumn( - char_u *p, - win_T *wp, - foldinfo_T foldinfo, - linenr_T lnum -) +static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum) { int i = 0; int level; @@ -1998,9 +2027,8 @@ fill_foldcolumn( /// or explicitly return `false`. /// /// @return the number of last row the line occupies. -static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, - bool nochange, bool number_only, foldinfo_T foldinfo, - Providers *providers) +static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, + bool number_only, foldinfo_T foldinfo, Providers *providers) { int c = 0; // init for GCC long vcol = 0; // virtual column (for tabs) @@ -2033,12 +2061,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int saved_c_final = 0; int saved_char_attr = 0; - int n_attr = 0; /* chars with special attr */ - int saved_attr2 = 0; /* char_attr saved for n_attr */ - int n_attr3 = 0; /* chars with overruling special attr */ - int saved_attr3 = 0; /* char_attr saved for n_attr3 */ + int n_attr = 0; // chars with special attr + int saved_attr2 = 0; // char_attr saved for n_attr + int n_attr3 = 0; // chars with overruling special attr + int saved_attr3 = 0; // char_attr saved for n_attr3 - int n_skip = 0; /* nr of chars to skip for 'nowrap' */ + int n_skip = 0; // nr of chars to skip for 'nowrap' int fromcol = -10; // start of inverting int tocol = MAXCOL; // end of inverting @@ -2048,29 +2076,29 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, pos_T pos; long v; - int char_attr = 0; /* attributes for next character */ - int attr_pri = FALSE; /* char_attr has priority */ + int char_attr = 0; // attributes for next character + int attr_pri = FALSE; // char_attr has priority int area_highlighting = FALSE; /* Visual or incsearch highlighting in this line */ - int attr = 0; /* attributes for area highlighting */ - int area_attr = 0; /* attributes desired by highlighting */ - int search_attr = 0; /* attributes desired by 'hlsearch' */ - int vcol_save_attr = 0; /* saved attr for 'cursorcolumn' */ - int syntax_attr = 0; /* attributes desired by syntax */ - int has_syntax = FALSE; /* this buffer has syntax highl. */ + int attr = 0; // attributes for area highlighting + int area_attr = 0; // attributes desired by highlighting + int search_attr = 0; // attributes desired by 'hlsearch' + int vcol_save_attr = 0; // saved attr for 'cursorcolumn' + int syntax_attr = 0; // attributes desired by syntax + int has_syntax = FALSE; // this buffer has syntax highl. int save_did_emsg; int eol_hl_off = 0; // 1 if highlighted char after EOL bool draw_color_col = false; // highlight colorcolumn int *color_cols = NULL; // pointer to according columns array bool has_spell = false; // this buffer has spell checking # define SPWORDLEN 150 - char_u nextline[SPWORDLEN * 2]; /* text with start of the next line */ - int nextlinecol = 0; /* column where nextline[] starts */ + char_u nextline[SPWORDLEN * 2]; // text with start of the next line + int nextlinecol = 0; // column where nextline[] starts int nextline_idx = 0; /* index in nextline[] where next line starts */ - int spell_attr = 0; /* attributes desired by spelling */ - int word_end = 0; /* last byte with same spell_attr */ - static linenr_T checked_lnum = 0; /* line number for "checked_col" */ + int spell_attr = 0; // attributes desired by spelling + int word_end = 0; // last byte with same spell_attr + static linenr_T checked_lnum = 0; // line number for "checked_col" static int checked_col = 0; /* column in "checked_lnum" up to which * there are no spell errors */ static int cap_col = -1; // column to check for Cap word @@ -2122,16 +2150,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int left_curline_col = 0; int right_curline_col = 0; - /* draw_state: items that are drawn in sequence: */ -#define WL_START 0 /* nothing done yet */ -# define WL_CMDLINE WL_START + 1 /* cmdline window column */ -# define WL_FOLD WL_CMDLINE + 1 /* 'foldcolumn' */ -# define WL_SIGN WL_FOLD + 1 /* column for signs */ -#define WL_NR WL_SIGN + 1 /* line number */ -# define WL_BRI WL_NR + 1 /* 'breakindent' */ -# define WL_SBR WL_BRI + 1 /* 'showbreak' or 'diff' */ -#define WL_LINE WL_SBR + 1 /* text in the line */ - int draw_state = WL_START; /* what to draw next */ + // draw_state: items that are drawn in sequence: +#define WL_START 0 // nothing done yet +# define WL_CMDLINE WL_START + 1 // cmdline window column +# define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' +# define WL_SIGN WL_FOLD + 1 // column for signs +#define WL_NR WL_SIGN + 1 // line number +# define WL_BRI WL_NR + 1 // 'breakindent' +# define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' +#define WL_LINE WL_SBR + 1 // text in the line + int draw_state = WL_START; // what to draw next int syntax_flags = 0; int syntax_seqnr = 0; @@ -2155,12 +2183,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, boguscols = 0; \ } - if (startrow > endrow) /* past the end already! */ + if (startrow > endrow) { // past the end already! return startrow; + } row = startrow; buf_T *buf = wp->w_buffer; + bool end_fill = (lnum == buf->b_ml.ml_line_count+1); if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, @@ -2228,6 +2258,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (wp->w_p_spell && !has_fold + && !end_fill && *wp->w_s->b_p_spl != NUL && !GA_EMPTY(&wp->w_s->b_langp) && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { @@ -2327,7 +2358,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, area_highlighting = true; attr = win_hl_attr(wp, HLF_V); } - // handle 'incsearch' and ":s///c" highlighting + // handle 'incsearch' and ":s///c" highlighting } else if (highlight_match && wp == curwin && !has_fold @@ -2356,19 +2387,22 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, filler_lines = diff_check(wp, lnum); if (filler_lines < 0) { if (filler_lines == -1) { - if (diff_find_change(wp, lnum, &change_start, &change_end)) - diff_hlf = HLF_ADD; /* added line */ - else if (change_start == 0) - diff_hlf = HLF_TXD; /* changed text */ - else - diff_hlf = HLF_CHD; /* changed line */ - } else - diff_hlf = HLF_ADD; /* added line */ + if (diff_find_change(wp, lnum, &change_start, &change_end)) { + diff_hlf = HLF_ADD; // added line + } else if (change_start == 0) { + diff_hlf = HLF_TXD; // changed text + } else { + diff_hlf = HLF_CHD; // changed line + } + } else { + diff_hlf = HLF_ADD; // added line + } filler_lines = 0; area_highlighting = TRUE; } - if (lnum == wp->w_topline) + if (lnum == wp->w_topline) { filler_lines = wp->w_topfill; + } filler_todo = filler_lines; // Cursor line highlighting for 'cursorline' in the current window. @@ -2428,7 +2462,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, line_attr_lowprio_save = line_attr_lowprio; } - line = ml_get_buf(wp->w_buffer, lnum, false); + line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false); ptr = line; if (has_spell && !number_only) { @@ -2441,7 +2475,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * current line into nextline[]. Above the start of the next line was * copied to nextline[SPWORDLEN]. */ if (nextline[SPWORDLEN] == NUL) { - /* No next line or it is empty. */ + // No next line or it is empty. nextlinecol = MAXCOL; nextline_idx = 0; } else { @@ -2454,7 +2488,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, STRMOVE(nextline + v, nextline + SPWORDLEN); nextline_idx = v + 1; } else { - /* Long line, use only the last SPWORDLEN bytes. */ + // Long line, use only the last SPWORDLEN bytes. nextlinecol = v - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512 nextline_idx = SPWORDLEN + 1; @@ -2462,7 +2496,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_p_list && !has_fold) { + if (wp->w_p_list && !has_fold && !end_fill) { if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.lead @@ -2475,7 +2509,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) { trailcol--; } - trailcol += (colnr_T) (ptr - line); + trailcol += (colnr_T)(ptr - line); } // find end of leading whitespace if (wp->w_p_lcs_chars.lead) { @@ -2497,10 +2531,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the * first character to be displayed. */ - if (wp->w_p_wrap) + if (wp->w_p_wrap) { v = wp->w_skipcol; - else + } else { v = wp->w_leftcol; + } if (v > 0 && !number_only) { char_u *prev_ptr = ptr; while (vcol < v && *ptr != NUL) { @@ -2539,10 +2574,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * Adjust for when the inverted text is before the screen, * and when the start of the inverted text is before the screen. */ - if (tocol <= vcol) + if (tocol <= vcol) { fromcol = 0; - else if (fromcol >= 0 && fromcol < vcol) + } else if (fromcol >= 0 && fromcol < vcol) { fromcol = vcol; + } // When w_skipcol is non-zero, first line needs 'showbreak' if (wp->w_p_wrap) { @@ -2570,13 +2606,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, spell_hlf = HLF_COUNT; word_end = (int)(spell_to_word_end(ptr, wp) - line + 1); } else { - /* bad word found, use attributes until end of word */ + // bad word found, use attributes until end of word assert(len <= INT_MAX); word_end = wp->w_cursor.col + (int)len + 1; - /* Turn index into actual attributes. */ - if (spell_hlf != HLF_COUNT) + // Turn index into actual attributes. + if (spell_hlf != HLF_COUNT) { spell_attr = highlight_attr[spell_hlf]; + } } wp->w_cursor = pos; @@ -2598,12 +2635,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * cursor */ fromcol_prev = fromcol; fromcol = -1; - } else if ((colnr_T)fromcol < wp->w_virtcol) - /* restart highlighting after the cursor */ + } else if ((colnr_T)fromcol < wp->w_virtcol) { + // restart highlighting after the cursor fromcol_prev = wp->w_virtcol; + } } - if (fromcol >= tocol) + if (fromcol >= tocol) { fromcol = -1; + } } /* @@ -2613,8 +2652,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, cur = wp->w_match_head; shl_flag = false; while ((cur != NULL || !shl_flag) && !number_only - && !has_fold - ) { + && !has_fold && !end_fill) { if (!shl_flag) { shl = &search_hl; shl_flag = true; @@ -2647,18 +2685,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, shl->startcol = 0; } if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) { - shl->endcol = shl->rm.endpos[0].col; + - shl->rm.startpos[0].lnum) { + shl->endcol = shl->rm.endpos[0].col; } else { - shl->endcol = MAXCOL; + shl->endcol = MAXCOL; } // Highlight one character for an empty match. if (shl->startcol == shl->endcol) { - if (line[shl->endcol] != NUL) { - shl->endcol += (*mb_ptr2len)(line + shl->endcol); - } else { - ++shl->endcol; - } + if (line[shl->endcol] != NUL) { + shl->endcol += (*mb_ptr2len)(line + shl->endcol); + } else { + ++shl->endcol; + } } if ((long)shl->startcol < v) { // match at leftcol shl->attr_cur = shl->attr; @@ -2667,8 +2705,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } area_highlighting = true; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } unsigned off = 0; // Offset relative start of line @@ -2705,7 +2744,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (draw_state == WL_CMDLINE - 1 && n_extra == 0) { draw_state = WL_CMDLINE; if (cmdwin_type != 0 && wp == curwin) { - /* Draw the cmdline character. */ + // Draw the cmdline character. n_extra = 1; c_extra = cmdwin_type; c_final = NUL; @@ -2733,18 +2772,17 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // sign column, this is hit until sign_idx reaches count if (draw_state == WL_SIGN - 1 && n_extra == 0) { - draw_state = WL_SIGN; - /* Show the sign column when there are any signs in this - * buffer or when using Netbeans. */ - int count = win_signcol_count(wp); - if (count > 0) { - get_sign_display_info( - false, wp, sattrs, row, - startrow, filler_lines, filler_todo, count, - &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); - } + draw_state = WL_SIGN; + /* Show the sign column when there are any signs in this + * buffer or when using Netbeans. */ + int count = win_signcol_count(wp); + if (count > 0) { + get_sign_display_info(false, wp, sattrs, row, + startrow, filler_lines, filler_todo, count, + &c_extra, &c_final, extra, sizeof(extra), + &p_extra, &n_extra, + &char_attr, &draw_state, &sign_idx); + } } if (draw_state == WL_NR - 1 && n_extra == 0) { @@ -2760,12 +2798,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { int count = win_signcol_count(wp); - get_sign_display_info( - true, wp, sattrs, row, - startrow, filler_lines, filler_todo, count, - &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + get_sign_display_info(true, wp, sattrs, row, + startrow, filler_lines, filler_todo, count, + &c_extra, &c_final, extra, sizeof(extra), + &p_extra, &n_extra, + &char_attr, &draw_state, &sign_idx); } else { if (row == startrow + filler_lines) { // Draw the line number (empty space after wrapping). */ @@ -2812,6 +2849,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, n_extra = number_width(wp) + 1; char_attr = win_hl_attr(wp, HLF_N); + if (wp->w_p_rnu && lnum < wp->w_cursor.lnum) { + // Use LineNrAbove + char_attr = win_hl_attr(wp, HLF_LNA); + } + if (wp->w_p_rnu && lnum > wp->w_cursor.lnum) { + // Use LineNrBelow + char_attr = win_hl_attr(wp, HLF_LNB); + } + sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); if (num_sattr != NULL) { // :sign defined with "numhl" highlight. @@ -2899,7 +2945,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, char_attr = win_hl_attr(wp, HLF_DED); } if (*p_sbr != NUL && need_showbreak) { - /* Draw 'showbreak' at the start of each broken line. */ + // Draw 'showbreak' at the start of each broken line. p_extra = p_sbr; c_extra = NUL; c_final = NUL; @@ -2911,8 +2957,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, vcol_sbr = vcol + MB_CHARLEN(p_sbr); /* Correct end of highlighted area for 'showbreak', * required when 'linebreak' is also set. */ - if (tocol == vcol) + if (tocol == vcol) { tocol += n_extra; + } // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. if (cul_attr) { char_attr = hl_combine_attr(cul_attr, char_attr); @@ -2930,7 +2977,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (saved_n_extra) { - /* Continue item from end of wrapped line. */ + // Continue item from end of wrapped line. n_extra = saved_n_extra; c_extra = saved_c_extra; c_final = saved_c_final; @@ -2984,20 +3031,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && vcol == 0 && n_extra == 0 && row == startrow) { - char_attr = win_hl_attr(wp, HLF_FL); + char_attr = win_hl_attr(wp, HLF_FL); - linenr_T lnume = lnum + foldinfo.fi_lines - 1; - memset(buf_fold, ' ', FOLD_TEXT_LEN); - p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); - n_extra = STRLEN(p_extra); + linenr_T lnume = lnum + foldinfo.fi_lines - 1; + memset(buf_fold, ' ', FOLD_TEXT_LEN); + p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); + n_extra = STRLEN(p_extra); - if (p_extra != buf_fold) { - xfree(p_extra_free); - p_extra_free = p_extra; - } - c_extra = NUL; - c_final = NUL; - p_extra[n_extra] = NUL; + if (p_extra != buf_fold) { + xfree(p_extra_free); + p_extra_free = p_extra; + } + c_extra = NUL; + c_final = NUL; + p_extra[n_extra] = NUL; } if (draw_state == WL_LINE @@ -3038,7 +3085,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && (colnr_T)vcol == wp->w_virtcol))) { area_attr = 0; // stop highlighting area_active = false; - } + } if (!n_extra) { /* @@ -3063,10 +3110,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (cur != NULL) { cur->pos.cur = 0; } - bool pos_inprogress = true; // mark that a position match search is - // in progress + bool pos_inprogress = true; // mark that a position match search is + // in progress while (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress)) { + || (cur != NULL && pos_inprogress)) { if (shl->startcol != MAXCOL && v >= (long)shl->startcol && v < (long)shl->endcol) { @@ -3100,10 +3147,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (shl->lnum == lnum) { shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) + if (shl->rm.endpos[0].lnum == 0) { shl->endcol = shl->rm.endpos[0].col; - else + } else { shl->endcol = MAXCOL; + } if (shl->startcol == shl->endcol) { // highlight empty match, try again after it @@ -3117,8 +3165,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } break; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } /* Use attributes from match with highest priority among @@ -3139,8 +3188,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, search_attr = shl->attr_cur; search_attr_from_match = shl != &search_hl; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } // Only highlight one character after the last column. if (*ptr == NUL @@ -3403,8 +3453,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (did_emsg) { wp->w_s->b_syn_error = TRUE; has_syntax = FALSE; - } else + } else { did_emsg = save_did_emsg; + } // Need to get the line again, a multi-line regexp may // have made it invalid. @@ -3484,9 +3535,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, checked_col = (int)((p - nextline) + len - nextline_idx); } - /* Turn index into actual attributes. */ - if (spell_hlf != HLF_COUNT) + // Turn index into actual attributes. + if (spell_hlf != HLF_COUNT) { spell_attr = highlight_attr[spell_hlf]; + } if (cap_col > 0) { if (p != prev_ptr @@ -3496,17 +3548,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, capcol_lnum = lnum + 1; cap_col = (int)((p - nextline) + cap_col - nextline_idx); - } else - /* Compute the actual column. */ + } else { + // Compute the actual column. cap_col += (int)(prev_ptr - line); + } } } } if (spell_attr != 0) { - if (!attr_pri) + if (!attr_pri) { char_attr = hl_combine_attr(char_attr, spell_attr); - else + } else { char_attr = hl_combine_attr(spell_attr, char_attr); + } } if (wp->w_buffer->terminal) { @@ -3541,9 +3595,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; c_final = NUL; if (ascii_iswhite(c)) { - if (c == TAB) - /* See "Tab alignment" below. */ + if (c == TAB) { + // See "Tab alignment" below. FIX_FOR_BOGUSCOLS; + } if (!wp->w_p_list) { c = ' '; } @@ -3690,7 +3745,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1; if (wp->w_p_lbr) { - c_extra = NUL; /* using p_extra from above */ + c_extra = NUL; // using p_extra from above } else { c_extra = wp->w_p_lcs_chars.tab2; } @@ -3756,10 +3811,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } else if (c != NUL) { p_extra = transchar_buf(wp->w_buffer, c); if (n_extra == 0) { - n_extra = byte2cells(c) - 1; + n_extra = byte2cells(c) - 1; + } + if ((dy_flags & DY_UHEX) && wp->w_p_rl) { + rl_mirror(p_extra); // reverse "<12>" } - if ((dy_flags & DY_UHEX) && wp->w_p_rl) - rl_mirror(p_extra); /* reverse "<12>" */ c_extra = NUL; c_final = NUL; if (wp->w_p_lbr) { @@ -3817,8 +3873,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, prev_syntax_id = syntax_seqnr; - if (n_extra > 0) + if (n_extra > 0) { vcol_off += n_extra; + } vcol += n_extra; if (wp->w_p_wrap && n_extra > 0) { if (wp->w_p_rl) { @@ -3944,8 +4001,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int n = 0; if (wp->w_p_rl) { - if (col < 0) + if (col < 0) { n = 1; + } } else { if (col >= grid->Columns) { n = -1; @@ -4006,16 +4064,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, v = wp->w_leftcol; } - /* check if line ends before left margin */ - if (vcol < v + col - win_col_off(wp)) + // check if line ends before left margin + if (vcol < v + col - win_col_off(wp)) { vcol = v + col - win_col_off(wp); + } // Get rid of the boguscols now, we want to draw until the right // edge for 'cursorcolumn'. col -= boguscols; // boguscols = 0; // Disabled because value never read after this - if (draw_color_col) + if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + } bool has_virttext = false; // Make sure alignment is the same regardless @@ -4226,7 +4286,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, tocol++; } if (wp->w_p_rl) { - /* now it's time to backup one cell */ + // now it's time to backup one cell --off; --col; } @@ -4241,8 +4301,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } else if (wp->w_p_cole > 0 && is_concealing) { --n_skip; ++vcol_off; - if (n_extra > 0) + if (n_extra > 0) { vcol_off += n_extra; + } if (wp->w_p_wrap) { /* * Special voodoo required if 'wrap' is on. @@ -4296,27 +4357,30 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, n_attr = 0; } } - - } else + } else { --n_skip; + } /* Only advance the "vcol" when after the 'number' or 'relativenumber' * column. */ if (draw_state > WL_NR - && filler_todo <= 0 - ) + && filler_todo <= 0) { ++vcol; + } - if (vcol_save_attr >= 0) + if (vcol_save_attr >= 0) { char_attr = vcol_save_attr; + } - /* restore attributes after "predeces" in 'listchars' */ - if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) + // restore attributes after "predeces" in 'listchars' + if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) { char_attr = saved_attr3; + } - /* restore attributes after last 'listchars' or 'number' char */ - if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) + // restore attributes after last 'listchars' or 'number' char + if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) { char_attr = saved_attr2; + } /* * At end of screen line and there is more to come: Display the line @@ -4328,15 +4392,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, || filler_todo > 0 || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && p_extra != at_end_str) - || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) - ) { + || (n_extra != 0 && + (c_extra != NUL || *p_extra != NUL)))) { bool wrap = wp->w_p_wrap // Wrapping enabled. - && filler_todo <= 0 // Not drawing diff filler lines. - && lcs_eol_one != -1 // Haven't printed the lcs_eol character. - && row != endrow - 1 // Not the last line being displayed. - && (grid->Columns == Columns // Window spans the width of the screen, - || ui_has(kUIMultigrid)) // or has dedicated grid. - && !wp->w_p_rl; // Not right-to-left. + && filler_todo <= 0 // Not drawing diff filler lines. + && lcs_eol_one != -1 // Haven't printed the lcs_eol character. + && row != endrow - 1 // Not the last line being displayed. + && (grid->Columns == Columns // Window spans the width of the screen, + || ui_has(kUIMultigrid)) // or has dedicated grid. + && !wp->w_p_rl; // Not right-to-left. int draw_col = col - boguscols; draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); @@ -4361,8 +4425,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * '$' and highlighting until last column, break here. */ if ((!wp->w_p_wrap && filler_todo <= 0 - ) || lcs_eol_one == -1) + ) || lcs_eol_one == -1) { break; + } // When the window is too narrow draw all "@" lines. if (draw_state != WL_LINE && filler_todo <= 0) { @@ -4370,7 +4435,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, row = endrow; } - /* When line got too long for screen break here. */ + // When line got too long for screen break here. if (row == endrow) { ++row; break; @@ -4383,7 +4448,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, off += col; } - /* reset the drawing state for the start of a wrapped line */ + // reset the drawing state for the start of a wrapped line draw_state = WL_START; saved_n_extra = n_extra; saved_p_extra = p_extra; @@ -4398,14 +4463,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, filler_todo--; // When the filler lines are actually below the last line of the // file, don't draw the line itself, break here. - if (filler_todo == 0 && wp->w_botfill) { + if (filler_todo == 0 && (wp->w_botfill || end_fill)) { break; } } + } // for every character in the line - } /* for every character in the line */ - - /* After an empty line check first word for capital. */ + // After an empty line check first word for capital. if (*skipwhite(line) == NUL) { capcol_lnum = lnum + 1; cap_col = 0; @@ -4511,25 +4575,11 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) // @param count max number of signs // @param[out] n_extrap number of characters from pp_extra to display // @param[in, out] sign_idxp Index of the displayed sign -static void get_sign_display_info( - bool nrcol, - win_T *wp, - sign_attrs_T sattrs[], - int row, - int startrow, - int filler_lines, - int filler_todo, - int count, - int *c_extrap, - int *c_finalp, - char_u *extra, - size_t extra_size, - char_u **pp_extra, - int *n_extrap, - int *char_attrp, - int *draw_statep, - int *sign_idxp -) +static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], int row, + int startrow, int filler_lines, int filler_todo, int count, + int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, + char_u **pp_extra, int *n_extrap, int *char_attrp, + int *draw_statep, int *sign_idxp) { // Draw cells with the sign value or blank. *c_extrap = ' '; @@ -4567,7 +4617,7 @@ static void get_sign_display_info( assert((size_t)win_signcol_width(wp) >= mb_string2cells(*pp_extra)); // symbol(s) bytes + (filling spaces) (one byte each) *n_extrap = symbol_blen + - (win_signcol_width(wp) - mb_string2cells(*pp_extra)); + (win_signcol_width(wp) - mb_string2cells(*pp_extra)); assert(extra_size > (size_t)symbol_blen); memset(extra, ' ', extra_size); @@ -4597,8 +4647,7 @@ static void get_sign_display_info( * - the character is multi-byte and the next byte is different * - the character is two cells wide and the second cell differs. */ -static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, - int cols) +static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, int cols) { return (cols > 0 && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to]) @@ -4620,9 +4669,8 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, /// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" /// If "wrap" is true, then hint to the UI that "row" contains a line /// which has wrapped into the next row. -static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, - int clear_width, int rlflag, win_T *wp, - int bg_attr, bool wrap) +static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width, + int rlflag, win_T *wp, int bg_attr, bool wrap) { unsigned off_from; unsigned off_to; @@ -4659,12 +4707,11 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, max_off_to = grid->line_offset[row] + grid->Columns; if (rlflag) { - /* Clear rest first, because it's left of the text. */ + // Clear rest first, because it's left of the text. if (clear_width > 0) { while (col <= endcol && grid->chars[off_to][0] == ' ' && grid->chars[off_to][1] == NUL - && grid->attrs[off_to] == bg_attr - ) { + && grid->attrs[off_to] == bg_attr) { ++off_to; ++col; } @@ -4805,7 +4852,6 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height) { wp->w_redr_status = true; @@ -4841,8 +4887,9 @@ void redraw_statuslines(void) win_redr_status(wp); } } - if (redraw_tabline) + if (redraw_tabline) { draw_tabline(); + } } /* @@ -4894,9 +4941,10 @@ static int status_match_len(expand_T *xp, char_u *s) int emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); - /* Check for menu separators - replace with '|'. */ - if (emenu && menu_is_separator(s)) + // Check for menu separators - replace with '|'. + if (emenu && menu_is_separator(s)) { return 1; + } while (*s != NUL) { s += skip_status_match_char(xp, s); @@ -4916,11 +4964,12 @@ static int skip_status_match_char(expand_T *xp, char_u *s) if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES) - && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL))) - ) { + && (s[0] == '\t' || + (s[0] == '\\' && s[1] != NUL)))) { #ifndef BACKSLASH_IN_FILENAME - if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') + if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') { return 2; + } #endif return 1; } @@ -4934,20 +4983,14 @@ static int skip_status_match_char(expand_T *xp, char_u *s) * * If inversion is possible we use it. Else '=' characters are used. */ -void -win_redr_status_matches ( - expand_T *xp, - int num_matches, - char_u **matches, /* list of matches */ - int match, - int showtail -) +void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, // list of matches + int match, int showtail) { #define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m]) int row; char_u *buf; int len; - int clen; /* length in screen cells */ + int clen; // length in screen cells int fillchar; int attr; int i; @@ -4961,33 +5004,36 @@ win_redr_status_matches ( int emenu; int l; - if (matches == NULL) /* interrupted completion? */ + if (matches == NULL) { // interrupted completion? return; + } buf = xmalloc(Columns * MB_MAXBYTES + 1); - if (match == -1) { /* don't show match but original text */ + if (match == -1) { // don't show match but original text match = 0; highlight = false; } - /* count 1 for the ending ">" */ + // count 1 for the ending ">" clen = status_match_len(xp, L_MATCH(match)) + 3; - if (match == 0) + if (match == 0) { first_match = 0; - else if (match < first_match) { - /* jumping left, as far as we can go */ + } else if (match < first_match) { + // jumping left, as far as we can go first_match = match; add_left = true; } else { - /* check if match fits on the screen */ - for (i = first_match; i < match; ++i) + // check if match fits on the screen + for (i = first_match; i < match; ++i) { clen += status_match_len(xp, L_MATCH(i)) + 2; - if (first_match > 0) + } + if (first_match > 0) { clen += 2; + } // jumping right, put match at the left if ((long)clen > Columns) { first_match = match; - /* if showing the last match, we can add some on the left */ + // if showing the last match, we can add some on the left clen = 2; for (i = match; i < num_matches; ++i) { clen += status_match_len(xp, L_MATCH(i)) + 2; @@ -5000,7 +5046,7 @@ win_redr_status_matches ( } } } - if (add_left) + if (add_left) { while (first_match > 0) { clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; if ((long)clen >= Columns) { @@ -5008,6 +5054,7 @@ win_redr_status_matches ( } first_match--; } + } fillchar = fillchar_status(&attr, curwin); @@ -5028,7 +5075,7 @@ win_redr_status_matches ( } s = L_MATCH(i); - /* Check for menu separators - replace with '|' */ + // Check for menu separators - replace with '|' emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); if (emenu && menu_is_separator(s)) { @@ -5036,7 +5083,7 @@ win_redr_status_matches ( l = (int)STRLEN(buf + len); len += l; clen += l; - } else + } else { for (; *s != NUL; ++s) { s += skip_status_match_char(xp, s); clen += ptr2cells(s); @@ -5049,14 +5096,17 @@ win_redr_status_matches ( len += (int)STRLEN(buf + len); } } - if (i == match) + } + if (i == match) { selend = buf + len; + } *(buf + len++) = ' '; *(buf + len++) = ' '; clen += 2; - if (++i == num_matches) + if (++i == num_matches) { break; + } } if (i != num_matches) { @@ -5145,7 +5195,7 @@ static void win_redr_status(win_T *wp) // popup menu is visible and may be drawn over it wp->w_redr_status = true; } else if (*p_stl != NUL || *wp->w_p_stl != NUL) { - /* redraw custom status line */ + // redraw custom status line redraw_custom_statusline(wp); } else { fillchar = fillchar_status(&attr, wp); @@ -5210,9 +5260,10 @@ static void win_redr_status(win_T *wp) this_ru_col + wp->w_wincol, fillchar, fillchar, attr); if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) - && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) + && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { grid_puts(&default_grid, NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); + } win_redr_ruler(wp, true); } @@ -5242,8 +5293,9 @@ static void redraw_custom_statusline(win_T *wp) /* When called recursively return. This can happen when the statusline * contains an expression that triggers a redraw. */ - if (entered) + if (entered) { return; + } entered = true; did_emsg = false; @@ -5271,8 +5323,9 @@ bool stl_connected(win_T *wp) fr = wp->w_frame; while (fr->fr_parent != NULL) { if (fr->fr_parent->fr_layout == FR_COL) { - if (fr->fr_next != NULL) + if (fr->fr_next != NULL) { break; + } } else { if (fr->fr_next != NULL) { return true; @@ -5304,7 +5357,7 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) curbuf = wp->w_buffer; curwin = wp; - STRCPY(buf, "b:keymap_name"); /* must be writable */ + STRCPY(buf, "b:keymap_name"); // must be writable ++emsg_skip; s = p = eval_to_string(buf, NULL, FALSE); --emsg_skip; @@ -5329,11 +5382,7 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) * Redraw the status line or ruler of window "wp". * When "wp" is NULL redraw the tab pages line from 'tabline'. */ -static void -win_redr_custom ( - win_T *wp, - bool draw_ruler -) +static void win_redr_custom(win_T *wp, bool draw_ruler) { static bool entered = false; int attr; @@ -5359,13 +5408,14 @@ win_redr_custom ( /* There is a tiny chance that this gets called recursively: When * redrawing a status line triggers redrawing the ruler or tabline. * Avoid trouble by not allowing recursion. */ - if (entered) + if (entered) { return; + } entered = true; - /* setup environment for the task at hand */ + // setup environment for the task at hand if (wp == NULL) { - /* Use 'tabline'. Always at the first line of the screen. */ + // Use 'tabline'. Always at the first line of the screen. stl = p_tal; row = 0; fillchar = ' '; @@ -5379,15 +5429,19 @@ win_redr_custom ( if (draw_ruler) { stl = p_ruf; - /* advance past any leading group spec - implicit in ru_col */ + // advance past any leading group spec - implicit in ru_col if (*stl == '%') { - if (*++stl == '-') + if (*++stl == '-') { stl++; - if (atoi((char *)stl)) - while (ascii_isdigit(*stl)) + } + if (atoi((char *)stl)) { + while (ascii_isdigit(*stl)) { stl++; - if (*stl++ != '(') + } + } + if (*stl++ != '(') { stl = p_ruf; + } } col = ru_col - (Columns - wp->w_width); if (col < (wp->w_width + 1) / 2) { @@ -5404,19 +5458,21 @@ win_redr_custom ( use_sandbox = was_set_insecurely(wp, (char_u *)"rulerformat", 0); } else { - if (*wp->w_p_stl != NUL) + if (*wp->w_p_stl != NUL) { stl = wp->w_p_stl; - else + } else { stl = p_stl; - use_sandbox = was_set_insecurely( - wp, (char_u *)"statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); + } + use_sandbox = was_set_insecurely(wp, (char_u *)"statusline", + *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } col += wp->w_wincol; } - if (maxwidth <= 0) + if (maxwidth <= 0) { goto theend; + } /* Temporarily reset 'cursorbind', we don't want a side effect from moving * the cursor away and back. */ @@ -5439,7 +5495,7 @@ win_redr_custom ( len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); - /* fill up with "fillchar" */ + // fill up with "fillchar" while (width < maxwidth && len < (int)sizeof(buf) - 1) { len += utf_char2bytes(fillchar, buf + len); width++; @@ -5459,14 +5515,15 @@ win_redr_custom ( col += vim_strnsize(p, textlen); p = hltab[n].start; - if (hltab[n].userhl == 0) + if (hltab[n].userhl == 0) { curattr = attr; - else if (hltab[n].userhl < 0) + } else if (hltab[n].userhl < 0) { curattr = syn_id2attr(-hltab[n].userhl); - else if (wp != NULL && wp != curwin && wp->w_status_height != 0) + } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) { curattr = highlight_stlnc[hltab[n].userhl - 1]; - else + } else { curattr = highlight_user[hltab[n].userhl - 1]; + } } // Make sure to use an empty string instead of p, if p is beyond buf + len. grid_puts(grid, p >= buf + len ? (char_u *)"" : p, row, col, @@ -5483,11 +5540,11 @@ win_redr_custom ( .type = kStlClickDisabled, }; for (n = 0; tabtab[n].start != NULL; n++) { - len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p)); + len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p)); while (col < len) { tab_page_click_defs[col++] = cur_click_def; } - p = (char_u *) tabtab[n].start; + p = (char_u *)tabtab[n].start; cur_click_def = tabtab[n].def; } while (col < Columns) { @@ -5651,8 +5708,7 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) /// get a single character directly from grid.chars into "bytes[]". /// Also return its attribute in *attrp; -void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, - int *attrp) +void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp) { unsigned off; @@ -5699,19 +5755,18 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr) assert(put_dirty_row == row); unsigned int off = grid->line_offset[row] + col; if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) { - schar_copy(grid->chars[off], schar); - grid->attrs[off] = attr; + schar_copy(grid->chars[off], schar); + grid->attrs[off] = attr; - put_dirty_first = MIN(put_dirty_first, col); - // TODO(bfredl): Y U NO DOUBLEWIDTH? - put_dirty_last = MAX(put_dirty_last, col+1); + put_dirty_first = MIN(put_dirty_first, col); + // TODO(bfredl): Y U NO DOUBLEWIDTH? + put_dirty_last = MAX(put_dirty_last, col+1); } } /// like grid_puts(), but output "text[len]". When "len" is -1 output up to /// a NUL. -void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, - int col, int attr) +void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr) { unsigned off; char_u *ptr = text; @@ -5723,7 +5778,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int u8c = 0; int u8cc[MAX_MCO]; int clear_next_cell = FALSE; - int prev_c = 0; /* previous Arabic character */ + int prev_c = 0; // previous Arabic character int pc, nc, nc1; int pcc[MAX_MCO]; int need_redraw; @@ -5816,7 +5871,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, if (clear_next_cell) { clear_next_cell = false; } else if ((len < 0 ? ptr[mbyte_blen] == NUL - : ptr + mbyte_blen >= text + len) + : ptr + mbyte_blen >= text + len) && ((mbyte_cells == 1 && grid_off2cells(grid, off, max_off) > 1) || (mbyte_cells == 2 @@ -5915,10 +5970,11 @@ static void init_search_hl(win_T *wp) matchitem_T *cur = wp->w_match_head; while (cur != NULL) { cur->hl.rm = cur->match; - if (cur->hlg_id == 0) + if (cur->hlg_id == 0) { cur->hl.attr = 0; - else + } else { cur->hl.attr = syn_id2attr(cur->hlg_id); + } cur->hl.buf = wp->w_buffer; cur->hl.lnum = 0; cur->hl.first_lnum = 0; @@ -5972,8 +6028,8 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) if (cur != NULL) { cur->pos.cur = 0; } - bool pos_inprogress = true; // mark that a position match search is - // in progress + bool pos_inprogress = true; // mark that a position match search is + // in progress int n = 0; while (shl->first_lnum < lnum && (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))) { @@ -5991,8 +6047,9 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) } } } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } } @@ -6004,14 +6061,10 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) * shl->lnum is zero. * Careful: Any pointers for buffer lines will become invalid. */ -static void -next_search_hl ( - win_T *win, - match_T *shl, /* points to search_hl or a match */ - linenr_T lnum, - colnr_T mincol, /* minimal column for a match */ - matchitem_T *cur /* to retrieve match positions if any */ -) +static void next_search_hl(win_T *win, match_T *shl, // points to search_hl or a match + linenr_T lnum, colnr_T mincol, // minimal column for a match + matchitem_T *cur // to retrieve match positions if any + ) FUNC_ATTR_NONNULL_ARG(2) { linenr_T l; @@ -6031,10 +6084,11 @@ next_search_hl ( // 2. If the previous match includes "mincol", use it. // 3. Continue after the previous match. l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) + if (lnum > l) { shl->lnum = 0; - else if (lnum < l || shl->rm.endpos[0].col > mincol) + } else if (lnum < l || shl->rm.endpos[0].col > mincol) { return; + } } /* @@ -6045,7 +6099,7 @@ next_search_hl ( for (;; ) { // Stop searching after passing the time limit. if (profile_passed_limit(shl->tm)) { - shl->lnum = 0; /* no match found in time */ + shl->lnum = 0; // no match found in time break; } // Three situations: @@ -6097,7 +6151,7 @@ next_search_hl ( } shl->rm.regprog = NULL; shl->lnum = 0; - got_int = FALSE; // avoid the "Type :quit to exit Vim" message + got_int = FALSE; // avoid the "Type :quit to exit Vim" message break; } } else if (cur != NULL) { @@ -6122,13 +6176,10 @@ next_search_hl ( /// If there is a match fill "shl" and return one. /// Return zero otherwise. -static int -next_search_hl_pos( - match_T *shl, // points to a match - linenr_T lnum, - posmatch_T *posmatch, // match positions - colnr_T mincol // minimal column for a match -) +static int next_search_hl_pos(match_T *shl, // points to a match + linenr_T lnum, posmatch_T *posmatch, // match positions + colnr_T mincol // minimal column for a match + ) FUNC_ATTR_NONNULL_ALL { int i; @@ -6182,8 +6233,8 @@ next_search_hl_pos( /// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' /// with character 'c1' in first column followed by 'c2' in the other columns. /// Use attributes 'attr'. -void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, - int end_col, int c1, int c2, int attr) +void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, + int c2, int attr) { schar_T sc; @@ -6421,9 +6472,9 @@ retry: msg_grid_invalid = true; } - win_new_shellsize(); /* fit the windows in the new sized shell */ + win_new_shellsize(); // fit the windows in the new sized shell - comp_col(); /* recompute columns for shown command and ruler */ + comp_col(); // recompute columns for shown command and ruler // We're changing the size of the screen. // - Allocate new arrays for default_grid @@ -6436,8 +6487,8 @@ retry: // size is wrong. grid_alloc(&default_grid, Rows, Columns, true, true); - StlClickDefinition *new_tab_page_click_defs = xcalloc( - (size_t)Columns, sizeof(*new_tab_page_click_defs)); + StlClickDefinition *new_tab_page_click_defs = + xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs)); clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); xfree(tab_page_click_defs); @@ -6545,8 +6596,7 @@ void screen_free_all_mem(void) /// /// @param[out] tpcd Table to clear. /// @param[in] tpcd_size Size of the table. -void clear_tab_page_click_defs(StlClickDefinition *const tpcd, - const long tpcd_size) +void clear_tab_page_click_defs(StlClickDefinition *const tpcd, const long tpcd_size) { if (tpcd != NULL) { for (long i = 0; i < tpcd_size; i++) { @@ -6554,7 +6604,7 @@ void clear_tab_page_click_defs(StlClickDefinition *const tpcd, xfree(tpcd[i].func); } } - memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0])); + memset(tpcd, 0, (size_t)tpcd_size * sizeof(tpcd[0])); } } @@ -6659,8 +6709,8 @@ void setcursor(void) // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. col = curwin->w_width_inner - curwin->w_wcol - - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 - && vim_isprintc(gchar_cursor())) ? 2 : 1); + - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 + && vim_isprintc(gchar_cursor())) ? 2 : 1); } screen_adjust_grid(&grid, &row, &col); @@ -6709,8 +6759,7 @@ void win_scroll_lines(win_T *wp, int row, int line_count) /// 'col' is the column from with we start inserting. // /// 'row', 'col' and 'end' are relative to the start of the region. -void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, - int width) +void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width) { int i; int j; @@ -6761,8 +6810,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, /// 'end' is the line after the scrolled part. Normally it is Rows. /// When scrolling region used 'off' is the offset from the top for the region. /// 'row' and 'end' are relative to the start of the region. -void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, - int width) +void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width) { int j; int i; @@ -6850,16 +6898,16 @@ int showmode(void) bool nwr_save = need_wait_return; - /* wait a bit before overwriting an important message */ + // wait a bit before overwriting an important message check_for_delay(FALSE); - /* if the cmdline is more than one line high, erase top lines */ + // if the cmdline is more than one line high, erase top lines need_clear = clear_cmdline; if (clear_cmdline && cmdline_row < Rows - 1) { msg_clr_cmdline(); // will reset clear_cmdline } - /* Position on the last line in the window, column 0 */ + // Position on the last line in the window, column 0 msg_pos_mode(); attr = HL_ATTR(HLF_CM); // Highlight mode @@ -6885,8 +6933,9 @@ int showmode(void) length -= vim_strsize(edit_submode_extra); } if (length > 0) { - if (edit_submode_pre != NULL) + if (edit_submode_pre != NULL) { length -= vim_strsize(edit_submode_pre); + } if (length - vim_strsize(edit_submode) > 0) { if (edit_submode_pre != NULL) { msg_puts_attr((const char *)edit_submode_pre, attr); @@ -6906,13 +6955,14 @@ int showmode(void) } else { if (State & TERM_FOCUS) { MSG_PUTS_ATTR(_(" TERMINAL"), attr); - } else if (State & VREPLACE_FLAG) + } else if (State & VREPLACE_FLAG) { MSG_PUTS_ATTR(_(" VREPLACE"), attr); - else if (State & REPLACE_FLAG) + } else if (State & REPLACE_FLAG) { MSG_PUTS_ATTR(_(" REPLACE"), attr); - else if (State & INSERT) { - if (p_ri) + } else if (State & INSERT) { + if (p_ri) { MSG_PUTS_ATTR(_(" REVERSE"), attr); + } MSG_PUTS_ATTR(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a') { @@ -6933,8 +6983,9 @@ int showmode(void) MSG_PUTS_ATTR(NameBuff, attr); } } - if ((State & INSERT) && p_paste) + if ((State & INSERT) && p_paste) { MSG_PUTS_ATTR(_(" (paste)"), attr); + } if (VIsual_active) { char *p; @@ -6983,9 +7034,10 @@ int showmode(void) // NB: also handles clearing the showmode if it was empty or disabled msg_ext_flush_showmode(); - /* In Visual mode the size of the selected area must be redrawn. */ - if (VIsual_active) + // In Visual mode the size of the selected area must be redrawn. + if (VIsual_active) { clear_showcmd(); + } // If the last window has no status line, the ruler is after the mode // message and must be redrawn @@ -7082,15 +7134,16 @@ void draw_tabline(void) return; } - if (tabline_height() < 1) + if (tabline_height() < 1) { return; + } // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. assert(Columns == tab_page_click_defs_size); clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); - /* Use the 'tabline' option if it's set. */ + // Use the 'tabline' option if it's set. if (*p_tal != NUL) { int saved_did_emsg = did_emsg; @@ -7176,7 +7229,7 @@ void draw_tabline(void) room = scol - col + tabwidth - 1; if (room > 0) { - /* Get buffer name in NameBuff[] */ + // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); (void)shorten_dir(NameBuff); len = vim_strsize(NameBuff); @@ -7206,13 +7259,14 @@ void draw_tabline(void) } } - if (use_sep_chars) + if (use_sep_chars) { c = '_'; - else + } else { c = ' '; + } grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); - /* Put an "X" for closing the current tab if there are several. */ + // Put an "X" for closing the current tab if there are several. if (first_tabpage->tp_next != NULL) { grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); tab_page_click_defs[Columns - 1] = (StlClickDefinition) { @@ -7267,10 +7321,11 @@ void ui_ext_tabline_update(void) */ void get_trans_bufname(buf_T *buf) { - if (buf_spname(buf) != NULL) + if (buf_spname(buf) != NULL) { STRLCPY(NameBuff, buf_spname(buf), MAXPATHL); - else + } else { home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); + } trans_characters(NameBuff, MAXPATHL); } @@ -7334,8 +7389,9 @@ int messaging(void) /// @param always if false, only show ruler if position has changed. void showruler(bool always) { - if (!always && !redrawing()) + if (!always && !redrawing()) { return; + } if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) { redraw_custom_statusline(curwin); } else { @@ -7344,12 +7400,13 @@ void showruler(bool always) if (need_maketitle || (p_icon && (stl_syntax & STL_IN_ICON)) - || (p_title && (stl_syntax & STL_IN_TITLE)) - ) + || (p_title && (stl_syntax & STL_IN_TITLE))) { maketitle(); - /* Redraw the tab pages line if needed. */ - if (redraw_tabline) + } + // Redraw the tab pages line if needed. + if (redraw_tabline) { draw_tabline(); + } } static void win_redr_ruler(win_T *wp, bool always) @@ -7365,8 +7422,9 @@ static void win_redr_ruler(win_T *wp, bool always) * Check if cursor.lnum is valid, since win_redr_ruler() may be called * after deleting lines, before cursor.lnum is corrected. */ - if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { return; + } // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. @@ -7394,24 +7452,24 @@ static void win_redr_ruler(win_T *wp, bool always) */ int empty_line = FALSE; if (!(State & INSERT) - && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) + && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) { empty_line = TRUE; + } /* * Only draw the ruler when something changed. */ validate_virtcol_win(wp); - if ( redraw_cmdline - || always - || wp->w_cursor.lnum != wp->w_ru_cursor.lnum - || wp->w_cursor.col != wp->w_ru_cursor.col - || wp->w_virtcol != wp->w_ru_virtcol - || wp->w_cursor.coladd != wp->w_ru_cursor.coladd - || wp->w_topline != wp->w_ru_topline - || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count - || wp->w_topfill != wp->w_ru_topfill - || empty_line != wp->w_ru_empty) { - + if (redraw_cmdline + || always + || wp->w_cursor.lnum != wp->w_ru_cursor.lnum + || wp->w_cursor.col != wp->w_ru_cursor.col + || wp->w_virtcol != wp->w_ru_virtcol + || wp->w_cursor.coladd != wp->w_ru_cursor.coladd + || wp->w_topline != wp->w_ru_topline + || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count + || wp->w_topfill != wp->w_ru_topfill + || empty_line != wp->w_ru_empty) { int width; int row; int fillchar; @@ -7449,12 +7507,12 @@ static void win_redr_ruler(win_T *wp, bool always) * To avoid portability problems we use strlen() here. */ vim_snprintf((char *)buffer, RULER_BUF_LEN, "%" PRId64 ",", - (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L - : (int64_t)wp->w_cursor.lnum); + (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L + : (int64_t)wp->w_cursor.lnum); size_t len = STRLEN(buffer); col_print(buffer + len, RULER_BUF_LEN - len, - empty_line ? 0 : (int)wp->w_cursor.col + 1, - (int)virtcol + 1); + empty_line ? 0 : (int)wp->w_cursor.col + 1, + (int)virtcol + 1); /* * Add a "50%" if there is room for it. @@ -7542,8 +7600,9 @@ int number_width(win_T *wp) lnum = wp->w_buffer->b_ml.ml_line_count; } - if (lnum == wp->w_nrwidth_line_count) + if (lnum == wp->w_nrwidth_line_count) { return wp->w_nrwidth_width; + } wp->w_nrwidth_line_count = lnum; n = 0; @@ -7622,11 +7681,12 @@ void screen_resize(int width, int height) return; } - if (width < 0 || height < 0) /* just checking... */ + if (width < 0 || height < 0) { // just checking... return; + } if (State == HITRETURN || State == SETWSIZE) { - /* postpone the resizing */ + // postpone the resizing State = SETWSIZE; return; } @@ -7635,8 +7695,9 @@ void screen_resize(int width, int height) * buffer has already been closed and removing a scrollbar causes a resize * event. Don't resize then, it will happen after entering another buffer. */ - if (curwin->w_buffer == NULL) + if (curwin->w_buffer == NULL) { return; + } recursive = true; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index bdbbc4aacf..e5d4752760 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6028,6 +6028,8 @@ static const char *highlight_init_both[] = { "VertSplit cterm=reverse gui=reverse", "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", "default link EndOfBuffer NonText", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", "default link QuickFixLine Search", "default link Substitute Search", "default link Whitespace NonText", diff --git a/src/nvim/tag.c b/src/nvim/tag.c index a971849f4c..61d48eb4bf 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -3061,24 +3061,26 @@ expand_tags ( ) { int i; - int c; - int tagnmflag; - char_u tagnm[100]; + int extra_flag; + char_u *name_buf; + size_t name_buf_size = 100; tagptrs_T t_p; int ret; - if (tagnames) - tagnmflag = TAG_NAMES; - else - tagnmflag = 0; + name_buf = xmalloc(name_buf_size); + + if (tagnames) { + extra_flag = TAG_NAMES; + } else { + extra_flag = 0; + } if (pat[0] == '/') { ret = find_tags(pat + 1, num_file, file, - TAG_REGEXP | tagnmflag | TAG_VERBOSE | TAG_NO_TAGFUNC, + TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC, TAG_MANY, curbuf->b_ffname); } else { ret = find_tags(pat, num_file, file, - TAG_REGEXP | tagnmflag | TAG_VERBOSE - | TAG_NO_TAGFUNC | TAG_NOIC, + TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC, TAG_MANY, curbuf->b_ffname); } if (ret == OK && !tagnames) { @@ -3086,18 +3088,29 @@ expand_tags ( * "<tagname>\0<kind>\0<filename>\0" */ for (i = 0; i < *num_file; i++) { + size_t len; + parse_match((*file)[i], &t_p); - c = (int)(t_p.tagname_end - t_p.tagname); - memmove(tagnm, t_p.tagname, (size_t)c); - tagnm[c++] = 0; - tagnm[c++] = (t_p.tagkind != NULL && *t_p.tagkind) - ? *t_p.tagkind : 'f'; - tagnm[c++] = 0; - memmove((*file)[i] + c, t_p.fname, t_p.fname_end - t_p.fname); - (*file)[i][c + (t_p.fname_end - t_p.fname)] = 0; - memmove((*file)[i], tagnm, (size_t)c); + len = t_p.tagname_end - t_p.tagname; + if (len > name_buf_size - 3) { + char_u *buf; + + name_buf_size = len + 3; + buf = xrealloc(name_buf, name_buf_size); + name_buf = buf; + } + + memmove(name_buf, t_p.tagname, len); + name_buf[len++] = 0; + name_buf[len++] = (t_p.tagkind != NULL && *t_p.tagkind) + ? *t_p.tagkind : 'f'; + name_buf[len++] = 0; + memmove((*file)[i] + len, t_p.fname, t_p.fname_end - t_p.fname); + (*file)[i][len + (t_p.fname_end - t_p.fname)] = 0; + memmove((*file)[i], name_buf, len); } } + xfree(name_buf); return ret; } diff --git a/src/nvim/testdir/sautest/autoload/foo.vim b/src/nvim/testdir/sautest/autoload/foo.vim index d7dcd5ce3d..298e7275d8 100644 --- a/src/nvim/testdir/sautest/autoload/foo.vim +++ b/src/nvim/testdir/sautest/autoload/foo.vim @@ -5,3 +5,7 @@ let foo#bar = {} func foo#bar.echo() let g:called_foo_bar_echo += 1 endfunc + +func foo#addFoo(head) + return a:head .. 'foo' +endfunc diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index a1ef8325ec..01d8f32893 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -90,8 +90,8 @@ func Test_argadd_empty_curbuf() call assert_equal('', bufname('%')) call assert_equal(1, line('$')) rew - call assert_notequal(curbuf, bufnr('%')) - call assert_equal('Xargadd', bufname('%')) + call assert_notequal(curbuf, '%'->bufnr()) + call assert_equal('Xargadd', '%'->bufname()) call assert_equal(2, line('$')) %argd diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 1d114221dc..52f243aaea 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -7,7 +7,7 @@ func Test_assert_equalfile() let goodtext = ["one", "two", "three"] call writefile(goodtext, 'Xone') - call assert_equal(1, assert_equalfile('Xone', 'xyzxyz')) + call assert_equal(1, 'Xone'->assert_equalfile('xyzxyz')) call assert_match("E485: Can't read file xyzxyz", v:errors[0]) call remove(v:errors, 0) diff --git a/src/nvim/testdir/test_autoload.vim b/src/nvim/testdir/test_autoload.vim index 7396c227c9..b8c4fa251f 100644 --- a/src/nvim/testdir/test_autoload.vim +++ b/src/nvim/testdir/test_autoload.vim @@ -8,6 +8,8 @@ func Test_autoload_dict_func() call g:foo#bar.echo() call assert_equal(1, g:loaded_foo_vim) call assert_equal(1, g:called_foo_bar_echo) + + eval 'bar'->g:foo#addFoo()->assert_equal('barfoo') endfunc func Test_source_autoload() diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index e038bce08e..b4e8a0bc71 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -102,7 +102,7 @@ func Test_deletebufline() call assert_equal(0, deletebufline(b, 2, 8)) call assert_equal(['aaa'], getbufline(b, 1, 2)) exe "bd!" b - call assert_equal(1, deletebufline(b, 1)) + call assert_equal(1, b->deletebufline(1)) split Xtest call setline(1, ['a', 'b', 'c']) @@ -131,11 +131,11 @@ func Test_appendbufline_redraw() endif let lines =<< trim END new foo - let winnr=bufwinnr('foo') - let buf=bufnr('foo') + let winnr = 'foo'->bufwinnr() + let buf = bufnr('foo') wincmd p call appendbufline(buf, '$', range(1,200)) - exe winnr. 'wincmd w' + exe winnr .. 'wincmd w' norm! G wincmd p call deletebufline(buf, 1, '$') diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index cb7ab44798..4b5b55e6bf 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -18,7 +18,7 @@ function Test_getbufwintabinfo() let l = getbufinfo('%') call assert_equal(bufnr('%'), l[0].bufnr) call assert_equal('vim', l[0].variables.editor) - call assert_notequal(-1, index(l[0].windows, bufwinid('%'))) + call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) " Test for getbufinfo() with 'bufmodified' call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index b6c2d1467e..562867f548 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -118,6 +118,16 @@ b = something(); bw! endfunc +func Test_cindent_func() + new + setlocal cindent + call setline(1, ['int main(void)', '{', 'return 0;', '}']) + call assert_equal(-1, cindent(0)) + call assert_equal(&sw, 3->cindent()) + call assert_equal(-1, cindent(line('$')+1)) + bwipe! +endfunc + " this was going beyond the end of the line. func Test_cindent_case() new diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index f39cda7663..efa7f552e0 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -725,7 +725,7 @@ func Test_diff_filler() diffthis redraw - call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()')) wincmd w call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) @@ -741,16 +741,16 @@ func Test_diff_hlID() diffthis redraw - call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "") + call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) - call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") + call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange") call assert_equal(diff_hlID(1, 2), hlID("DiffText")) - call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText") - call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") + call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(2, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(3, 1), hlID("DiffAdd")) - call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd") - call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "") + call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd") + call diff_hlID(4, 1)->synIDattr("name")->assert_equal("") wincmd w call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim index 154ef570e0..78675d7016 100644 --- a/src/nvim/testdir/test_float_func.vim +++ b/src/nvim/testdir/test_float_func.vim @@ -7,6 +7,8 @@ end func Test_abs() call assert_equal('1.23', string(abs(1.23))) call assert_equal('1.23', string(abs(-1.23))) + eval -1.23->abs()->string()->assert_equal('1.23') + call assert_equal('0.0', string(abs(0.0))) call assert_equal('0.0', string(abs(1.0/(1.0/0.0)))) call assert_equal('0.0', string(abs(-1.0/(1.0/0.0)))) @@ -22,6 +24,7 @@ endfunc func Test_sqrt() call assert_equal('0.0', string(sqrt(0.0))) call assert_equal('1.414214', string(sqrt(2.0))) + eval 2.0->sqrt()->string()->assert_equal('1.414214') call assert_equal("str2float('inf')", string(sqrt(1.0/0.0))) call assert_equal("str2float('nan')", string(sqrt(-1.0))) call assert_equal("str2float('nan')", string(sqrt(0.0/0.0))) @@ -31,6 +34,7 @@ endfunc func Test_log() call assert_equal('0.0', string(log(1.0))) call assert_equal('-0.693147', string(log(0.5))) + eval 0.5->log()->string()->assert_equal('-0.693147') call assert_equal("-str2float('inf')", string(log(0.0))) call assert_equal("str2float('nan')", string(log(-1.0))) call assert_equal("str2float('inf')", string(log(1.0/0.0))) @@ -42,6 +46,7 @@ func Test_log10() call assert_equal('0.0', string(log10(1.0))) call assert_equal('2.0', string(log10(100.0))) call assert_equal('2.079181', string(log10(120.0))) + eval 120.0->log10()->string()->assert_equal('2.079181') call assert_equal("-str2float('inf')", string(log10(0.0))) call assert_equal("str2float('nan')", string(log10(-1.0))) call assert_equal("str2float('inf')", string(log10(1.0/0.0))) @@ -53,6 +58,7 @@ func Test_exp() call assert_equal('1.0', string(exp(0.0))) call assert_equal('7.389056', string(exp(2.0))) call assert_equal('0.367879', string(exp(-1.0))) + eval -1.0->exp()->string()->assert_equal('0.367879') call assert_equal("str2float('inf')", string(exp(1.0/0.0))) call assert_equal('0.0', string(exp(-1.0/0.0))) call assert_equal("str2float('nan')", string(exp(0.0/0.0))) @@ -63,6 +69,7 @@ func Test_sin() call assert_equal('0.0', string(sin(0.0))) call assert_equal('0.841471', string(sin(1.0))) call assert_equal('-0.479426', string(sin(-0.5))) + eval -0.5->sin()->string()->assert_equal('-0.479426') call assert_equal("str2float('nan')", string(sin(0.0/0.0))) call assert_equal("str2float('nan')", string(sin(1.0/0.0))) call assert_equal('0.0', string(sin(1.0/(1.0/0.0)))) @@ -73,6 +80,8 @@ endfunc func Test_asin() call assert_equal('0.0', string(asin(0.0))) call assert_equal('1.570796', string(asin(1.0))) + eval 1.0->asin()->string()->assert_equal('1.570796') + call assert_equal('-0.523599', string(asin(-0.5))) call assert_equal("str2float('nan')", string(asin(1.1))) call assert_equal("str2float('nan')", string(asin(1.0/0.0))) @@ -84,6 +93,7 @@ func Test_sinh() call assert_equal('0.0', string(sinh(0.0))) call assert_equal('0.521095', string(sinh(0.5))) call assert_equal('-1.026517', string(sinh(-0.9))) + eval -0.9->sinh()->string()->assert_equal('-1.026517') call assert_equal("str2float('inf')", string(sinh(1.0/0.0))) call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0))) call assert_equal("str2float('nan')", string(sinh(0.0/0.0))) @@ -94,6 +104,7 @@ func Test_cos() call assert_equal('1.0', string(cos(0.0))) call assert_equal('0.540302', string(cos(1.0))) call assert_equal('0.877583', string(cos(-0.5))) + eval -0.5->cos()->string()->assert_equal('0.877583') call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal("str2float('nan')", string(cos(1.0/0.0))) call assert_fails('call cos("")', 'E808:') @@ -103,6 +114,7 @@ func Test_acos() call assert_equal('1.570796', string(acos(0.0))) call assert_equal('0.0', string(acos(1.0))) call assert_equal('3.141593', string(acos(-1.0))) + eval -1.0->acos()->string()->assert_equal('3.141593') call assert_equal('2.094395', string(acos(-0.5))) call assert_equal("str2float('nan')", string(acos(1.1))) call assert_equal("str2float('nan')", string(acos(1.0/0.0))) @@ -113,6 +125,7 @@ endfunc func Test_cosh() call assert_equal('1.0', string(cosh(0.0))) call assert_equal('1.127626', string(cosh(0.5))) + eval 0.5->cosh()->string()->assert_equal('1.127626') call assert_equal("str2float('inf')", string(cosh(1.0/0.0))) call assert_equal("str2float('inf')", string(cosh(-1.0/0.0))) call assert_equal("str2float('nan')", string(cosh(0.0/0.0))) @@ -123,6 +136,7 @@ func Test_tan() call assert_equal('0.0', string(tan(0.0))) call assert_equal('0.546302', string(tan(0.5))) call assert_equal('-0.546302', string(tan(-0.5))) + eval -0.5->tan()->string()->assert_equal('-0.546302') call assert_equal("str2float('nan')", string(tan(1.0/0.0))) call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal('0.0', string(tan(1.0/(1.0/0.0)))) @@ -134,6 +148,7 @@ func Test_atan() call assert_equal('0.0', string(atan(0.0))) call assert_equal('0.463648', string(atan(0.5))) call assert_equal('-0.785398', string(atan(-1.0))) + eval -1.0->atan()->string()->assert_equal('-0.785398') call assert_equal('1.570796', string(atan(1.0/0.0))) call assert_equal('-1.570796', string(atan(-1.0/0.0))) call assert_equal("str2float('nan')", string(atan(0.0/0.0))) @@ -144,6 +159,7 @@ func Test_atan2() call assert_equal('-2.356194', string(atan2(-1, -1))) call assert_equal('2.356194', string(atan2(1, -1))) call assert_equal('0.0', string(atan2(1.0, 1.0/0.0))) + eval 1.0->atan2(1.0/0.0)->string()->assert_equal('0.0') call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0))) call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0))) call assert_fails('call atan2("", -1)', 'E808:') @@ -154,6 +170,7 @@ func Test_tanh() call assert_equal('0.0', string(tanh(0.0))) call assert_equal('0.462117', string(tanh(0.5))) call assert_equal('-0.761594', string(tanh(-1.0))) + eval -1.0->tanh()->string()->assert_equal('-0.761594') call assert_equal('1.0', string(tanh(1.0/0.0))) call assert_equal('-1.0', string(tanh(-1.0/0.0))) call assert_equal("str2float('nan')", string(tanh(0.0/0.0))) @@ -164,6 +181,7 @@ func Test_fmod() call assert_equal('0.13', string(fmod(12.33, 1.22))) call assert_equal('-0.13', string(fmod(-12.33, 1.22))) call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0))) + eval (1.0/0.0)->fmod(1.0)->string()->assert_equal("str2float('nan')") " On Windows we get "nan" instead of 1.0, accept both. let res = string(fmod(1.0, 1.0/0.0)) if res != "str2float('nan')" @@ -177,6 +195,7 @@ endfunc func Test_pow() call assert_equal('1.0', string(pow(0.0, 0.0))) call assert_equal('8.0', string(pow(2.0, 3.0))) + eval 2.0->pow(3.0)->string()->assert_equal('8.0') call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) @@ -192,6 +211,7 @@ func Test_str2float() call assert_equal('1.0', string(str2float(' 1.0 '))) call assert_equal('1.23', string(str2float('1.23'))) call assert_equal('1.23', string(str2float('1.23abc'))) + eval '1.23abc'->str2float()->string()->assert_equal('1.23') call assert_equal('1.0e40', string(str2float('1e40'))) call assert_equal('-1.23', string(str2float('-1.23'))) call assert_equal('1.23', string(str2float(' + 1.23 '))) @@ -228,6 +248,7 @@ func Test_float2nr() call assert_equal(1, float2nr(1.234)) call assert_equal(123, float2nr(1.234e2)) call assert_equal(12, float2nr(123.4e-1)) + eval 123.4e-1->float2nr()->assert_equal(12) let max_number = 1/0 let min_number = -max_number call assert_equal(max_number/2+1, float2nr(pow(2, 62))) @@ -242,6 +263,7 @@ func Test_floor() call assert_equal('2.0', string(floor(2.0))) call assert_equal('2.0', string(floor(2.11))) call assert_equal('2.0', string(floor(2.99))) + eval 2.99->floor()->string()->assert_equal('2.0') call assert_equal('-3.0', string(floor(-2.11))) call assert_equal('-3.0', string(floor(-2.99))) call assert_equal("str2float('nan')", string(floor(0.0/0.0))) @@ -255,6 +277,7 @@ func Test_ceil() call assert_equal('3.0', string(ceil(2.11))) call assert_equal('3.0', string(ceil(2.99))) call assert_equal('-2.0', string(ceil(-2.11))) + eval -2.11->ceil()->string()->assert_equal('-2.0') call assert_equal('-2.0', string(ceil(-2.99))) call assert_equal("str2float('nan')", string(ceil(0.0/0.0))) call assert_equal("str2float('inf')", string(ceil(1.0/0.0))) @@ -266,6 +289,7 @@ func Test_round() call assert_equal('2.0', string(round(2.1))) call assert_equal('3.0', string(round(2.5))) call assert_equal('3.0', string(round(2.9))) + eval 2.9->round()->string()->assert_equal('3.0') call assert_equal('-2.0', string(round(-2.1))) call assert_equal('-3.0', string(round(-2.5))) call assert_equal('-3.0', string(round(-2.9))) @@ -279,6 +303,7 @@ func Test_trunc() call assert_equal('2.0', string(trunc(2.1))) call assert_equal('2.0', string(trunc(2.5))) call assert_equal('2.0', string(trunc(2.9))) + eval 2.9->trunc()->string()->assert_equal('2.0') call assert_equal('-2.0', string(trunc(-2.1))) call assert_equal('-2.0', string(trunc(-2.5))) call assert_equal('-2.0', string(trunc(-2.9))) @@ -291,6 +316,7 @@ endfunc func Test_isinf() call assert_equal(1, isinf(1.0/0.0)) call assert_equal(-1, isinf(-1.0/0.0)) + eval (-1.0/0.0)->isinf()->assert_equal(-1) call assert_false(isinf(1.0)) call assert_false(isinf(0.0/0.0)) call assert_false(isinf('a')) @@ -302,6 +328,7 @@ func Test_isnan() call assert_true(isnan(0.0/0.0)) call assert_false(isnan(1.0)) call assert_false(isnan(1.0/0.0)) + eval (1.0/0.0)->isnan()->assert_false() call assert_false(isnan(-1.0/0.0)) call assert_false(isnan('a')) call assert_false(isnan([])) diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 48f97be96b..6cb3e24201 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -852,7 +852,7 @@ func Test_byte2line_line2byte() set fileformat=mac call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], - \ map(range(-1, 8), 'byte2line(v:val)')) + \ map(range(-1, 8), 'v:val->byte2line()')) call assert_equal([-1, -1, 1, 3, 6, 8, -1], \ map(range(-1, 5), 'line2byte(v:val)')) @@ -875,6 +875,34 @@ func Test_byte2line_line2byte() bw! endfunc +func Test_byteidx() + let a = '.é.' " one char of two bytes + call assert_equal(0, byteidx(a, 0)) + call assert_equal(0, byteidxcomp(a, 0)) + call assert_equal(1, byteidx(a, 1)) + call assert_equal(1, byteidxcomp(a, 1)) + call assert_equal(3, byteidx(a, 2)) + call assert_equal(3, byteidxcomp(a, 2)) + call assert_equal(4, byteidx(a, 3)) + call assert_equal(4, byteidxcomp(a, 3)) + call assert_equal(-1, byteidx(a, 4)) + call assert_equal(-1, byteidxcomp(a, 4)) + + let b = '.é.' " normal e with composing char + call assert_equal(0, b->byteidx(0)) + call assert_equal(1, b->byteidx(1)) + call assert_equal(4, b->byteidx(2)) + call assert_equal(5, b->byteidx(3)) + call assert_equal(-1, b->byteidx(4)) + + call assert_equal(0, b->byteidxcomp(0)) + call assert_equal(1, b->byteidxcomp(1)) + call assert_equal(2, b->byteidxcomp(2)) + call assert_equal(4, b->byteidxcomp(3)) + call assert_equal(5, b->byteidxcomp(4)) + call assert_equal(-1, b->byteidxcomp(5)) +endfunc + " Test for charidx() func Test_charidx() let a = 'xáb́y' @@ -1065,7 +1093,7 @@ func Test_col() call assert_equal(7, col('$')) call assert_equal(4, col("'x")) call assert_equal(6, col("'Y")) - call assert_equal(2, col([1, 2])) + call assert_equal(2, [1, 2]->col()) call assert_equal(7, col([1, '$'])) call assert_equal(0, col('')) @@ -1413,13 +1441,13 @@ func Test_bufadd_bufload() call assert_equal([''], getbufline(buf, 1, '$')) let curbuf = bufnr('') - call writefile(['some', 'text'], 'otherName') - let buf = bufadd('otherName') + call writefile(['some', 'text'], 'XotherName') + let buf = 'XotherName'->bufadd() call assert_notequal(0, buf) - call assert_equal(1, bufexists('otherName')) + eval 'XotherName'->bufexists()->assert_equal(1) call assert_equal(0, getbufvar(buf, '&buflisted')) call assert_equal(0, bufloaded(buf)) - call bufload(buf) + eval buf->bufload() call assert_equal(1, bufloaded(buf)) call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) call assert_equal(curbuf, bufnr('')) @@ -1439,8 +1467,9 @@ func Test_bufadd_bufload() call assert_equal(0, bufexists(buf2)) bwipe someName - bwipe otherName + bwipe XotherName call assert_equal(0, bufexists('someName')) + call delete('XotherName') endfunc func Test_readdir() @@ -1473,6 +1502,20 @@ func Test_readdir() call delete('Xdir', 'rf') endfunc +func Test_call() + call assert_equal(3, call('len', [123])) + call assert_equal(3, 'len'->call([123])) + call assert_fails("call call('len', 123)", 'E714:') + call assert_equal(0, call('', [])) + + function Mylen() dict + return len(self.data) + endfunction + let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} + eval mydict.len->call([], mydict)->assert_equal(4) + call assert_fails("call call('Mylen', [], 0)", 'E715:') +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim index 128b8ff945..41b1a4ad7c 100644 --- a/src/nvim/testdir/test_hide.vim +++ b/src/nvim/testdir/test_hide.vim @@ -37,7 +37,7 @@ function Test_hide() " :hide as a command hide call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) - call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + call assert_equal([1, 1], ['Xf1'->buflisted(), 'Xf1'->bufloaded()]) bwipeout! Xf1 new Xf1 diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index f026c8a55f..63bb4ae1ef 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -61,7 +61,7 @@ endfunction function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) - call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:') + call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:') call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') endfunc diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 0fd76a23ea..505a052a19 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -242,7 +242,7 @@ func Test_matchaddpos_otherwin() \] call assert_equal(expect, savematches) - call clearmatches(winid) + eval winid->clearmatches() call assert_equal([], getmatches(winid)) call setmatches(savematches, winid) diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim new file mode 100644 index 0000000000..7a6e6aa19d --- /dev/null +++ b/src/nvim/testdir/test_method.vim @@ -0,0 +1,159 @@ +" Tests for ->method() + +func Test_list_method() + let l = [1, 2, 3] + call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) + eval l->assert_equal(l) + eval l->assert_equal(l, 'wrong') + eval l->assert_notequal([3, 2, 1]) + eval l->assert_notequal([3, 2, 1], 'wrong') + call assert_equal(l, l->copy()) + call assert_equal(l, l->deepcopy()) + call assert_equal(1, l->count(2)) + call assert_false(l->empty()) + call assert_true([]->empty()) + call assert_equal(579, ['123', '+', '456']->join()->eval()) + call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5])) + call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2')) + call assert_equal(2, l->get(1)) + call assert_equal(1, l->index(2)) + call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0)) + call assert_fails('eval l->items()', 'E715:') + call assert_equal('1 2 3', l->join()) + call assert_fails('eval l->keys()', 'E715:') + call assert_equal(3, l->len()) + call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1')) + call assert_equal(3, l->max()) + call assert_equal(1, l->min()) + call assert_equal(2, [1, 2, 3]->remove(1)) + call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2)) + call assert_equal([3, 2, 1], [1, 2, 3]->reverse()) + call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort()) + call assert_equal('[1, 2, 3]', l->string()) + call assert_equal(v:t_list, l->type()) + call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) + call assert_fails('eval l->values()', 'E715:') +endfunc + +func Test_dict_method() + let d = #{one: 1, two: 2, three: 3} + + call assert_equal(d, d->copy()) + call assert_equal(d, d->deepcopy()) + call assert_equal(1, d->count(2)) + call assert_false(d->empty()) + call assert_true({}->empty()) + call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4})) + call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4')) + call assert_equal(2, d->get('two')) + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails("let x = d->index(2)", 'E897:') + " call assert_fails("let x = d->insert(0)", 'E899:') + call assert_fails("let x = d->index(2)", 'E714:') + call assert_fails("let x = d->insert(0)", 'E686:') + call assert_true(d->has_key('two')) + call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items()) + call assert_fails("let x = d->join()", 'E714:') + call assert_equal(['one', 'two', 'three'], d->keys()) + call assert_equal(3, d->len()) + call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1')) + call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1')) + call assert_equal(3, d->max()) + call assert_equal(1, d->min()) + call assert_equal(2, d->remove("two")) + let d.two = 2 + call assert_fails('let x = d->repeat(2)', 'E731:') + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails('let x = d->reverse()', 'E899:') + call assert_fails('let x = d->reverse()', 'E686:') + call assert_fails('let x = d->sort()', 'E686:') + call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string()) + call assert_equal(v:t_dict, d->type()) + call assert_fails('let x = d->uniq()', 'E686:') + call assert_equal([1, 2, 3], d->values()) +endfunc + +func Test_string_method() + eval '1 2 3'->split()->assert_equal(['1', '2', '3']) + eval '1 2 3'->split()->map({i, v -> str2nr(v)})->assert_equal([1, 2, 3]) + eval 'ABC'->str2list()->assert_equal([65, 66, 67]) + eval 'ABC'->strlen()->assert_equal(3) + eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c') + eval "aあb"->strwidth()->assert_equal(4) + eval 'abc'->substitute('b', 'x', '')->assert_equal('axc') + + eval 'abc'->printf('the %s arg')->assert_equal('the abc arg') +endfunc + +func Test_method_append() + new + eval ['one', 'two', 'three']->append(1) + call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) + + %del + let bnr = bufnr('') + wincmd w + eval ['one', 'two', 'three']->appendbufline(bnr, 1) + call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$')) + + exe 'bwipe! ' .. bnr +endfunc + +func Test_method_funcref() + func Concat(one, two, three) + return a:one .. a:two .. a:three + endfunc + let FuncRef = function('Concat') + eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + + " not enough arguments + call assert_fails("eval 'foo'->FuncRef('bar')", 'E119:') + " too many arguments + call assert_fails("eval 'foo'->FuncRef('bar', 'tail', 'four')", 'E118:') + + let Partial = function('Concat', ['two']) + eval 'one'->Partial('three')->assert_equal('onetwothree') + + " not enough arguments + call assert_fails("eval 'one'->Partial()", 'E119:') + " too many arguments + call assert_fails("eval 'one'->Partial('three', 'four')", 'E118:') + + delfunc Concat +endfunc + +func Test_method_float() + eval 1.234->string()->assert_equal('1.234') + eval -1.234->string()->assert_equal('-1.234') +endfunc + +func Test_method_syntax() + eval [1, 2, 3] ->sort( ) + eval [1, 2, 3] + \ ->sort( + \ ) + call assert_fails('eval [1, 2, 3]-> sort()', 'E260:') + call assert_fails('eval [1, 2, 3]->sort ()', 'E274:') + call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:') +endfunc + +func Test_method_lambda() + eval "text"->{x -> x .. " extended"}()->assert_equal('text extended') + eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more') + + call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:') + + " todo: lambda accepts more arguments than it consumes + " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') + + " Nvim doesn't include test_refcount(). + " let l = [1, 2, 3] + " eval l->{x -> x}() + " call assert_equal(1, test_refcount(l)) +endfunc + +func Test_method_not_supported() + call assert_fails('eval 123->changenr()', 'E276:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim index eaabe3f67e..92a1bf3c9a 100644 --- a/src/nvim/testdir/test_number.vim +++ b/src/nvim/testdir/test_number.vim @@ -3,6 +3,8 @@ source check.vim source view_util.vim +source screendump.vim + func s:screen_lines(start, end) abort return ScreenLines([a:start, a:end], 8) endfunc @@ -265,6 +267,37 @@ func Test_relativenumber_uninitialised() bwipe! endfunc +func Test_relativenumber_colors() + CheckScreendump + + let lines =<< trim [CODE] + call setline(1, range(200)) + 111 + set number relativenumber + hi LineNr ctermfg=red + [CODE] + call writefile(lines, 'XTest_relnr') + + " Check that the balloon shows up after a mouse move + let buf = RunVimInTerminal('-S XTest_relnr', {'rows': 10, 'cols': 50}) + call term_wait(buf, 100) + " Default colors + call VerifyScreenDump(buf, 'Test_relnr_colors_1', {}) + + call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_2', {}) + + call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_3', {}) + + call term_sendkeys(buf, ":hi clear LineNrAbove\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_4', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_relnr') +endfunc + " Test for displaying line numbers with 'rightleft' func Test_number_rightleft() CheckFeature rightleft diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 06bdb1236a..710450293c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -250,7 +250,7 @@ endfunc func Test_noinsert_complete() func! s:complTest1() abort - call complete(1, ['source', 'soundfold']) + eval ['source', 'soundfold']->complete(1) return '' endfunc @@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base) return 0 else call complete_add('four1') - call complete_add('four2') + eval 'four2'->complete_add() call complete_check() call complete_add('four3') call complete_add('four4') @@ -989,7 +989,7 @@ func GetCompleteInfo() if empty(g:compl_what) let g:compl_info = complete_info() else - let g:compl_info = complete_info(g:compl_what) + let g:compl_info = g:compl_what->complete_info() endif return '' endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 875e23894f..2344bac498 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -546,8 +546,8 @@ func Test_synstack_synIDtrans() call assert_equal([], synstack(1, 1)) norm f/ - call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) - call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart']) + eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment']) norm fA call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 6bbe714d19..7b8ee778cc 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -7,10 +7,10 @@ func Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return endif - let out = system('echo 123') + let out = 'echo 123'->system() call assert_equal("123\n", out) - let out = systemlist('echo 123') + let out = 'echo 123'->systemlist() if &shell =~# 'cmd.exe$' call assert_equal(["123\r"], out) else diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index b6d9143bc9..0fa7f85f0d 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -548,6 +548,16 @@ func Test_tag_line_toolong() call assert_equal('Xsomewhere', expand('%')) call assert_equal(3, getcurpos()[1]) + " expansion on command line works with long lines when &wildoptions contains + " 'tagfile' + set wildoptions=tagfile + call writefile([ + \ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa file /^pattern$/;" f' + \ ], 'Xtags') + call feedkeys(":tag \<Tab>", 'tx') + " Should not crash + call assert_true(v:true) + call delete('Xtags') call delete('Xsomewhere') set tags& diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 7dcd92a54b..5231ef7b4f 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -47,7 +47,7 @@ func FuncWithRef(a) endfunc func Test_user_func() - let g:FuncRef=function("FuncWithRef") + let g:FuncRef = function("FuncWithRef") let g:counter = 0 inoremap <expr> ( ListItem() inoremap <expr> [ ListReset() @@ -62,6 +62,14 @@ func Test_user_func() call assert_equal(9, g:retval) call assert_equal(333, g:FuncRef(333)) + let g:retval = "nop" + call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf")) + call assert_equal('fail', 45->Compute(0, "retval")) + call assert_equal('nop', g:retval) + call assert_equal('ok', 45->Compute(5, "retval")) + call assert_equal(9, g:retval) + " call assert_equal(333, 333->g:FuncRef()) + enew normal oXX+-XX @@ -150,6 +158,14 @@ func Test_default_arg() \ execute('func Args2')) endfunc +func s:addFoo(lead) + return a:lead .. 'foo' +endfunc + +func Test_user_method() + eval 'bar'->s:addFoo()->assert_equal('barfoo') +endfunc + func Test_failed_call_in_try() try | call UnknownFunc() | catch | endtry endfunc diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 5922660ef9..d5837e88c9 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1372,6 +1372,7 @@ func Test_bitwise_functions() " and call assert_equal(127, and(127, 127)) call assert_equal(16, and(127, 16)) + eval 127->and(16)->assert_equal(16) call assert_equal(0, and(127, 128)) call assert_fails("call and(1.0, 1)", 'E805:') call assert_fails("call and([], 1)", 'E745:') @@ -1382,6 +1383,7 @@ func Test_bitwise_functions() " or call assert_equal(23, or(16, 7)) call assert_equal(15, or(8, 7)) + eval 8->or(7)->assert_equal(15) call assert_equal(123, or(0, 123)) call assert_fails("call or(1.0, 1)", 'E805:') call assert_fails("call or([], 1)", 'E745:') @@ -1392,6 +1394,7 @@ func Test_bitwise_functions() " xor call assert_equal(0, xor(127, 127)) call assert_equal(111, xor(127, 16)) + eval 127->xor(16)->assert_equal(111) call assert_equal(255, xor(127, 128)) call assert_fails("call xor(1.0, 1)", 'E805:') call assert_fails("call xor([], 1)", 'E745:') @@ -1401,6 +1404,7 @@ func Test_bitwise_functions() call assert_fails("call xor(1, {})", 'E728:') " invert call assert_equal(65408, and(invert(127), 65535)) + eval 127->invert()->and(65535)->assert_equal(65408) call assert_equal(65519, and(invert(16), 65535)) call assert_equal(65407, and(invert(128), 65535)) call assert_fails("call invert(1.0)", 'E805:') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 9c62bdb16e..dbabdcf427 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -861,6 +861,15 @@ func Test_visual_block_mode() set tabstop& shiftwidth& endfunc +func Test_visual_force_motion_feedkeys() + onoremap <expr> i- execute('let g:mode = mode(1)') + call feedkeys('dvi-', 'x') + call assert_equal('nov', g:mode) + call feedkeys('di-', 'x') + call assert_equal('no', g:mode) + ounmap i- +endfunc + " Test block-insert using cursor keys for movement func Test_visual_block_insert_cursor_keys() new diff --git a/src/nvim/undo.c b/src/nvim/undo.c index e1a7dbb2d3..fb96d7e6ff 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -672,6 +672,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) #ifdef HAVE_READLINK char fname_buf[MAXPATHL]; #endif + char *p; if (ffname == NULL) { return NULL; @@ -704,6 +705,13 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) memmove(tail + tail_len + 1, ".un~", sizeof(".un~")); } else { dir_name[dir_len] = NUL; + + // Remove trailing pathseps from directory name + p = &dir_name[dir_len - 1]; + while (vim_ispathsep(*p)) { + *p-- = NUL; + } + bool has_directory = os_isdir((char_u *)dir_name); if (!has_directory && *dirp == NUL && !reading) { // Last directory in the list does not exist, create it. @@ -720,7 +728,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) if (has_directory) { if (munged_name == NULL) { munged_name = xstrdup(ffname); - for (char *p = munged_name; *p != NUL; MB_PTR_ADV(p)) { + for (p = munged_name; *p != NUL; MB_PTR_ADV(p)) { if (vim_ispathsep(*p)) { *p = '%'; } |