diff options
Diffstat (limited to 'src')
68 files changed, 1157 insertions, 467 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2d803792c8..bdedce8076 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -484,7 +484,9 @@ set_property( APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB " ) -if(LUAJIT_FOUND) +if(NOT LUAJIT_FOUND) + message(STATUS "luajit not found, skipping nvim-test (unit tests) target") +else() set(NVIM_TEST_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUAJIT_LIBRARIES}) add_library( nvim-test diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index fa4ad27e60..e1fe7617ff 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -25,6 +25,7 @@ #include "nvim/window.h" #include "nvim/undo.h" #include "nvim/ex_docmd.h" +#include "nvim/buffer_updates.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/buffer.c.generated.h" @@ -75,6 +76,59 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) return rv; } +/// Activate updates from this buffer to the current channel. +/// +/// @param buffer The buffer handle +/// @param send_buffer Set to true if the initial notification should contain +/// the whole buffer. If so, the first notification will be a +/// `nvim_buf_lines_event`. Otherwise, the first notification will be +/// a `nvim_buf_changedtick_event` +/// @param opts Optional parameters. Currently not used. +/// @param[out] err Details of an error that may have occurred +/// @return False when updates couldn't be enabled because the buffer isn't +/// loaded or `opts` contained an invalid key; otherwise True. +Boolean nvim_buf_attach(uint64_t channel_id, + Buffer buffer, + Boolean send_buffer, + Dictionary opts, + Error *err) + FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY +{ + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "dict isn't empty"); + return false; + } + + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return false; + } + + return buf_updates_register(buf, channel_id, send_buffer); +} +// +/// Deactivate updates from this buffer to the current channel. +/// +/// @param buffer The buffer handle +/// @param[out] err Details of an error that may have occurred +/// @return False when updates couldn't be disabled because the buffer +/// isn't loaded; otherwise True. +Boolean nvim_buf_detach(uint64_t channel_id, + Buffer buffer, + Error *err) + FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return false; + } + + buf_updates_unregister(buf, channel_id); + return true; +} + /// Sets a buffer line /// /// @deprecated use nvim_buf_set_lines instead. @@ -184,23 +238,9 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, rv.size = (size_t)(end - start); rv.items = xcalloc(sizeof(Object), rv.size); - for (size_t i = 0; i < rv.size; i++) { - int64_t lnum = start + (int64_t)i; - - if (lnum >= MAXLNUM) { - api_set_error(err, kErrorTypeValidation, "Line index is too high"); - goto end; - } - - const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false); - Object str = STRING_OBJ(cstr_to_string(bufstr)); - - // Vim represents NULs as NLs, but this may confuse clients. - if (channel_id != VIML_INTERNAL_CALL) { - strchrsub(str.data.string.data, '\n', '\0'); - } - - rv.items[i] = str; + if (!buf_collect_lines(buf, rv.size, start, + (channel_id != VIML_INTERNAL_CALL), &rv, err)) { + goto end; } end: @@ -407,7 +447,7 @@ void nvim_buf_set_lines(uint64_t channel_id, false); } - changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); + changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true); if (save_curbuf.br_buf == NULL) { fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index f8eebcdb10..5207a57b88 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -29,6 +29,8 @@ static void msgpack_rpc_add_method_handler(String method, map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); } +/// @param name API method name +/// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len) { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 692a0b51fd..f3e883de02 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -16,6 +16,7 @@ #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/window.h" +#include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" @@ -742,6 +743,43 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE return (String){ .data = str, .size = strlen(str) }; } +/// Collects `n` buffer lines into array `l`, optionally replacing newlines +/// with NUL. +/// +/// @param buf Buffer to get lines from +/// @param n Number of lines to collect +/// @param replace_nl Replace newlines ("\n") with NUL +/// @param start Line number to start from +/// @param[out] l Lines are copied here +/// @param err[out] Error, if any +/// @return true unless `err` was set +bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, + Array *l, Error *err) +{ + for (size_t i = 0; i < n; i++) { + int64_t lnum = start + (int64_t)i; + + if (lnum >= MAXLNUM) { + if (err != NULL) { + api_set_error(err, kErrorTypeValidation, "Line index is too high"); + } + return false; + } + + const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false); + Object str = STRING_OBJ(cstr_to_string(bufstr)); + + if (replace_nl) { + // Vim represents NULs as NLs, but this may confuse clients. + strchrsub(str.data.string.data, '\n', '\0'); + } + + l->items[i] = str; + } + + return true; +} + /// Converts from type Object to a VimL value. /// /// @param obj Object to convert from. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b73ecc2d03..b3ae52602b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -16,6 +16,7 @@ #include "nvim/api/private/dispatch.h" #include "nvim/api/buffer.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -1163,6 +1164,11 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err) MsgpackRpcRequestHandler handler = msgpack_rpc_get_handler_for(name.data, name.size); + if (handler.fn == msgpack_rpc_handle_missing_method) { + api_set_error(&nested_error, kErrorTypeException, "Invalid method: %s", + name.size > 0 ? name.data : "<empty>"); + break; + } Object result = handler.fn(channel_id, args, &nested_error); if (ERROR_SET(&nested_error)) { // error handled after loop diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ba63822837..eb781b1be0 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -73,6 +73,7 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/os/input.h" +#include "nvim/buffer_updates.h" typedef enum { kBLSUnchanged = 0, @@ -574,6 +575,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* Change directories when the 'acd' option is set. */ do_autochdir(); + // disable buffer updates for the current buffer + buf_updates_unregister_all(buf); + /* * Remove the buffer from the list. */ @@ -784,6 +788,8 @@ free_buffer_stuff ( map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs xfree(buf->b_start_fenc); buf->b_start_fenc = NULL; + + buf_updates_unregister_all(buf); } /* @@ -1732,9 +1738,11 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) if (flags & BLN_DUMMY) buf->b_flags |= BF_DUMMY; buf_clear_file(buf); - clrallmarks(buf); /* clear marks */ - fmarks_check_names(buf); /* check file marks for this file */ - buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */ + clrallmarks(buf); // clear marks + fmarks_check_names(buf); // check file marks for this file + buf->b_p_bl = (flags & BLN_LISTED) ? true : false; // init 'buflisted' + kv_destroy(buf->update_channels); + kv_init(buf->update_channels); if (!(flags & BLN_DUMMY)) { // Tricky: these autocommands may change the buffer list. They could also // split the window with re-using the one empty buffer. This may result in @@ -5185,7 +5193,7 @@ void sign_list_placed(buf_T *rbuf) while (buf != NULL && !got_int) { if (buf->b_signlist != NULL) { vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname); - MSG_PUTS_ATTR(lbuf, hl_attr(HLF_D)); + MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 807baf02c1..50d8c822c1 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -38,6 +38,8 @@ typedef struct { #include "nvim/api/private/defs.h" // for Map(K, V) #include "nvim/map.h" +// for kvec +#include "nvim/lib/kvec.h" #define MODIFIABLE(buf) (buf->b_p_ma) @@ -771,6 +773,10 @@ struct file_buffer { BufhlInfo b_bufhl_info; // buffer stored highlights kvec_t(BufhlLine *) b_bufhl_move_space; // temporary space for highlights + + // array of channelids which have asked to receive updates for this + // buffer. + kvec_t(uint64_t) update_channels; }; /* diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c new file mode 100644 index 0000000000..c1b2828666 --- /dev/null +++ b/src/nvim/buffer_updates.c @@ -0,0 +1,210 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/buffer_updates.h" +#include "nvim/memline.h" +#include "nvim/api/private/helpers.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/assert.h" + +// Register a channel. Return True if the channel was added, or already added. +// Return False if the channel couldn't be added because the buffer is +// unloaded. +bool buf_updates_register(buf_T *buf, uint64_t channel_id, bool send_buffer) +{ + // must fail if the buffer isn't loaded + if (buf->b_ml.ml_mfp == NULL) { + return false; + } + + // count how many channels are currently watching the buffer + size_t size = kv_size(buf->update_channels); + if (size) { + for (size_t i = 0; i < size; i++) { + if (kv_A(buf->update_channels, i) == channel_id) { + // buffer is already registered ... nothing to do + return true; + } + } + } + + // append the channelid to the list + kv_push(buf->update_channels, channel_id); + + if (send_buffer) { + Array args = ARRAY_DICT_INIT; + args.size = 6; + args.items = xcalloc(sizeof(Object), args.size); + + // the first argument is always the buffer handle + args.items[0] = BUFFER_OBJ(buf->handle); + args.items[1] = INTEGER_OBJ(buf->b_changedtick); + // the first line that changed (zero-indexed) + args.items[2] = INTEGER_OBJ(0); + // the last line that was changed + args.items[3] = INTEGER_OBJ(-1); + Array linedata = ARRAY_DICT_INIT; + + // collect buffer contents + + STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); + size_t line_count = (size_t)buf->b_ml.ml_line_count; + + if (line_count >= 1) { + linedata.size = line_count; + linedata.items = xcalloc(sizeof(Object), line_count); + + buf_collect_lines(buf, line_count, 1, true, &linedata, NULL); + } + + args.items[4] = ARRAY_OBJ(linedata); + args.items[5] = BOOLEAN_OBJ(false); + + rpc_send_event(channel_id, "nvim_buf_lines_event", args); + } else { + buf_updates_changedtick_single(buf, channel_id); + } + + return true; +} + +void buf_updates_send_end(buf_T *buf, uint64_t channelid) +{ + Array args = ARRAY_DICT_INIT; + args.size = 1; + args.items = xcalloc(sizeof(Object), args.size); + args.items[0] = BUFFER_OBJ(buf->handle); + rpc_send_event(channelid, "nvim_buf_detach_event", args); +} + +void buf_updates_unregister(buf_T *buf, uint64_t channelid) +{ + size_t size = kv_size(buf->update_channels); + if (!size) { + return; + } + + // go through list backwards and remove the channel id each time it appears + // (it should never appear more than once) + size_t j = 0; + size_t found = 0; + for (size_t i = 0; i < size; i++) { + if (kv_A(buf->update_channels, i) == channelid) { + found++; + } else { + // copy item backwards into prior slot if needed + if (i != j) { + kv_A(buf->update_channels, j) = kv_A(buf->update_channels, i); + } + j++; + } + } + + if (found) { + // remove X items from the end of the array + buf->update_channels.size -= found; + + // make a new copy of the active array without the channelid in it + buf_updates_send_end(buf, channelid); + + if (found == size) { + kv_destroy(buf->update_channels); + kv_init(buf->update_channels); + } + } +} + +void buf_updates_unregister_all(buf_T *buf) +{ + size_t size = kv_size(buf->update_channels); + if (size) { + for (size_t i = 0; i < size; i++) { + buf_updates_send_end(buf, kv_A(buf->update_channels, i)); + } + kv_destroy(buf->update_channels); + kv_init(buf->update_channels); + } +} + +void buf_updates_send_changes(buf_T *buf, + linenr_T firstline, + int64_t num_added, + int64_t num_removed, + bool send_tick) +{ + // if one the channels doesn't work, put its ID here so we can remove it later + uint64_t badchannelid = 0; + + // notify each of the active channels + for (size_t i = 0; i < kv_size(buf->update_channels); i++) { + uint64_t channelid = kv_A(buf->update_channels, i); + + // send through the changes now channel contents now + Array args = ARRAY_DICT_INIT; + args.size = 6; + args.items = xcalloc(sizeof(Object), args.size); + + // the first argument is always the buffer handle + args.items[0] = BUFFER_OBJ(buf->handle); + + // next argument is b:changedtick + args.items[1] = send_tick ? INTEGER_OBJ(buf->b_changedtick) : NIL; + + // the first line that changed (zero-indexed) + args.items[2] = INTEGER_OBJ(firstline - 1); + + // the last line that was changed + args.items[3] = INTEGER_OBJ(firstline - 1 + num_removed); + + // linedata of lines being swapped in + Array linedata = ARRAY_DICT_INIT; + if (num_added > 0) { + STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); + linedata.size = (size_t)num_added; + linedata.items = xcalloc(sizeof(Object), (size_t)num_added); + buf_collect_lines(buf, (size_t)num_added, firstline, true, &linedata, + NULL); + } + args.items[4] = ARRAY_OBJ(linedata); + args.items[5] = BOOLEAN_OBJ(false); + if (!rpc_send_event(channelid, "nvim_buf_lines_event", args)) { + // We can't unregister the channel while we're iterating over the + // update_channels array, so we remember its ID to unregister it at + // the end. + badchannelid = channelid; + } + } + + // We can only ever remove one dead channel at a time. This is OK because the + // change notifications are so frequent that many dead channels will be + // cleared up quickly. + if (badchannelid != 0) { + ELOG("Disabling buffer updates for dead channel %llu", badchannelid); + buf_updates_unregister(buf, badchannelid); + } +} + +void buf_updates_changedtick(buf_T *buf) +{ + // notify each of the active channels + for (size_t i = 0; i < kv_size(buf->update_channels); i++) { + uint64_t channel_id = kv_A(buf->update_channels, i); + buf_updates_changedtick_single(buf, channel_id); + } +} + +void buf_updates_changedtick_single(buf_T *buf, uint64_t channel_id) +{ + Array args = ARRAY_DICT_INIT; + args.size = 2; + args.items = xcalloc(sizeof(Object), args.size); + + // the first argument is always the buffer handle + args.items[0] = BUFFER_OBJ(buf->handle); + + // next argument is b:changedtick + args.items[1] = INTEGER_OBJ(buf->b_changedtick); + + // don't try and clean up dead channels here + rpc_send_event(channel_id, "nvim_buf_changedtick_event", args); +} diff --git a/src/nvim/buffer_updates.h b/src/nvim/buffer_updates.h new file mode 100644 index 0000000000..b2d0a62270 --- /dev/null +++ b/src/nvim/buffer_updates.h @@ -0,0 +1,10 @@ +#ifndef NVIM_BUFFER_UPDATES_H +#define NVIM_BUFFER_UPDATES_H + +#include "nvim/buffer_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "buffer_updates.h.generated.h" +#endif + +#endif // NVIM_BUFFER_UPDATES_H diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 64d743891b..6ad64bbb85 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -602,6 +602,7 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, // process_channel_event will modify the read buffer(convert NULs into NLs) if (chan->term) { terminal_receive(chan->term, ptr, count); + terminal_flush_output(chan->term); } rbuffer_consumed(buf, count); diff --git a/src/nvim/diff.c b/src/nvim/diff.c index f9e40ed06f..61e0b76558 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2344,7 +2344,7 @@ void ex_diffgetput(exarg_T *eap) } } } - changed_lines(lnum, 0, lnum + count, (long)added); + changed_lines(lnum, 0, lnum + count, (long)added, true); if (dfree != NULL) { // Diff is deleted, update folds in other windows. diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 6dbb0d05e0..218a3f0604 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1711,7 +1711,7 @@ static void printdigraph(digr_T *dp) p += (*mb_char2bytes)(dp->result, p); *p = NUL; - msg_outtrans_attr(buf, hl_attr(HLF_8)); + msg_outtrans_attr(buf, HL_ATTR(HLF_8)); p = buf; if (char2cells(dp->result) == 1) { *p++ = ' '; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 462762aea0..f3e2663d76 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1473,10 +1473,11 @@ void edit_putchar(int c, int highlight) if (ScreenLines != NULL) { update_topline(); /* just in case w_topline isn't valid */ validate_cursor(); - if (highlight) - attr = hl_attr(HLF_8); - else + if (highlight) { + attr = HL_ATTR(HLF_8); + } else { attr = 0; + } pc_row = curwin->w_winrow + curwin->w_wrow; pc_col = curwin->w_wincol; pc_status = PC_STATUS_UNSET; @@ -1879,7 +1880,7 @@ static bool check_compl_option(bool dict_opt) edit_submode = NULL; msg_attr((dict_opt ? _("'dictionary' option is empty") - : _("'thesaurus' option is empty")), hl_attr(HLF_E)); + : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); if (emsg_silent == 0) { vim_beep(BO_COMPL); setcursor(); @@ -2772,8 +2773,8 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, fp = mch_fopen((char *)files[i], "r"); /* open dictionary file */ if (flags != DICT_EXACT) { vim_snprintf((char *)IObuff, IOSIZE, - _("Scanning dictionary: %s"), (char *)files[i]); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); + _("Scanning dictionary: %s"), (char *)files[i]); + (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); } if (fp == NULL) { @@ -3733,15 +3734,15 @@ static int ins_compl_get_exp(pos_T *ini) dict_f = DICT_EXACT; } vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), - ins_buf->b_fname == NULL - ? buf_spname(ins_buf) - : ins_buf->b_sfname == NULL - ? ins_buf->b_fname - : ins_buf->b_sfname); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); - } else if (*e_cpt == NUL) + ins_buf->b_fname == NULL + ? buf_spname(ins_buf) + : ins_buf->b_sfname == NULL + ? ins_buf->b_fname + : ins_buf->b_sfname); + (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); + } else if (*e_cpt == NUL) { break; - else { + } else { if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { type = -1; } else if (*e_cpt == 'k' || *e_cpt == 's') { @@ -3760,9 +3761,10 @@ static int ins_compl_get_exp(pos_T *ini) else if (*e_cpt == ']' || *e_cpt == 't') { type = CTRL_X_TAGS; vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); - } else + (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); + } else { type = -1; + } /* in any case e_cpt is advanced to the next entry */ (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); @@ -4899,7 +4901,7 @@ static int ins_complete(int c, bool enable_pum) if (!p_smd) { msg_attr((const char *)edit_submode_extra, (edit_submode_highl < HLF_COUNT - ? hl_attr(edit_submode_highl) : 0)); + ? HL_ATTR(edit_submode_highl) : 0)); } } else { msg_clr_cmdline(); // necessary for "noshowmode" @@ -7545,9 +7547,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) curwin->w_cursor.coladd = 0; } - /* - * delete newline! - */ + // Delete newline! if (curwin->w_cursor.col == 0) { lnum = Insstart.lnum; if (curwin->w_cursor.lnum == lnum || revins_on) { @@ -7556,7 +7556,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) return false; } Insstart.lnum--; - Insstart.col = MAXCOL; + Insstart.col = (colnr_T)STRLEN(ml_get(Insstart.lnum)); } /* * In replace mode: diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ffea88aa83..30c17af8c9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6742,36 +6742,39 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *tofree; if (opt_msg_tv->v_type != VAR_UNKNOWN) { - tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL); + tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL); ga_concat(gap, tofree); xfree(tofree); + ga_concat(gap, (char_u *)": "); + } + + if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)"Pattern "); + } else if (atype == ASSERT_NOTEQUAL) { + ga_concat(gap, (char_u *)"Expected not equal to "); } else { - if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)"Pattern "); - } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, (char_u *)"Expected not equal to "); - } else { - ga_concat(gap, (char_u *)"Expected "); - } - if (exp_str == NULL) { - tofree = (char_u *)encode_tv2string(exp_tv, NULL); - ga_concat_esc(gap, tofree); - xfree(tofree); + ga_concat(gap, (char_u *)"Expected "); + } + + if (exp_str == NULL) { + tofree = (char_u *)encode_tv2string(exp_tv, NULL); + ga_concat_esc(gap, tofree); + xfree(tofree); + } else { + ga_concat_esc(gap, exp_str); + } + + if (atype != ASSERT_NOTEQUAL) { + if (atype == ASSERT_MATCH) { + ga_concat(gap, (char_u *)" does not match "); + } else if (atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)" does match "); } else { - ga_concat_esc(gap, exp_str); - } - if (atype != ASSERT_NOTEQUAL) { - if (atype == ASSERT_MATCH) { - ga_concat(gap, (char_u *)" does not match "); - } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)" does match "); - } else { - ga_concat(gap, (char_u *)" but got "); - } - tofree = (char_u *)encode_tv2string(got_tv, NULL); - ga_concat_esc(gap, tofree); - xfree(tofree); + ga_concat(gap, (char_u *)" but got "); } + tofree = (char_u *)encode_tv2string(got_tv, NULL); + ga_concat_esc(gap, tofree); + xfree(tofree); } } @@ -8824,7 +8827,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } unsigned long count = (unsigned long)(foldend - foldstart + 1); - txt = ngettext("+-%s%3ld line: ", "+-%s%3ld lines: ", count); + txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count); r = xmalloc(STRLEN(txt) + STRLEN(dashes) // for %s + 20 // for %3ld @@ -14806,18 +14809,14 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) pos.col = 0; } if (name[0] == '.' && name[1] == NUL) { - // set cursor - if (fnum == curbuf->b_fnum) { - curwin->w_cursor = pos; - if (curswant >= 0) { - curwin->w_curswant = curswant - 1; - curwin->w_set_curswant = false; - } - check_cursor(); - rettv->vval.v_number = 0; - } else { - EMSG(_(e_invarg)); + // set cursor; "fnum" is ignored + curwin->w_cursor = pos; + if (curswant >= 0) { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; } + check_cursor(); + rettv->vval.v_number = 0; } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { @@ -20439,7 +20438,7 @@ static void list_func_head(ufunc_T *fp, int indent) MSG_PUTS(" "); MSG_PUTS("function "); if (fp->uf_name[0] == K_SPECIAL) { - MSG_PUTS_ATTR("<SNR>", hl_attr(HLF_8)); + MSG_PUTS_ATTR("<SNR>", HL_ATTR(HLF_8)); msg_puts((const char *)fp->uf_name + 3); } else { msg_puts((const char *)fp->uf_name); diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index d2fb52243c..2baa667e7d 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -14,7 +14,7 @@ #include "nvim/vim.h" #include "nvim/memory.h" -#define DEFAULT_MAXMEM 1024 * 1024 * 10 +#define DEFAULT_MAXMEM 1024 * 1024 * 2000 typedef struct { Stream *stream; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f575d58f05..4e0bdb777c 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -35,6 +35,7 @@ #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" +#include "nvim/buffer_updates.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -279,7 +280,7 @@ void ex_align(exarg_T *eap) new_indent = 0; (void)set_indent(new_indent, 0); /* set indent */ } - changed_lines(eap->line1, 0, eap->line2 + 1, 0L); + changed_lines(eap->line1, 0, eap->line2 + 1, 0L, true); curwin->w_cursor = save_curpos; beginline(BL_WHITE | BL_FIX); } @@ -612,7 +613,7 @@ void ex_sort(exarg_T *eap) } else if (deleted < 0) { mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false); } - changed_lines(eap->line1, 0, eap->line2 + 1, -deleted); + changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); curwin->w_cursor.lnum = eap->line1; beginline(BL_WHITE | BL_FIX); @@ -744,8 +745,9 @@ void ex_retab(exarg_T *eap) if (curbuf->b_p_ts != new_ts) redraw_curbuf_later(NOT_VALID); - if (first_line != 0) - changed_lines(first_line, 0, last_line + 1, 0L); + if (first_line != 0) { + changed_lines(first_line, 0, last_line + 1, 0L, true); + } curwin->w_p_list = save_list; /* restore 'list' */ @@ -806,6 +808,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) */ last_line = curbuf->b_ml.ml_line_count; mark_adjust_nofold(line1, line2, last_line - line2, 0L, true); + changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines, false); if (dest >= line2) { mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false); FOR_ALL_TAB_WINDOWS(tab, win) { @@ -828,6 +831,12 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust_nofold(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L, true); + changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false); + + // send update regarding the new lines that were added + if (kv_size(curbuf->update_channels)) { + buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true); + } /* * Now we delete the original text -- webb @@ -858,9 +867,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) last_line = curbuf->b_ml.ml_line_count; if (dest > last_line + 1) dest = last_line + 1; - changed_lines(line1, 0, dest, 0L); + changed_lines(line1, 0, dest, 0L, false); } else { - changed_lines(dest + 1, 0, line1 + num_lines, 0L); + changed_lines(dest + 1, 0, line1 + num_lines, 0L, false); + } + + // send nvim_buf_lines_event regarding lines that were deleted + if (kv_size(curbuf->update_channels)) { + buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true); } return OK; @@ -1486,7 +1500,7 @@ void print_line_no_prefix(linenr_T lnum, int use_number, int list) if (curwin->w_p_nu || use_number) { vim_snprintf(numbuf, sizeof(numbuf), "%*" PRIdLINENR " ", number_width(curwin), lnum); - msg_puts_attr(numbuf, hl_attr(HLF_N)); // Highlight line nrs. + msg_puts_attr(numbuf, HL_ATTR(HLF_N)); // Highlight line nrs. } msg_prt_line(ml_get(lnum), list); } @@ -2428,6 +2442,7 @@ int do_ecmd( goto theend; } u_unchanged(curbuf); + buf_updates_unregister_all(curbuf); buf_freeall(curbuf, BFA_KEEP_UNDO); // Tell readfile() not to clear or reload undo info. @@ -3153,8 +3168,10 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, /// /// The usual escapes are supported as described in the regexp docs. /// +/// @param do_buf_event If `true`, send buffer updates. /// @return buffer used for 'inccommand' preview -static buf_T *do_sub(exarg_T *eap, proftime_T timeout) +static buf_T *do_sub(exarg_T *eap, proftime_T timeout, + bool do_buf_event) { long i = 0; regmmatch_T regmatch; @@ -3618,7 +3635,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) msg_no_more = TRUE; /* write message same highlighting as for * wait_return */ - smsg_attr(hl_attr(HLF_R), + smsg_attr(HL_ATTR(HLF_R), _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub); msg_no_more = FALSE; msg_scroll = i; @@ -4000,7 +4017,14 @@ skip: * the line number before the change (same as adding the number of * deleted lines). */ i = curbuf->b_ml.ml_line_count - old_line_count; - changed_lines(first_line, 0, last_line - i, i); + changed_lines(first_line, 0, last_line - i, i, false); + + if (kv_size(curbuf->update_channels)) { + int64_t num_added = last_line - first_line; + int64_t num_removed = num_added - i; + buf_updates_send_changes(curbuf, first_line, num_added, num_removed, + do_buf_event); + } } xfree(sub_firstline); /* may have to free allocated copy of the line */ @@ -6246,7 +6270,7 @@ void ex_substitute(exarg_T *eap) { bool preview = (State & CMDPREVIEW); if (*p_icm == NUL || !preview) { // 'inccommand' is disabled - (void)do_sub(eap, profile_zero()); + (void)do_sub(eap, profile_zero(), true); return; } @@ -6270,7 +6294,7 @@ void ex_substitute(exarg_T *eap) // Don't show search highlighting during live substitution bool save_hls = p_hls; p_hls = false; - buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt)); + buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt), false); p_hls = save_hls; if (save_changedtick != curbuf->b_changedtick) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index ce02808ad3..c87e3d4c66 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -698,7 +698,7 @@ return { }, { command='delfunction', - flags=bit.bor(NEEDARG, WORD1, CMDWIN), + flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN), addr_type=ADDR_LINES, func='ex_delfunction', }, @@ -3082,7 +3082,7 @@ return { }, { command='windo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), addr_type=ADDR_WINDOWS, func='ex_listdo', }, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 96d2102156..e95890adbf 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1512,7 +1512,7 @@ int buf_write_all(buf_T *buf, int forceit) (linenr_T)1, buf->b_ml.ml_line_count, NULL, false, forceit, true, false)); if (curbuf != old_curbuf) { - msg_source(hl_attr(HLF_W)); + msg_source(HL_ATTR(HLF_W)); MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)")); } return retval; @@ -3258,7 +3258,7 @@ retry: ga.ga_len--; } else { // lines like ":map xx yy^M" will have failed if (!sp->error) { - msg_source(hl_attr(HLF_W)); + msg_source(HL_ATTR(HLF_W)); EMSG(_("W15: Warning: Wrong line separator, ^M may be missing")); } sp->error = true; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e1efd5710d..709dc60b13 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4949,7 +4949,7 @@ static void uc_list(char_u *name, size_t name_len) msg_putchar(gap != &ucmds ? 'b' : ' '); msg_putchar(' '); - msg_outtrans_attr(cmd->uc_name, hl_attr(HLF_D)); + msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D)); len = (int)STRLEN(cmd->uc_name) + 4; do { @@ -6767,8 +6767,8 @@ static void ex_tabs(exarg_T *eap) msg_putchar('\n'); vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++); - msg_outtrans_attr(IObuff, hl_attr(HLF_T)); - ui_flush(); /* output one line at a time */ + msg_outtrans_attr(IObuff, HL_ATTR(HLF_T)); + ui_flush(); // output one line at a time os_breakcheck(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f62b0a2060..d2db309c4f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -295,10 +295,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) redir_off = true; // don't redirect the typed command if (!cmd_silent) { - s->i = msg_scrolled; - msg_scrolled = 0; // avoid wait_return message gotocmdline(true); - msg_scrolled += s->i; redrawcmdprompt(); // draw prompt or indent set_cmdspos(); } @@ -349,7 +346,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) // redraw the statusline for statuslines that display the current mode // using the mode() function. - if (KeyTyped) { + if (KeyTyped && msg_scrolled == 0) { curwin->w_redr_status = true; redraw_statuslines(); } @@ -382,7 +379,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { msg_putchar('\n'); - msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); api_clear_error(&err); redrawcmd(); } @@ -465,7 +462,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (!tl_ret && ERROR_SET(&err)) { msg_putchar('\n'); - msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); api_clear_error(&err); } @@ -629,6 +626,7 @@ static int command_line_execute(VimState *state, int key) // Entered command line, move it up cmdline_row--; redrawcmd(); + wild_menu_showing = 0; } else if (save_p_ls != -1) { // restore 'laststatus' and 'winminheight' p_ls = save_p_ls; @@ -639,13 +637,13 @@ static int command_line_execute(VimState *state, int key) restore_cmdline(&s->save_ccline); redrawcmd(); save_p_ls = -1; + wild_menu_showing = 0; } else { win_redraw_last_status(topframe); wild_menu_showing = 0; // must be before redraw_statuslines #8385 redraw_statuslines(); } KeyTyped = skt; - wild_menu_showing = 0; if (ccline.input_fn) { RedrawingDisabled = old_RedrawingDisabled; } @@ -2588,7 +2586,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) #define PRINT_ERRMSG(...) \ do { \ msg_putchar('\n'); \ - msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, __VA_ARGS__); \ + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, __VA_ARGS__); \ printed_errmsg = true; \ } while (0) bool ret = true; @@ -4154,13 +4152,13 @@ static int showmatches(expand_T *xp, int wildmenu) lines = (num_files + columns - 1) / columns; } - attr = hl_attr(HLF_D); /* find out highlighting for directories */ + attr = HL_ATTR(HLF_D); // find out highlighting for directories if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - MSG_PUTS_ATTR(_("tagname"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_("tagname"), HL_ATTR(HLF_T)); msg_clr_eos(); msg_advance(maxlen - 3); - MSG_PUTS_ATTR(_(" kind file\n"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_(" kind file\n"), HL_ATTR(HLF_T)); } /* list the files line by line */ @@ -4168,12 +4166,12 @@ static int showmatches(expand_T *xp, int wildmenu) lastlen = 999; for (k = i; k < num_files; k += lines) { if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - msg_outtrans_attr(files_found[k], hl_attr(HLF_D)); + msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D)); p = files_found[k] + STRLEN(files_found[k]) + 1; msg_advance(maxlen + 1); msg_puts((const char *)p); msg_advance(maxlen + 3); - msg_puts_long_attr(p + 2, hl_attr(HLF_D)); + msg_puts_long_attr(p + 2, HL_ATTR(HLF_D)); break; } for (j = maxlen - lastlen; --j >= 0; ) diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c index 5801a2d8fb..fae2c805f9 100644 --- a/src/nvim/farsi.c +++ b/src/nvim/farsi.c @@ -1603,7 +1603,7 @@ static void conv_to_pvim(void) // Assume the screen has been messed up: clear it and redraw. redraw_later(CLEAR); - MSG_ATTR((const char *)farsi_text_1, hl_attr(HLF_S)); + MSG_ATTR((const char *)farsi_text_1, HL_ATTR(HLF_S)); } /// Convert the Farsi VIM into Farsi 3342 standard. @@ -1624,7 +1624,7 @@ static void conv_to_pstd(void) // Assume the screen has been messed up: clear it and redraw. redraw_later(CLEAR); - msg_attr((const char *)farsi_text_2, hl_attr(HLF_S)); + msg_attr((const char *)farsi_text_2, HL_ATTR(HLF_S)); } /// left-right swap the characters in buf[len]. diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 520aedaac7..0417c3daed 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3694,7 +3694,7 @@ nofail: retval = FAIL; if (end == 0) { - const int attr = hl_attr(HLF_E); // Set highlight for error messages. + const int attr = HL_ATTR(HLF_E); // Set highlight for error messages. MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), attr | MSG_HIST); MSG_PUTS_ATTR(_( @@ -3901,7 +3901,7 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) msg_silent = 0; // Must give this prompt. // Don't use emsg() here, don't want to flush the buffers. msg_attr(_("WARNING: The file has been changed since reading it!!!"), - hl_attr(HLF_E)); + HL_ATTR(HLF_E)); if (ask_yesno(_("Do you really want to write to it"), true) == 'n') { return FAIL; } @@ -5020,9 +5020,9 @@ buf_check_timestamp ( } else { if (!autocmd_busy) { msg_start(); - msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST); + msg_puts_attr(tbuf, HL_ATTR(HLF_E) + MSG_HIST); if (*mesg2 != NUL) { - msg_puts_attr(mesg2, hl_attr(HLF_W) + MSG_HIST); + msg_puts_attr(mesg2, HL_ATTR(HLF_W) + MSG_HIST); } msg_clr_eos(); (void)msg_end(); @@ -5445,13 +5445,13 @@ static void show_autocmd(AutoPat *ap, event_T event) if (event != last_event || ap->group != last_group) { if (ap->group != AUGROUP_DEFAULT) { if (AUGROUP_NAME(ap->group) == NULL) { - msg_puts_attr(get_deleted_augroup(), hl_attr(HLF_E)); + msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E)); } else { - msg_puts_attr(AUGROUP_NAME(ap->group), hl_attr(HLF_T)); + msg_puts_attr(AUGROUP_NAME(ap->group), HL_ATTR(HLF_T)); } msg_puts(" "); } - msg_puts_attr(event_nr2name(event), hl_attr(HLF_T)); + msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T)); last_event = event; last_group = ap->group; msg_putchar('\n'); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 316fbef47c..282b72b67a 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -20,6 +20,7 @@ #include "nvim/ex_docmd.h" #include "nvim/func_attr.h" #include "nvim/indent.h" +#include "nvim/buffer_updates.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -742,8 +743,20 @@ deleteFold ( /* Deleting markers may make cursor column invalid. */ check_cursor_col(); - if (last_lnum > 0) - changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L); + if (last_lnum > 0) { + changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L, false); + + // send one nvim_buf_lines_event at the end + if (kv_size(curbuf->update_channels)) { + // last_lnum is the line *after* the last line of the outermost fold + // that was modified. Note also that deleting a fold might only require + // the modification of the *first* line of the fold, but we send through a + // notification that includes every line that was part of the fold + int64_t num_changed = last_lnum - first_lnum; + buf_updates_send_changes(curbuf, first_lnum, num_changed, + num_changed, true); + } + } } /* clearFolding() {{{2 */ @@ -1590,7 +1603,15 @@ static void foldCreateMarkers(linenr_T start, linenr_T end) /* Update both changes here, to avoid all folds after the start are * changed when the start marker is inserted and the end isn't. */ - changed_lines(start, (colnr_T)0, end, 0L); + changed_lines(start, (colnr_T)0, end, 0L, false); + + if (kv_size(curbuf->update_channels)) { + // Note: foldAddMarker() may not actually change start and/or end if + // u_save() is unable to save the buffer line, but we send the + // nvim_buf_lines_event anyway since it won't do any harm. + int64_t num_changed = 1 + end - start; + buf_updates_send_changes(curbuf, start, num_changed, num_changed, true); + } } /* foldAddMarker() {{{2 */ @@ -1785,7 +1806,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, unsigned long count = (unsigned long)(lnume - lnum + 1); vim_snprintf((char *)buf, FOLD_TEXT_LEN, - ngettext("+--%3ld line folded", + NGETTEXT("+--%3ld line folded", "+--%3ld lines folded ", count), count); text = buf; diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 2ee1e5d4c5..15fcafb584 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -223,6 +223,11 @@ for i = 1, #functions do output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') end + -- accept empty lua tables as empty dictionarys + if rt:match('^Dictionary') then + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') + output:write('\n '..converted..' = (Dictionary)ARRAY_DICT_INIT;') + end output:write('\n } else {') output:write('\n api_set_error(error, kErrorTypeException, "Wrong type for argument '..j..', expecting '..param[1]..'");') output:write('\n goto cleanup;') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9a2ecbfbd8..690a83af50 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3197,9 +3197,9 @@ showmap ( } while (len < 12); if (mp->m_noremap == REMAP_NONE) { - msg_puts_attr("*", hl_attr(HLF_8)); + msg_puts_attr("*", HL_ATTR(HLF_8)); } else if (mp->m_noremap == REMAP_SCRIPT) { - msg_puts_attr("&", hl_attr(HLF_8)); + msg_puts_attr("&", HL_ATTR(HLF_8)); } else { msg_putchar(' '); } @@ -3212,7 +3212,7 @@ showmap ( /* Use FALSE below if we only want things like <Up> to show up as such on * the rhs, and not M-x etc, TRUE gets both -- webb */ if (*mp->m_str == NUL) { - msg_puts_attr("<Nop>", hl_attr(HLF_8)); + msg_puts_attr("<Nop>", HL_ATTR(HLF_8)); } else { // Remove escaping of CSI, because "m_str" is in a format to be used // as typeahead. diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h index 60317b8484..acc7e3a92c 100644 --- a/src/nvim/gettext.h +++ b/src/nvim/gettext.h @@ -10,10 +10,11 @@ # else # define N_(x) x # endif +# define NGETTEXT(x, xs, n) ngettext(x, xs, n) #else # define _(x) ((char *)(x)) # define N_(x) x -# define ngettext(x, xs, n) ((n) == 1 ? (x) : (xs)) +# define NGETTEXT(x, xs, n) ((n) == 1 ? (x) : (xs)) # define bindtextdomain(x, y) // empty # define bind_textdomain_codeset(x, y) // empty # define textdomain(x) // empty diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index abc4773d84..b3a9eabdb8 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -584,7 +584,7 @@ static void prt_header(prt_settings_T *psettings, int pagenum, linenr_T lnum) static void prt_message(char_u *s) { screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); - screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R)); + screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); ui_flush(); } diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 5d7bd26a2b..ab33cf7863 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -496,9 +496,9 @@ staterr: if (p_csverbose) { msg_clr_eos(); - (void)smsg_attr(hl_attr(HLF_R), - _("Added cscope database %s"), - csinfo[i].fname); + (void)smsg_attr(HL_ATTR(HLF_R), + _("Added cscope database %s"), + csinfo[i].fname); } } @@ -1258,8 +1258,8 @@ static void cs_kill_execute( { if (p_csverbose) { msg_clr_eos(); - (void)smsg_attr(hl_attr(HLF_R) | MSG_HIST, - _("cscope connection %s closed"), cname); + (void)smsg_attr(HL_ATTR(HLF_R) | MSG_HIST, + _("cscope connection %s closed"), cname); } cs_release_csp(i, TRUE); } @@ -1590,16 +1590,16 @@ static void cs_print_tags_priv(char **matches, char **cntxts, char *buf = xmalloc(newsize); size_t bufsize = newsize; // Track available bufsize (void)snprintf(buf, bufsize, cstag_msg, ptag); - MSG_PUTS_ATTR(buf, hl_attr(HLF_T)); + MSG_PUTS_ATTR(buf, HL_ATTR(HLF_T)); msg_clr_eos(); // restore matches[0] *ptag_end = '\t'; // Column headers for match number, line number and filename. - MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_("\n # line"), HL_ATTR(HLF_T)); msg_advance(msg_col + 2); - MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_("filename / context / line\n"), HL_ATTR(HLF_T)); for (size_t i = 0; i < num_matches; i++) { assert(strcnt(matches[i], '\t') >= 2); @@ -1626,8 +1626,8 @@ static void cs_print_tags_priv(char **matches, char **cntxts, bufsize = newsize; } (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno); - MSG_PUTS_ATTR(buf, hl_attr(HLF_CM)); - MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM)); + MSG_PUTS_ATTR(buf, HL_ATTR(HLF_CM)); + MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), HL_ATTR(HLF_CM)); // compute the required space for the context char *context = cntxts[i] ? cntxts[i] : globalcntx; @@ -1915,7 +1915,7 @@ static int cs_reset(exarg_T *eap) * "Added cscope database..." */ snprintf(buf, ARRAY_SIZE(buf), " (#%zu)", i); - MSG_PUTS_ATTR(buf, hl_attr(HLF_R)); + MSG_PUTS_ATTR(buf, HL_ATTR(HLF_R)); } } xfree(dblist[i]); @@ -1927,7 +1927,7 @@ static int cs_reset(exarg_T *eap) xfree(fllist); if (p_csverbose) { - msg_attr(_("All cscope databases reset"), hl_attr(HLF_R) | MSG_HIST); + msg_attr(_("All cscope databases reset"), HL_ATTR(HLF_R) | MSG_HIST); } return CSCOPE_SUCCESS; } /* cs_reset */ @@ -1993,7 +1993,7 @@ static int cs_show(exarg_T *eap) else { MSG_PUTS_ATTR( _(" # pid database name prepend path\n"), - hl_attr(HLF_T)); + HL_ATTR(HLF_T)); for (size_t i = 0; i < csinfo_size; i++) { if (csinfo[i].fname == NULL) continue; diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 3cd26a5bf7..7cfe3f4a18 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -106,39 +106,41 @@ int setmark_pos(int c, pos_T *pos, int fnum) return OK; } + // Can't set a mark in a non-existant buffer. + buf_T *buf = buflist_findnr(fnum); + if (buf == NULL) { + return FAIL; + } + if (c == '"') { - RESET_FMARK(&curbuf->b_last_cursor, *pos, curbuf->b_fnum); + RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum); return OK; } /* Allow setting '[ and '] for an autocommand that simulates reading a * file. */ if (c == '[') { - curbuf->b_op_start = *pos; + buf->b_op_start = *pos; return OK; } if (c == ']') { - curbuf->b_op_end = *pos; + buf->b_op_end = *pos; return OK; } if (c == '<' || c == '>') { - if (c == '<') - curbuf->b_visual.vi_start = *pos; - else - curbuf->b_visual.vi_end = *pos; - if (curbuf->b_visual.vi_mode == NUL) - /* Visual_mode has not yet been set, use a sane default. */ - curbuf->b_visual.vi_mode = 'v'; + if (c == '<') { + buf->b_visual.vi_start = *pos; + } else { + buf->b_visual.vi_end = *pos; + } + if (buf->b_visual.vi_mode == NUL) { + // Visual_mode has not yet been set, use a sane default. + buf->b_visual.vi_mode = 'v'; + } return OK; } - buf_T *buf = buflist_findnr(fnum); - // Can't set a mark in a non-existant buffer. - if (buf == NULL) { - return FAIL; - } - if (ASCII_ISLOWER(c)) { i = c - 'a'; RESET_FMARK(buf->b_namedm + i, *pos, fnum); @@ -358,13 +360,14 @@ pos_T *getmark_buf_fnum(buf_T *buf, int c, int changefile, int *fnum) } else if (c == '<' || c == '>') { /* start/end of visual area */ startp = &buf->b_visual.vi_start; endp = &buf->b_visual.vi_end; - if ((c == '<') == lt(*startp, *endp)) + if (((c == '<') == lt(*startp, *endp) || endp->lnum == 0) + && startp->lnum != 0) { posp = startp; - else + } else { posp = endp; - /* - * For Visual line mode, set mark at begin or end of line - */ + } + + // For Visual line mode, set mark at begin or end of line if (buf->b_visual.vi_mode == 'V') { pos_copy = *posp; posp = &pos_copy; @@ -647,8 +650,8 @@ void do_marks(exarg_T *eap) show_one_mark(-1, arg, NULL, NULL, false); } -static void -show_one_mark ( +static void +show_one_mark( int c, char_u *arg, pos_T *p, @@ -687,9 +690,10 @@ show_one_mark ( mustfree = TRUE; } if (name != NULL) { - msg_outtrans_attr(name, current ? hl_attr(HLF_D) : 0); - if (mustfree) + msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0); + if (mustfree) { xfree(name); + } } } ui_flush(); /* show one line at a time */ @@ -800,8 +804,8 @@ void ex_jumps(exarg_T *eap) curwin->w_jumplist[i].fmark.mark.col); msg_outtrans(IObuff); msg_outtrans_attr(name, - curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum - ? hl_attr(HLF_D) : 0); + curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum + ? HL_ATTR(HLF_D) : 0); xfree(name); os_breakcheck(); } @@ -826,7 +830,7 @@ void ex_changes(exarg_T *eap) int i; char_u *name; - /* Highlight title */ + // Highlight title MSG_PUTS_TITLE(_("\nchange line col text")); for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i) { @@ -842,7 +846,7 @@ void ex_changes(exarg_T *eap) curbuf->b_changelist[i].mark.col); msg_outtrans(IObuff); name = mark_line(&curbuf->b_changelist[i].mark, 17); - msg_outtrans_attr(name, hl_attr(HLF_D)); + msg_outtrans_attr(name, HL_ATTR(HLF_D)); xfree(name); os_breakcheck(); } diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index a52ab9f5d3..05e326104b 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -37,6 +37,8 @@ #ifdef HAVE_LOCALE_H # include <locale.h> #endif +#include "nvim/eval.h" +#include "nvim/path.h" #include "nvim/iconv.h" #include "nvim/mbyte.h" #include "nvim/charset.h" @@ -72,6 +74,9 @@ struct interval { # include "unicode_tables.generated.h" #endif +char_u e_loadlib[] = "E370: Could not load library %s"; +char_u e_loadfunc[] = "E448: Could not load library function %s"; + // To speed up BYTELEN(); keep a lookup table to quickly get the length in // bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes // which are illegal when used as the first byte have a 1. The NUL byte has @@ -2038,9 +2043,10 @@ void * my_iconv_open(char_u *to, char_u *from) return (void *)-1; /* detected a broken iconv() previously */ #ifdef DYNAMIC_ICONV - /* Check if the iconv.dll can be found. */ - if (!iconv_enabled(true)) + // Check if the iconv.dll can be found. + if (!iconv_enabled(true)) { return (void *)-1; + } #endif fd = iconv_open((char *)enc_skip(to), (char *)enc_skip(from)); @@ -2162,7 +2168,7 @@ static HINSTANCE hMsvcrtDLL = 0; # ifndef DYNAMIC_ICONV_DLL # define DYNAMIC_ICONV_DLL "iconv.dll" -# define DYNAMIC_ICONV_DLL_ALT "libiconv.dll" +# define DYNAMIC_ICONV_DLL_ALT "libiconv-2.dll" # endif # ifndef DYNAMIC_MSVCRT_DLL # define DYNAMIC_MSVCRT_DLL "msvcrt.dll" @@ -2208,6 +2214,35 @@ static void * get_iconv_import_func(HINSTANCE hInst, return NULL; } +// Load library "name". +HINSTANCE vimLoadLib(char *name) +{ + HINSTANCE dll = NULL; + + // NOTE: Do not use mch_dirname() and mch_chdir() here, they may call + // vimLoadLib() recursively, which causes a stack overflow. + WCHAR old_dirw[MAXPATHL]; + + // Path to exe dir. + char *buf = xstrdup((char *)get_vim_var_str(VV_PROGPATH)); + // ptrdiff_t len = ; + // assert(len > 0); + buf[path_tail_with_sep(buf) - buf] = '\0'; + + if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0) { + // Change directory to where the executable is, both to make + // sure we find a .dll there and to avoid looking for a .dll + // in the current directory. + SetCurrentDirectory((LPCSTR)buf); + // TODO(justinmk): use uv_dlopen instead. see os_libcall + dll = LoadLibrary(name); + SetCurrentDirectoryW(old_dirw); + } + + return dll; +} + + /* * Try opening the iconv.dll and return TRUE if iconv() can be used. */ @@ -2255,10 +2290,13 @@ bool iconv_enabled(bool verbose) void iconv_end(void) { - if (hIconvDLL != 0) + if (hIconvDLL != 0) { + // TODO(justinmk): use uv_dlclose instead. FreeLibrary(hIconvDLL); - if (hMsvcrtDLL != 0) + } + if (hMsvcrtDLL != 0) { FreeLibrary(hMsvcrtDLL); + } hIconvDLL = 0; hMsvcrtDLL = 0; } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 3b0cac0456..0449af1e2b 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -767,7 +767,7 @@ void ml_recover(void) recoverymode = TRUE; called_from_main = (curbuf->b_ml.ml_mfp == NULL); - attr = hl_attr(HLF_E); + attr = HL_ATTR(HLF_E); /* * If the file name ends in ".s[uvw][a-z]" we assume this is the swap file. diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 42417f75d5..1bbd07686b 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -825,8 +825,8 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth) msg_outnum((long)menu->priority); MSG_PUTS(" "); } - /* Same highlighting as for directories!? */ - msg_outtrans_attr(menu->name, hl_attr(HLF_D)); + // Same highlighting as for directories!? + msg_outtrans_attr(menu->name, HL_ATTR(HLF_D)); } if (menu != NULL && menu->children == NULL) { @@ -854,7 +854,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth) msg_putchar(' '); MSG_PUTS(" "); if (*menu->strings[bit] == NUL) { - msg_puts_attr("<Nop>", hl_attr(HLF_8)); + msg_puts_attr("<Nop>", HL_ATTR(HLF_8)); } else { msg_outtrans_special(menu->strings[bit], false); } diff --git a/src/nvim/message.c b/src/nvim/message.c index 63accaaa23..7935bcbc2f 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -286,7 +286,7 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) half = i = (int)STRLEN(s); for (;;) { do { - half = half - (*mb_head_off)(s, s + half - 1) - 1; + half = half - utf_head_off(s, s + half - 1) - 1; } while (half > 0 && utf_iscomposing(utf_ptr2char(s + half))); n = ptr2cells(s + half); if (len + n > room || half == 0) { @@ -434,7 +434,7 @@ void msg_source(int attr) } p = get_emsg_lnum(); if (p != NULL) { - msg_attr(p, hl_attr(HLF_N)); + msg_attr(p, HL_ATTR(HLF_N)); xfree(p); last_sourcing_lnum = sourcing_lnum; /* only once for each line */ } @@ -557,7 +557,7 @@ int emsg(const char_u *s_) emsg_on_display = true; // remember there is an error message msg_scroll++; // don't overwrite a previous message - attr = hl_attr(HLF_E); // set highlight mode for error messages + attr = HL_ATTR(HLF_E); // set highlight mode for error messages if (msg_scrolled != 0) { need_wait_return = true; // needed in case emsg() is called after } // wait_return has reset need_wait_return @@ -1034,9 +1034,10 @@ static void hit_return_msg(void) if (got_int) MSG_PUTS(_("Interrupt: ")); - MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), hl_attr(HLF_R)); - if (!msg_use_printf()) + MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R)); + if (!msg_use_printf()) { msg_clr_eos(); + } p_more = save_p_more; } @@ -1140,7 +1141,7 @@ void msg_home_replace(char_u *fname) void msg_home_replace_hl(char_u *fname) { - msg_home_replace_attr(fname, hl_attr(HLF_D)); + msg_home_replace_attr(fname, HL_ATTR(HLF_D)); } static void msg_home_replace_attr(char_u *fname, int attr) @@ -1230,7 +1231,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr) } plain_start = str + mb_l; msg_puts_attr((const char *)transchar(c), - (attr == 0 ? hl_attr(HLF_8) : attr)); + (attr == 0 ? HL_ATTR(HLF_8) : attr)); retval += char2cells(c); } len -= mb_l - 1; @@ -1244,7 +1245,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr) msg_puts_attr_len(plain_start, str - plain_start, attr); } plain_start = str + 1; - msg_puts_attr((const char *)s, attr == 0 ? hl_attr(HLF_8) : attr); + msg_puts_attr((const char *)s, attr == 0 ? HL_ATTR(HLF_8) : attr); retval += (int)STRLEN(s); } else { retval++; @@ -1299,7 +1300,7 @@ int msg_outtrans_special( } const char_u *str = strstart; int retval = 0; - int attr = hl_attr(HLF_8); + int attr = HL_ATTR(HLF_8); while (*str != NUL) { const char *string; @@ -1501,18 +1502,18 @@ void msg_prt_line(char_u *s, int list) } else { c = lcs_tab1; c_extra = lcs_tab2; - attr = hl_attr(HLF_8); + attr = HL_ATTR(HLF_8); } } else if (c == 160 && list && lcs_nbsp != NUL) { c = lcs_nbsp; - attr = hl_attr(HLF_8); + attr = HL_ATTR(HLF_8); } else if (c == NUL && list && lcs_eol != NUL) { p_extra = (char_u *)""; c_extra = NUL; n_extra = 1; c = lcs_eol; - attr = hl_attr(HLF_AT); - --s; + attr = HL_ATTR(HLF_AT); + s--; } else if (c != NUL && (n = byte2cells(c)) > 1) { n_extra = n - 1; p_extra = transchar_byte(c); @@ -1520,13 +1521,13 @@ void msg_prt_line(char_u *s, int list) c = *p_extra++; /* Use special coloring to be able to distinguish <hex> from * the same in plain text. */ - attr = hl_attr(HLF_8); + attr = HL_ATTR(HLF_8); } else if (c == ' ' && trail != NULL && s > trail) { c = lcs_trail; - attr = hl_attr(HLF_8); + attr = HL_ATTR(HLF_8); } else if (c == ' ' && list && lcs_space != NUL) { c = lcs_space; - attr = hl_attr(HLF_8); + attr = HL_ATTR(HLF_8); } } @@ -1547,13 +1548,12 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) { int cw; - msg_didout = TRUE; /* remember that line is not empty */ + msg_didout = true; // remember that line is not empty cw = (*mb_ptr2cells)(s); - if (cw > 1 && ( - cmdmsg_rl ? msg_col <= 1 : - msg_col == Columns - 1)) { - /* Doesn't fit, print a highlighted '>' to fill it up. */ - msg_screen_putchar('>', hl_attr(HLF_AT)); + if (cw > 1 + && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) { + // Doesn't fit, print a highlighted '>' to fill it up. + msg_screen_putchar('>', HL_ATTR(HLF_AT)); return s; } @@ -1585,7 +1585,7 @@ void msg_puts(const char *s) void msg_puts_title(const char *s) { - msg_puts_attr(s, hl_attr(HLF_T)); + msg_puts_attr(s, HL_ATTR(HLF_T)); } /* @@ -1607,7 +1607,7 @@ void msg_puts_long_len_attr(char_u *longstr, int len, int attr) if (len > room && room >= 20) { slen = (room - 3) / 2; msg_outtrans_len_attr(longstr, slen, attr); - msg_puts_attr("...", hl_attr(HLF_8)); + msg_puts_attr("...", HL_ATTR(HLF_8)); } msg_outtrans_len_attr(longstr + len - slen, slen, attr); } @@ -1886,7 +1886,7 @@ static void msg_scroll_up(void) if (dy_flags & DY_MSGSEP) { if (msg_scrolled == 0) { screen_fill(Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, - fill_msgsep, fill_msgsep, hl_attr(HLF_MSGSEP)); + fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); } int nscroll = MIN(msg_scrollsize()+1, Rows); ui_call_set_scroll_region(Rows-nscroll, Rows-1, 0, Columns-1); @@ -2437,7 +2437,7 @@ void msg_moremsg(int full) int attr; char_u *s = (char_u *)_("-- More --"); - attr = hl_attr(HLF_M); + attr = HL_ATTR(HLF_M); screen_puts(s, (int)Rows - 1, 0, attr); if (full) screen_puts((char_u *) @@ -2720,7 +2720,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) xfree(keep_msg); keep_msg = NULL; if (hl) { - keep_msg_attr = hl_attr(HLF_W); + keep_msg_attr = HL_ATTR(HLF_W); } else { keep_msg_attr = 0; } @@ -3069,7 +3069,7 @@ void display_confirm_msg(void) // Avoid that 'q' at the more prompt truncates the message here. confirm_msg_used++; if (confirm_msg != NULL) { - msg_puts_attr((const char *)confirm_msg, hl_attr(HLF_M)); + msg_puts_attr((const char *)confirm_msg, HL_ATTR(HLF_M)); } confirm_msg_used--; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 28455f0ba9..a5da9d3220 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -28,6 +28,7 @@ #include "nvim/getchar.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/buffer_updates.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -106,7 +107,8 @@ open_line ( char_u *p; char_u saved_char = NUL; // init for GCC pos_T *pos; - bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin); + bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin + && *curbuf->b_p_inde == NUL); bool no_si = false; // reset did_si afterwards int first_char = NUL; // init for GCC int vreplace_mode; @@ -835,8 +837,8 @@ open_line ( saved_line = NULL; if (did_append) { changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col, - curwin->w_cursor.lnum + 1, 1L); - did_append = FALSE; + curwin->w_cursor.lnum + 1, 1L, true); + did_append = false; /* Move marks after the line break to the new line. */ if (flags & OPENLINE_MARKFIX) @@ -853,8 +855,9 @@ open_line ( */ curwin->w_cursor.lnum = old_cursor.lnum + 1; } - if (did_append) - changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L); + if (did_append) { + changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true); + } curwin->w_cursor.col = newcol; curwin->w_cursor.coladd = 0; @@ -1819,6 +1822,10 @@ void changed_bytes(linenr_T lnum, colnr_T col) { changedOneline(curbuf, lnum); changed_common(lnum, col, lnum + 1, 0L); + // notify any channels that are watching + if (kv_size(curbuf->update_channels)) { + buf_updates_send_changes(curbuf, lnum, 1, 1, true); + } /* Diff highlighting in other diff windows may need to be updated too. */ if (curwin->w_p_diff) { @@ -1859,7 +1866,7 @@ static void changedOneline(buf_T *buf, linenr_T lnum) */ void appended_lines(linenr_T lnum, long count) { - changed_lines(lnum + 1, 0, lnum + 1, count); + changed_lines(lnum + 1, 0, lnum + 1, count, true); } /* @@ -1872,7 +1879,7 @@ void appended_lines_mark(linenr_T lnum, long count) if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) { mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false); } - changed_lines(lnum + 1, 0, lnum + 1, count); + changed_lines(lnum + 1, 0, lnum + 1, count, true); } /* @@ -1882,7 +1889,7 @@ void appended_lines_mark(linenr_T lnum, long count) */ void deleted_lines(linenr_T lnum, long count) { - changed_lines(lnum, 0, lnum + count, -count); + changed_lines(lnum, 0, lnum + count, -count, true); } /* @@ -1893,7 +1900,7 @@ void deleted_lines(linenr_T lnum, long count) void deleted_lines_mark(linenr_T lnum, long count) { mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, -count, false); - changed_lines(lnum, 0, lnum + count, -count); + changed_lines(lnum, 0, lnum + count, -count, true); } /* @@ -1908,12 +1915,16 @@ void deleted_lines_mark(linenr_T lnum, long count) * Takes care of calling changed() and updating b_mod_*. * Careful: may trigger autocommands that reload the buffer. */ -void -changed_lines ( - linenr_T lnum, /* first line with change */ - colnr_T col, /* column in first line with change */ - linenr_T lnume, /* line below last changed line */ - long xtra /* number of extra lines (negative when deleting) */ +void +changed_lines( + linenr_T lnum, // first line with change + colnr_T col, // column in first line with change + linenr_T lnume, // line below last changed line + long xtra, // number of extra lines (negative when deleting) + bool do_buf_event // some callers like undo/redo call changed_lines() + // and then increment b_changedtick *again*. This flag + // allows these callers to send the nvim_buf_lines_event + // events after they're done modifying b_changedtick. ) { changed_lines_buf(curbuf, lnum, lnume, xtra); @@ -1937,6 +1948,12 @@ changed_lines ( } changed_common(lnum, col, lnume, xtra); + + if (do_buf_event && kv_size(curbuf->update_channels)) { + int64_t num_added = (int64_t)(lnume + xtra - lnum); + int64_t num_removed = lnume - lnum; + buf_updates_send_changes(curbuf, lnum, num_added, num_removed, true); + } } /// Mark line range in buffer as changed. @@ -2202,8 +2219,8 @@ change_warning ( msg_start(); if (msg_row == Rows - 1) msg_col = col; - msg_source(hl_attr(HLF_W)); - MSG_PUTS_ATTR(_(w_readonly), hl_attr(HLF_W) | MSG_HIST); + msg_source(HL_ATTR(HLF_W)); + MSG_PUTS_ATTR(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST); set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); msg_clr_eos(); (void)msg_end(); @@ -2243,7 +2260,7 @@ int ask_yesno(const char *const str, const bool direct) int r = ' '; while (r != 'y' && r != 'n') { // Same highlighting as for wait_return. - smsg_attr(hl_attr(HLF_R), "%s (y/n)?", str); + smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str); if (direct) { r = get_keystroke(); } else { @@ -2549,8 +2566,8 @@ void vim_beep(unsigned val) /* When 'verbose' is set and we are sourcing a script or executing a * function give the user a hint where the beep comes from. */ if (vim_strchr(p_debug, 'e') != NULL) { - msg_source(hl_attr(HLF_W)); - msg_attr(_("Beep!"), hl_attr(HLF_W)); + msg_source(HL_ATTR(HLF_W)); + msg_attr(_("Beep!"), HL_ATTR(HLF_W)); } } } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 26b84b7cc7..6d0c270a51 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -312,24 +312,30 @@ static void handle_request(Channel *channel, msgpack_object *request) api_clear_error(&error); return; } + // Retrieve the request handler MsgpackRpcRequestHandler handler; + Array args = ARRAY_DICT_INIT; msgpack_object *method = msgpack_rpc_method(request); if (method) { handler = msgpack_rpc_get_handler_for(method->via.bin.ptr, method->via.bin.size); + if (handler.fn == msgpack_rpc_handle_missing_method) { + String m = method->via.bin.size > 0 + ? cbuf_to_string(method->via.bin.ptr, method->via.bin.size) + : cstr_to_string("<empty>"); + ADD(args, STRING_OBJ(m)); + handler.async = true; + } else if (!msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { + handler.fn = msgpack_rpc_handle_invalid_arguments; + handler.async = true; + } } else { handler.fn = msgpack_rpc_handle_missing_method; handler.async = true; } - Array args = ARRAY_DICT_INIT; - if (!msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { - handler.fn = msgpack_rpc_handle_invalid_arguments; - handler.async = true; - } - RequestEvent *evdata = xmalloc(sizeof(RequestEvent)); evdata->channel = channel; evdata->handler = handler; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index fecae11d45..e18c4472b5 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -493,7 +493,8 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, Array args, Error *error) { - api_set_error(error, kErrorTypeException, "Invalid method name"); + api_set_error(error, kErrorTypeException, "Invalid method: %s", + args.size > 0 ? args.items[0].data.string.data : "?"); return NIL; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a2aaf8f9af..a7c4c255b7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2933,8 +2933,9 @@ void check_visual_highlight(void) static bool did_check = false; if (full_screen) { - if (!did_check && hl_attr(HLF_V) == 0) + if (!did_check && HL_ATTR(HLF_V) == 0) { MSG(_("Warning: terminal cannot highlight")); + } did_check = true; } } @@ -6140,7 +6141,7 @@ static void n_swapchar(cmdarg_T *cap) curwin->w_set_curswant = true; if (did_change) { changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, - 0L); + 0L, true); curbuf->b_op_start = startpos; curbuf->b_op_end = curwin->w_cursor; if (curbuf->b_op_end.col > 0) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d874768dfc..45de76f80a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -214,7 +214,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) ++curwin->w_cursor.lnum; } - changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); + changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); if (oap->motion_type == kMTBlockWise) { curwin->w_cursor.lnum = oap->start.lnum; @@ -570,7 +570,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def } } /* for all lnum */ - changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L); + changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L, true); State = oldstate; } @@ -632,12 +632,13 @@ void op_reindent(oparg_T *oap, Indenter how) /* Mark changed lines so that they will be redrawn. When Visual * highlighting was present, need to continue until the last line. When * there is no change still need to remove the Visual highlighting. */ - if (last_changed != 0) + if (last_changed != 0) { changed_lines(first_changed, 0, - oap->is_VIsual ? start_lnum + oap->line_count : - last_changed + 1, 0L); - else if (oap->is_VIsual) + oap->is_VIsual ? start_lnum + oap->line_count : + last_changed + 1, 0L, true); + } else if (oap->is_VIsual) { redraw_curbuf_later(INVERTED); + } if (oap->line_count > p_report) { i = oap->line_count - (i + 1); @@ -1455,7 +1456,7 @@ int op_delete(oparg_T *oap) check_cursor_col(); changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col, - oap->end.lnum + 1, 0L); + oap->end.lnum + 1, 0L, true); oap->line_count = 0; // no lines deleted } else if (oap->motion_type == kMTLineWise) { if (oap->op_type == OP_CHANGE) { @@ -1822,7 +1823,7 @@ int op_replace(oparg_T *oap, int c) curwin->w_cursor = oap->start; check_cursor(); - changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); + changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true); /* Set "'[" and "']" marks. */ curbuf->b_op_start = oap->start; @@ -1856,8 +1857,9 @@ void op_tilde(oparg_T *oap) did_change |= one_change; } - if (did_change) - changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); + if (did_change) { + changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); + } } else { // not block mode if (oap->motion_type == kMTLineWise) { oap->start.col = 0; @@ -1881,7 +1883,7 @@ void op_tilde(oparg_T *oap) } if (did_change) { changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, - 0L); + 0L, true); } } @@ -2264,7 +2266,7 @@ int op_change(oparg_T *oap) } } check_cursor(); - changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L); + changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L, true); xfree(ins_text); } } @@ -3033,7 +3035,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curwin->w_cursor.col += bd.startspaces; } - changed_lines(lnum, 0, curwin->w_cursor.lnum, nr_lines); + changed_lines(lnum, 0, curwin->w_cursor.lnum, nr_lines, true); /* Set '[ mark. */ curbuf->b_op_start = curwin->w_cursor; @@ -3210,10 +3212,10 @@ error: // note changed text for displaying and folding if (y_type == kMTCharWise) { changed_lines(curwin->w_cursor.lnum, col, - curwin->w_cursor.lnum + 1, nr_lines); + curwin->w_cursor.lnum + 1, nr_lines, true); } else { changed_lines(curbuf->b_op_start.lnum, 0, - curbuf->b_op_start.lnum, nr_lines); + curbuf->b_op_start.lnum, nr_lines, true); } /* put '] mark at last inserted character */ @@ -3332,7 +3334,7 @@ void ex_display(exarg_T *eap) if (arg != NULL && *arg == NUL) arg = NULL; - int attr = hl_attr(HLF_8); + int attr = HL_ATTR(HLF_8); /* Highlight title */ MSG_PUTS_TITLE(_("\n--- Registers ---")); @@ -3693,7 +3695,7 @@ int do_join(size_t count, /* Only report the change in the first line here, del_lines() will report * the deleted line. */ changed_lines(curwin->w_cursor.lnum, currsize, - curwin->w_cursor.lnum + 1, 0L); + curwin->w_cursor.lnum + 1, 0L, true); /* * Delete following lines. To do this we move the cursor there @@ -4363,7 +4365,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) } change_cnt = do_addsub(oap->op_type, &pos, 0, amount); if (change_cnt) { - changed_lines(pos.lnum, 0, pos.lnum + 1, 0L); + changed_lines(pos.lnum, 0, pos.lnum + 1, 0L, true); } } else { int one_change; @@ -4419,7 +4421,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) } } if (change_cnt) { - changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L); + changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); } if (!change_cnt && oap->is_VIsual) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 26fc164c6c..882289c8b8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3963,8 +3963,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, static char *w_arabic = N_( "W17: Arabic requires UTF-8, do ':set encoding=utf-8'"); - msg_source(hl_attr(HLF_W)); - msg_attr(_(w_arabic), hl_attr(HLF_W)); + msg_source(HL_ATTR(HLF_W)); + msg_attr(_(w_arabic), HL_ATTR(HLF_W)); set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1); } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 7fb4a93b54..25c4cc4f92 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -373,11 +373,10 @@ void expand_env_esc(char_u *restrict srcp, *var++ = *tail++; } *var = NUL; - // Use os_get_user_directory() to get the user directory. - // If this function fails, the shell is used to - // expand ~user. This is slower and may fail if the shell - // does not support ~user (old versions of /bin/sh). - var = (char_u *)os_get_user_directory((char *)dst + 1); + // Get the user directory. If this fails the shell is used to expand + // ~user, which is slower and may fail on old versions of /bin/sh. + var = (*dst == NUL) ? NULL + : (char_u *)os_get_user_directory((char *)dst + 1); mustfree = true; if (var == NULL) { expand_T xpc; diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 82bb918f70..c6463c2c92 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -75,11 +75,10 @@ int os_get_uname(uv_uid_t uid, char *s, size_t len) char *os_get_user_directory(const char *name) { #if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) - struct passwd *pw; - if (name == NULL) { + if (name == NULL || *name == NUL) { return NULL; } - pw = getpwnam(name); // NOLINT(runtime/threadsafe_fn) + struct passwd *pw = getpwnam(name); // NOLINT(runtime/threadsafe_fn) if (pw != NULL) { // save the string from the static passwd entry into malloced memory return xstrdup(pw->pw_dir); diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index db93f016bf..356094baa1 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -59,7 +59,6 @@ #define BACKSLASH_IN_FILENAME #ifdef _MSC_VER -typedef SSIZE_T ssize_t; typedef int mode_t; #endif diff --git a/src/nvim/path.c b/src/nvim/path.c index 4f3f7c0661..61cfaea84a 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1097,17 +1097,18 @@ static bool has_env_var(char_u *p) } #ifdef SPECIAL_WILDCHAR -/* - * Return TRUE if "p" contains a special wildcard character. - * Allowing for escaping. - */ + +// Return TRUE if "p" contains a special wildcard character, one that Vim +// cannot expand, requires using a shell. static bool has_special_wildchar(char_u *p) { for (; *p; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) + // Allow for escaping + if (*p == '\\' && p[1] != NUL) { + p++; + } else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) { return true; + } } return false; } @@ -2033,7 +2034,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, break; } if (match_file_list(p_wig, (*files)[i], ffname)) { - // remove this matching files from the list + // remove this matching file from the list xfree((*files)[i]); for (j = i; j + 1 < *num_files; j++) { (*files)[j] = (*files)[j + 1]; diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 94cc63baea..a7b910f0eb 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Gettext) +find_package(Gettext REQUIRED) find_program(XGETTEXT_PRG xgettext) find_program(ICONV_PRG iconv) diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1c3cb5d6b2..aeb27a5cac 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2260,7 +2260,7 @@ void qf_list(exarg_T *eap) vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)fname); msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index - ? hl_attr(HLF_QFL) : hl_attr(HLF_D)); + ? HL_ATTR(HLF_QFL) : HL_ATTR(HLF_D)); if (qfp->qf_lnum == 0) { IObuff[0] = NUL; } else if (qfp->qf_col == 0) { @@ -2271,7 +2271,7 @@ void qf_list(exarg_T *eap) } vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s:", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); - msg_puts_attr((const char *)IObuff, hl_attr(HLF_N)); + msg_puts_attr((const char *)IObuff, HL_ATTR(HLF_N)); if (qfp->qf_pattern != NULL) { qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE); xstrlcat((char *)IObuff, ":", IOSIZE); diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index ee7d6d8500..ef02b6529c 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -479,6 +479,8 @@ static char_u *regprop(char_u *); #endif static char_u e_missingbracket[] = N_("E769: Missing ] after %s["); +static char_u e_reverse_range[] = N_("E944: Reverse range in character class"); +static char_u e_large_class[] = N_("E945: Range too large in character class"); static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%("); static char_u e_unmatchedp[] = N_("E54: Unmatched %s("); static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)"); @@ -2232,15 +2234,18 @@ collection: if (endc == '\\' && !reg_cpo_lit) endc = coll_get_char(); - if (startc > endc) - EMSG_RET_NULL(_(e_invrange)); + if (startc > endc) { + EMSG_RET_NULL(_(e_reverse_range)); + } if (has_mbyte && ((*mb_char2len)(startc) > 1 || (*mb_char2len)(endc) > 1)) { - /* Limit to a range of 256 chars */ - if (endc > startc + 256) - EMSG_RET_NULL(_(e_invrange)); - while (++startc <= endc) + // Limit to a range of 256 chars + if (endc > startc + 256) { + EMSG_RET_NULL(_(e_large_class)); + } + while (++startc <= endc) { regmbc(startc); + } } else { while (++startc <= endc) regc(startc); @@ -4241,26 +4246,28 @@ regmatch ( int opndc = 0, inpc; opnd = OPERAND(scan); - /* Safety check (just in case 'encoding' was changed since - * compiling the program). */ + // Safety check (just in case 'encoding' was changed since + // compiling the program). if ((len = (*mb_ptr2len)(opnd)) < 2) { status = RA_NOMATCH; break; } - if (enc_utf8) - opndc = mb_ptr2char(opnd); + if (enc_utf8) { + opndc = utf_ptr2char(opnd); + } if (enc_utf8 && utf_iscomposing(opndc)) { /* When only a composing char is given match at any * position where that composing char appears. */ status = RA_NOMATCH; for (i = 0; reginput[i] != NUL; i += utf_ptr2len(reginput + i)) { - inpc = mb_ptr2char(reginput + i); + inpc = utf_ptr2char(reginput + i); if (!utf_iscomposing(inpc)) { - if (i > 0) + if (i > 0) { break; + } } else if (opndc == inpc) { - /* Include all following composing chars. */ - len = i + mb_ptr2len(reginput + i); + // Include all following composing chars. + len = i + utfc_ptr2len(reginput + i); status = RA_MATCH; break; } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 0b8e979ca2..c2b1b97ce9 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1711,8 +1711,9 @@ collection: if (emit_range) { endc = startc; startc = oldstartc; - if (startc > endc) - EMSG_RET_FAIL(_(e_invrange)); + if (startc > endc) { + EMSG_RET_FAIL(_(e_reverse_range)); + } if (endc > startc + 2) { /* Emit a range instead of the sequence of @@ -1804,9 +1805,9 @@ collection: int plen; nfa_do_multibyte: - /* plen is length of current char with composing chars */ + // plen is length of current char with composing chars if (enc_utf8 && ((*mb_char2len)(c) - != (plen = (*mb_ptr2len)(old_regparse)) + != (plen = utfc_ptr2len(old_regparse)) || utf_iscomposing(c))) { int i = 0; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 4299002084..f36d408b25 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3106,9 +3106,9 @@ win_line ( if (n_extra > 0) { if (c_extra != NUL) { c = c_extra; - mb_c = c; /* doesn't handle non-utf-8 multi-byte! */ - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + mb_c = c; // doesn't handle non-utf-8 multi-byte! + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } else @@ -3118,15 +3118,15 @@ win_line ( if (has_mbyte) { mb_c = c; if (enc_utf8) { - /* If the UTF-8 character is more than one byte: - * Decode it into "mb_c". */ - mb_l = (*mb_ptr2len)(p_extra); - mb_utf8 = FALSE; - if (mb_l > n_extra) + // If the UTF-8 character is more than one byte: + // Decode it into "mb_c". + mb_l = utfc_ptr2len(p_extra); + mb_utf8 = false; + if (mb_l > n_extra) { mb_l = 1; - else if (mb_l > 1) { + } else if (mb_l > 1) { mb_c = utfc_ptr2char(p_extra, u8cc); - mb_utf8 = TRUE; + mb_utf8 = true; c = 0xc0; } } else { @@ -3177,10 +3177,10 @@ win_line ( if (has_mbyte) { mb_c = c; if (enc_utf8) { - /* If the UTF-8 character is more than one byte: Decode it - * into "mb_c". */ - mb_l = (*mb_ptr2len)(ptr); - mb_utf8 = FALSE; + // If the UTF-8 character is more than one byte: Decode it + // into "mb_c". + mb_l = utfc_ptr2len(ptr); + mb_utf8 = false; if (mb_l > 1) { mb_c = utfc_ptr2char(ptr, u8cc); // Overlong encoded ASCII or ASCII with composing char @@ -3486,7 +3486,7 @@ win_line ( extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { + if (enc_utf8 && utf_char2len(c) > 1) { mb_utf8 = true; u8cc[0] = 0; c = 0xc0; @@ -3501,12 +3501,13 @@ win_line ( extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; - } else - mb_utf8 = FALSE; + } else { + mb_utf8 = false; + } } } @@ -3602,8 +3603,8 @@ win_line ( extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } @@ -3647,8 +3648,8 @@ win_line ( extra_attr = win_hl_attr(wp, HLF_AT); n_attr = 1; mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } else @@ -3762,8 +3763,8 @@ win_line ( n_skip = 1; } mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } else @@ -3816,8 +3817,8 @@ win_line ( extra_attr = win_hl_attr(wp, HLF_AT); } mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } else { @@ -4044,8 +4045,8 @@ win_line ( c = lcs_ext; char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; - if (enc_utf8 && (*mb_char2len)(c) > 1) { - mb_utf8 = TRUE; + if (enc_utf8 && utf_char2len(c) > 1) { + mb_utf8 = true; u8cc[0] = 0; c = 0xc0; } else @@ -4061,7 +4062,8 @@ win_line ( * Also highlight the 'colorcolumn' if it is different than * 'cursorcolumn' */ vcol_save_attr = -1; - if (draw_state == WL_LINE && !lnum_in_visual_area) { + if (draw_state == WL_LINE && !lnum_in_visual_area + && search_attr == 0 && area_attr == 0) { if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol && lnum != wp->w_cursor.lnum) { vcol_save_attr = char_attr; @@ -4889,7 +4891,7 @@ win_redr_status_matches ( screen_puts(buf, row, 0, attr); if (selstart != NULL && highlight) { *selend = NUL; - screen_puts(selstart, row, selstart_col, hl_attr(HLF_WM)); + screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM)); } screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr); @@ -5154,7 +5156,7 @@ win_redr_custom ( stl = p_tal; row = 0; fillchar = ' '; - attr = hl_attr(HLF_TPF); + attr = HL_ATTR(HLF_TPF); maxwidth = Columns; use_sandbox = was_set_insecurely((char_u *)"tabline", 0); } else { @@ -5567,7 +5569,7 @@ static void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal = 0; } if (wp != curwin) { - wp->w_hl_attr_normal = hl_combine_attr(hl_attr(HLF_INACTIVE), + wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE), wp->w_hl_attr_normal); } @@ -5576,7 +5578,7 @@ static void update_window_hl(win_T *wp, bool invalid) if (wp->w_hl_ids[hlf] > 0) { attr = syn_id2attr(wp->w_hl_ids[hlf]); } else { - attr = hl_attr(hlf); + attr = HL_ATTR(hlf); } if (wp->w_hl_attr_normal != 0) { attr = hl_combine_attr(wp->w_hl_attr_normal, attr); @@ -6653,7 +6655,7 @@ int showmode(void) /* Position on the last line in the window, column 0 */ msg_pos_mode(); - attr = hl_attr(HLF_CM); /* Highlight mode */ + attr = HL_ATTR(HLF_CM); // Highlight mode if (do_mode) { MSG_PUTS_ATTR("--", attr); // CTRL-X in Insert mode @@ -6801,7 +6803,7 @@ void clearmode(void) { msg_pos_mode(); if (Recording) { - recording_mode(hl_attr(HLF_CM)); + recording_mode(HL_ATTR(HLF_CM)); } msg_clr_eos(); } @@ -6832,8 +6834,8 @@ static void draw_tabline(void) int modified; int c; int len; - int attr_nosel = hl_attr(HLF_TP); - int attr_fill = hl_attr(HLF_TPF); + int attr_nosel = HL_ATTR(HLF_TP); + int attr_fill = HL_ATTR(HLF_TPF); char_u *p; int room; int use_sep_chars = (t_colors < 8 diff --git a/src/nvim/search.c b/src/nvim/search.c index 84782497a0..cb59eb6d04 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -130,8 +130,8 @@ typedef struct SearchedFile { * * returns FAIL if failed, OK otherwise. */ -int -search_regcomp ( +int +search_regcomp( char_u *pat, int pat_save, int pat_use, @@ -2121,9 +2121,9 @@ static int check_linecomment(char_u *line) * Show the match only if it is visible on the screen. * If there isn't a match, then beep. */ -void -showmatch ( - int c /* char to show match for */ +void +showmatch( + int c // char to show match for ) { pos_T *lpos, save_cursor; @@ -2377,8 +2377,14 @@ findpar ( ++curr; curwin->w_cursor.lnum = curr; if (curr == curbuf->b_ml.ml_line_count && what != '}') { - if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0) { - --curwin->w_cursor.col; + char_u *line = ml_get(curr); + + // Put the cursor on the last character in the last line and make the + // motion inclusive. + if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) { + curwin->w_cursor.col--; + curwin->w_cursor.col -= + (*mb_head_off)(line, line + curwin->w_cursor.col); *pincl = true; } } else @@ -2483,8 +2489,8 @@ static int cls(void) * Returns FAIL if the cursor was already at the end of the file. * If eol is TRUE, last word stops at end of line (for operators). */ -int -fwd_word ( +int +fwd_word( long count, int bigword, /* "W", "E" or "B" */ int eol @@ -2666,8 +2672,8 @@ finished: * * Returns FAIL if start of the file was reached. */ -int -bckend_word ( +int +bckend_word( long count, int bigword, /* TRUE for "B" */ int eol /* TRUE: stop at end of line. */ @@ -2756,8 +2762,8 @@ static void find_first_blank(pos_T *posp) /* * Skip count/2 sentences and count/2 separating white spaces. */ -static void -findsent_forward ( +static void +findsent_forward( long count, int at_start_sent /* cursor is at start of sentence */ ) @@ -2776,8 +2782,8 @@ findsent_forward ( * Find word under cursor, cursor at end. * Used while an operator is pending, and in Visual mode. */ -int -current_word ( +int +current_word( oparg_T *oap, long count, int include, /* TRUE: include word and white space */ @@ -3084,8 +3090,8 @@ extend: * Find block under the cursor, cursor at end. * "what" and "other" are two matching parenthesis/brace/etc. */ -int -current_block ( +int +current_block( oparg_T *oap, long count, int include, /* TRUE == include white space */ @@ -3282,8 +3288,8 @@ static int in_html_tag(int end_tag) /* * Find tag block under the cursor, cursor at end. */ -int -current_tagblock ( +int +current_tagblock( oparg_T *oap, long count_arg, int include /* TRUE == include white space */ @@ -3465,8 +3471,8 @@ theend: return retval; } -int -current_par ( +int +current_par( oparg_T *oap, long count, int include, /* TRUE == include white space */ @@ -3632,8 +3638,8 @@ extend: * as a quote. * Returns column number of "quotechar" or -1 when not found. */ -static int -find_next_quote ( +static int +find_next_quote( char_u *line, int col, int quotechar, @@ -3664,8 +3670,8 @@ find_next_quote ( * as a quote. * Return the found column or zero. */ -static int -find_prev_quote ( +static int +find_prev_quote( char_u *line, int col_start, int quotechar, @@ -3694,8 +3700,8 @@ find_prev_quote ( * Find quote under the cursor, cursor at end. * Returns TRUE if found, else FALSE. */ -int -current_quote ( +int +current_quote( oparg_T *oap, long count, int include, /* TRUE == include quote char */ @@ -3920,8 +3926,8 @@ current_quote ( * Find next search match under cursor, cursor at end. * Used while an operator is pending, and in Visual mode. */ -int -current_search ( +int +current_search( long count, int forward /* move forward or backwards */ ) @@ -4116,19 +4122,19 @@ int linewhite(linenr_T lnum) * Find identifiers or defines in included files. * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. */ -void -find_pattern_in_path ( - char_u *ptr, /* pointer to search pattern */ - int dir, /* direction of expansion */ - size_t len, /* length of search pattern */ - int whole, /* match whole words only */ - int skip_comments, /* don't match inside comments */ - int type, /* Type of search; are we looking for a type? - a macro? */ +void +find_pattern_in_path( + char_u *ptr, // pointer to search pattern + int dir, // direction of expansion + size_t len, // length of search pattern + int whole, // match whole words only + int skip_comments, // don't match inside comments + int type, // Type of search; are we looking for a type? + // a macro? long count, - int action, /* What to do when we find it */ - linenr_T start_lnum, /* first line to start searching */ - linenr_T end_lnum /* last line for searching */ + int action, // What to do when we find it + linenr_T start_lnum, // first line to start searching + linenr_T end_lnum // last line for searching ) { SearchedFile *files; /* Stack of included files */ @@ -4282,7 +4288,7 @@ find_pattern_in_path ( if (new_fname != NULL) { /* using "new_fname" is more reliable, e.g., when * 'includeexpr' is set. */ - msg_outtrans_attr(new_fname, hl_attr(HLF_D)); + msg_outtrans_attr(new_fname, HL_ATTR(HLF_D)); } else { /* * Isolate the file name. @@ -4320,7 +4326,7 @@ find_pattern_in_path ( } save_char = p[i]; p[i] = NUL; - msg_outtrans_attr(p, hl_attr(HLF_D)); + msg_outtrans_attr(p, HL_ATTR(HLF_D)); p[i] = save_char; } @@ -4367,11 +4373,11 @@ find_pattern_in_path ( files[depth].lnum = 0; files[depth].matched = FALSE; if (action == ACTION_EXPAND) { - msg_hist_off = TRUE; /* reset in msg_trunc_attr() */ - vim_snprintf((char*)IObuff, IOSIZE, - _("Scanning included file: %s"), - (char *)new_fname); - msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); + msg_hist_off = true; // reset in msg_trunc_attr() + vim_snprintf((char *)IObuff, IOSIZE, + _("Scanning included file: %s"), + (char *)new_fname); + msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); } else if (p_verbose >= 5) { verbose_enter(); smsg(_("Searching included file %s"), @@ -4724,7 +4730,7 @@ static void show_pat_in_path(char_u *line, int type, int did_show, int action, F msg_puts((const char *)IObuff); snprintf((char *)IObuff, IOSIZE, "%4ld", *lnum); // Show line nr. // Highlight line numbers. - msg_puts_attr((const char *)IObuff, hl_attr(HLF_N)); + msg_puts_attr((const char *)IObuff, HL_ATTR(HLF_N)); msg_puts(" "); } msg_prt_line(line, FALSE); diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 686962704a..0db1578e8d 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1031,8 +1031,9 @@ static bool can_compound(slang_T *slang, char_u *word, char_u *flags) if (enc_utf8) { // Need to convert the single byte flags to utf8 characters. p = uflags; - for (i = 0; flags[i] != NUL; ++i) - p += mb_char2bytes(flags[i], p); + for (i = 0; flags[i] != NUL; i++) { + p += utf_char2bytes(flags[i], p); + } *p = NUL; p = uflags; } else @@ -4269,28 +4270,23 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // the score from SCORE_SUBST to // SCORE_SUBCOMP. if (enc_utf8 - && utf_iscomposing( - mb_ptr2char(tword - + sp->ts_twordlen - - sp->ts_tcharlen)) - && utf_iscomposing( - mb_ptr2char(fword - + sp->ts_fcharstart))) - sp->ts_score -= - SCORE_SUBST - SCORE_SUBCOMP; - - // For a similar character adjust score from - // SCORE_SUBST to SCORE_SIMILAR. - else if (!soundfold - && slang->sl_has_map - && similar_chars(slang, - mb_ptr2char(tword - + sp->ts_twordlen - - sp->ts_tcharlen), - mb_ptr2char(fword - + sp->ts_fcharstart))) - sp->ts_score -= - SCORE_SUBST - SCORE_SIMILAR; + && utf_iscomposing(utf_ptr2char(tword + sp->ts_twordlen + - sp->ts_tcharlen)) + && utf_iscomposing(utf_ptr2char(fword + + sp->ts_fcharstart))) { + sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP; + } else if (!soundfold + && slang->sl_has_map + && similar_chars(slang, + mb_ptr2char(tword + + sp->ts_twordlen + - sp->ts_tcharlen), + mb_ptr2char(fword + + sp->ts_fcharstart))) { + // For a similar character adjust score from + // SCORE_SUBST to SCORE_SIMILAR. + sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR; + } } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_twordlen > sp->ts_tcharlen) { p = tword + sp->ts_twordlen - sp->ts_tcharlen; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 26de519f3c..68f0422f7d 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3591,7 +3591,7 @@ syn_list_one ( {0, NULL} }; - attr = hl_attr(HLF_D); /* highlight like directories */ + attr = HL_ATTR(HLF_D); // highlight like directories /* list the keywords for "id" */ if (!syncing) { @@ -3691,9 +3691,9 @@ static void syn_list_cluster(int id) msg_advance(endcol); if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) { - put_id_list("cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, hl_attr(HLF_D)); + put_id_list("cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, HL_ATTR(HLF_D)); } else { - msg_puts_attr("cluster", hl_attr(HLF_D)); + msg_puts_attr("cluster", HL_ATTR(HLF_D)); msg_puts("=NONE"); } } @@ -7186,7 +7186,7 @@ static void highlight_list_one(int id) if (sgp->sg_link && !got_int) { (void)syn_list_header(didh, 9999, id); didh = true; - msg_puts_attr("links to", hl_attr(HLF_D)); + msg_puts_attr("links to", HL_ATTR(HLF_D)); msg_putchar(' '); msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); } @@ -7234,8 +7234,8 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, didh = TRUE; if (!got_int) { if (*name != NUL) { - MSG_PUTS_ATTR(name, hl_attr(HLF_D)); - MSG_PUTS_ATTR("=", hl_attr(HLF_D)); + MSG_PUTS_ATTR(name, HL_ATTR(HLF_D)); + MSG_PUTS_ATTR("=", HL_ATTR(HLF_D)); } msg_outtrans(ts); } @@ -7507,7 +7507,7 @@ static int syn_add_group(char_u *name) } else if (!ASCII_ISALNUM(*p) && *p != '_') { /* This is an error, but since there previously was no check only * give a warning. */ - msg_source(hl_attr(HLF_W)); + msg_source(HL_ATTR(HLF_W)); MSG(_("W18: Invalid character in group name")); break; } @@ -7749,10 +7749,12 @@ static void highlight_list(void) { int i; - for (i = 10; --i >= 0; ) - highlight_list_two(i, hl_attr(HLF_D)); - for (i = 40; --i >= 0; ) + for (i = 10; --i >= 0; ) { + highlight_list_two(i, HL_ATTR(HLF_D)); + } + for (i = 40; --i >= 0; ) { highlight_list_two(99, 0); + } } static void highlight_list_two(int cnt, int attr) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index d7bdf97c48..473381a13c 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -512,10 +512,10 @@ do_tag ( if (msg_col == 0) msg_didout = FALSE; /* overwrite previous message */ msg_start(); - MSG_PUTS_ATTR(_(" # pri kind tag"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_(" # pri kind tag"), HL_ATTR(HLF_T)); msg_clr_eos(); taglen_advance(taglen); - MSG_PUTS_ATTR(_("file\n"), hl_attr(HLF_T)); + MSG_PUTS_ATTR(_("file\n"), HL_ATTR(HLF_T)); for (i = 0; i < num_matches && !got_int; i++) { parse_match(matches[i], &tagp); @@ -535,15 +535,15 @@ do_tag ( } msg_advance(13); msg_outtrans_len_attr(tagp.tagname, - (int)(tagp.tagname_end - tagp.tagname), - hl_attr(HLF_T)); + (int)(tagp.tagname_end - tagp.tagname), + HL_ATTR(HLF_T)); msg_putchar(' '); taglen_advance(taglen); /* Find out the actual file name. If it is long, truncate * it and put "..." in the middle */ p = tag_full_fname(&tagp); - msg_puts_long_attr(p, hl_attr(HLF_D)); + msg_puts_long_attr(p, HL_ATTR(HLF_D)); xfree(p); if (msg_col > 0) @@ -573,8 +573,8 @@ do_tag ( p = tagp.tagkind_end; continue; } - /* print all other extra fields */ - attr = hl_attr(HLF_CM); + // print all other extra fields + attr = HL_ATTR(HLF_CM); while (*p && *p != '\r' && *p != '\n') { if (msg_col + ptr2cells(p) >= Columns) { msg_putchar('\n'); @@ -849,7 +849,7 @@ do_tag ( if ((num_matches > prev_num_matches || new_tag) && num_matches > 1) { if (ic) { - msg_attr((const char *)IObuff, hl_attr(HLF_W)); + msg_attr((const char *)IObuff, HL_ATTR(HLF_W)); } else { msg(IObuff); } @@ -960,7 +960,7 @@ void do_tags(exarg_T *eap) tagstack[i].fmark.mark.lnum); msg_outtrans(IObuff); msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum - ? hl_attr(HLF_D) : 0); + ? HL_ATTR(HLF_D) : 0); xfree(name); } ui_flush(); /* show one line at a time */ diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 39cb2b6372..c2370de0f8 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -528,6 +528,13 @@ void terminal_send(Terminal *term, char *data, size_t size) term->opts.write_cb(data, size, term->opts.data); } +void terminal_flush_output(Terminal *term) +{ + size_t len = vterm_output_read(term->vt, term->textbuf, + sizeof(term->textbuf)); + terminal_send(term, term->textbuf, len); +} + void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; @@ -545,9 +552,7 @@ void terminal_send_key(Terminal *term, int c) vterm_keyboard_unichar(term->vt, (uint32_t)c, mod); } - size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); - terminal_send(term, term->textbuf, (size_t)len); + terminal_flush_output(term); } void terminal_receive(Terminal *term, char *data, size_t len) @@ -982,7 +987,7 @@ static bool send_mouse_event(Terminal *term, int c) mouse_action(term, button, row, col, drag, 0); size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); + sizeof(term->textbuf)); terminal_send(term, term->textbuf, (size_t)len); return false; } @@ -1234,7 +1239,9 @@ static void refresh_screen(Terminal *term, buf_T *buf) int change_start = row_to_linenr(term, term->invalid_start); int change_end = change_start + changed; - changed_lines(change_start, 0, change_end, added); + changed_lines(change_start, 0, change_end, added, + // Don't send nvim_buf_lines_event for :terminal buffer. + false); term->invalid_start = INT_MAX; term->invalid_end = -1; } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index c1ede08c31..a161f14bc8 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -88,6 +88,8 @@ NEW_TESTS ?= \ test_options.res \ test_profile.res \ test_put.res \ + test_python2.res \ + test_python3.res \ test_quickfix.res \ test_quotestar.res \ test_recover.res \ diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 8139f00f0e..a998bd90f1 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -366,6 +366,15 @@ func Test_cmdline_complete_wildoptions() bw! endfunc +func Test_cmdline_complete_user_cmd() + command! -complete=color -nargs=1 Foo : + call feedkeys(":Foo \<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"Foo blue', @:) + call feedkeys(":Foo b\<Tab>\<Home>\"\<cr>", 'tx') + call assert_equal('"Foo blue', @:) + delcommand Foo +endfunc + " using a leading backslash here set cpo+=C diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim index 56a4cc9b31..b648a3361b 100644 --- a/src/nvim/testdir/test_listlbr_utf8.vim +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -194,6 +194,21 @@ func Test_multibyte_sign_and_colorcolumn() call s:close_windows() endfunc +func Test_colorcolumn_priority() + call s:test_windows('setl cc=4 cuc hls') + call setline(1, ["xxyy", ""]) + norm! gg + exe "normal! /xxyy\<CR>" + norm! G + redraw! + let line_attr = s:screen_attr(1, [1, &cc]) + " Search wins over CursorColumn + call assert_equal(line_attr[1], line_attr[0]) + " Search wins over Colorcolumn + call assert_equal(line_attr[2], line_attr[3]) + call s:close_windows('setl hls&vim') +endfunc + func Test_illegal_byte_and_breakat() call s:test_windows("setl sbr= brk+=<") vert resize 18 diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index f5e4c4b90c..f4fe1c2705 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -104,7 +104,7 @@ func Test_map_langmap() imap a c call feedkeys("Go\<C-R>a\<Esc>", "xt") call assert_equal('bbbb', getline('$')) - + " langmap should not apply in Command-line mode set langmap=+{ nolangremap call feedkeys(":call append(line('$'), '+')\<CR>", "xt") @@ -160,3 +160,41 @@ func Test_map_meta_quotes() set nomodified iunmap <M-"> endfunc + +func Test_abbr_after_line_join() + new + abbr foo bar + set backspace=indent,eol,start + exe "normal o\<BS>foo " + call assert_equal("bar ", getline(1)) + bwipe! + unabbr foo + set backspace& +endfunc + +func Test_map_timeout() + nnoremap aaaa :let got_aaaa = 1<CR> + nnoremap bb :let got_bb = 1<CR> + nmap b aaa + new + func ExitInsert(timer) + let g:line = getline(1) + call feedkeys("\<Esc>", "t") + endfunc + set timeout timeoutlen=200 + call timer_start(300, 'ExitInsert') + " After the 'b' Vim waits for another character to see if it matches 'bb'. + " When it times out it is expanded to "aaa", but there is no wait for + " "aaaa". Can't check that reliably though. + call feedkeys("b", "xt!") + call assert_equal("aa", g:line) + call assert_false(exists('got_aaa')) + call assert_false(exists('got_bb')) + + bwipe! + nunmap aaaa + nunmap bb + nunmap b + set timeoutlen& + delfunc ExitInsert +endfunc diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index d00b1ddc88..18a0c71aab 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -24,3 +24,47 @@ function! Test_Incr_Marks() call assert_equal("XXX 123 123", getline(3)) enew! endfunction + +func Test_setpos() + new one + let onebuf = bufnr('%') + let onewin = win_getid() + call setline(1, ['aaa', 'bbb', 'ccc']) + new two + let twobuf = bufnr('%') + let twowin = win_getid() + call setline(1, ['aaa', 'bbb', 'ccc']) + + " for the cursor the buffer number is ignored + call setpos(".", [0, 2, 1, 0]) + call assert_equal([0, 2, 1, 0], getpos(".")) + call setpos(".", [onebuf, 3, 3, 0]) + call assert_equal([0, 3, 3, 0], getpos(".")) + + call setpos("''", [0, 1, 3, 0]) + call assert_equal([0, 1, 3, 0], getpos("''")) + call setpos("''", [onebuf, 2, 2, 0]) + call assert_equal([0, 2, 2, 0], getpos("''")) + + " buffer-local marks + for mark in ["'a", "'\"", "'[", "']", "'<", "'>"] + call win_gotoid(twowin) + call setpos(mark, [0, 2, 1, 0]) + call assert_equal([0, 2, 1, 0], getpos(mark), "for mark " . mark) + call setpos(mark, [onebuf, 1, 3, 0]) + call win_gotoid(onewin) + call assert_equal([0, 1, 3, 0], getpos(mark), "for mark " . mark) + endfor + + " global marks + call win_gotoid(twowin) + call setpos("'N", [0, 2, 1, 0]) + call assert_equal([twobuf, 2, 1, 0], getpos("'N")) + call setpos("'N", [onebuf, 1, 3, 0]) + call assert_equal([onebuf, 1, 3, 0], getpos("'N")) + + call win_gotoid(onewin) + bwipe! + call win_gotoid(twowin) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 10d4c5dd94..27ac084ef0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2387,3 +2387,15 @@ func Test_changelist() %bwipe! let &ul = save_ul endfunc + +func Test_delete_until_paragraph() + if !has('multi_byte') + return + endif + new + normal grádv} + call assert_equal('á', getline(1)) + normal grád} + call assert_equal('', getline(1)) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index eb42e35bd3..5ae8528ee9 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -22,6 +22,13 @@ function! Test_whichwrap() set whichwrap& endfunction +function! Test_isfname() + " This used to cause Vim to access uninitialized memory. + set isfname= + call assert_equal("~X", expand("~X")) + set isfname& +endfunction + function! Test_options() let caught = 'ok' try diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim new file mode 100644 index 0000000000..fb98c1eda7 --- /dev/null +++ b/src/nvim/testdir/test_python2.vim @@ -0,0 +1,24 @@ +" Test for python 2 commands. +" TODO: move tests from test87.in here. + +if !has('python') + finish +endif + +func Test_pydo() + " Check deleting lines does not trigger ml_get error. + py import vim + new + call setline(1, ['one', 'two', 'three']) + pydo vim.command("%d_") + bwipe! + + " Check switching to another buffer does not trigger ml_get error. + new + let wincount = winnr('$') + call setline(1, ['one', 'two', 'three']) + pydo vim.command("new") + call assert_equal(wincount + 1, winnr('$')) + bwipe! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim new file mode 100644 index 0000000000..bb241dacb1 --- /dev/null +++ b/src/nvim/testdir/test_python3.vim @@ -0,0 +1,24 @@ +" Test for python 2 commands. +" TODO: move tests from test88.in here. + +if !has('python3') + finish +endif + +func Test_py3do() + " Check deleting lines does not trigger an ml_get error. + py3 import vim + new + call setline(1, ['one', 'two', 'three']) + py3do vim.command("%d_") + bwipe! + + " Check switching to another buffer does not trigger an ml_get error. + new + let wincount = winnr('$') + call setline(1, ['one', 'two', 'three']) + py3do vim.command("new") + call assert_equal(wincount + 1, winnr('$')) + bwipe! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index ecd686743e..97638e9aac 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -109,12 +109,10 @@ func s:classes_test() call assert_equal('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', alnumchars) call assert_equal("\b", backspacechar) call assert_equal("\t ", blankchars) - " Commented out: it succeeds on Linux and Windows, but fails on macOs in Travis. - " call assert_equal("\x01\x02\x03\x04\x05\x06\x07\b\t\n\x0b\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f\x7f", cntrlchars) + call assert_equal("\x01\x02\x03\x04\x05\x06\x07\b\t\n\x0b\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f\x7f", cntrlchars) call assert_equal("0123456789", digitchars) call assert_equal("\<Esc>", escapechar) - " Commented out: it succeeds on Linux and Windows, but fails on macOs in Travis. - " call assert_equal('!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', graphchars) + call assert_equal('!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', graphchars) call assert_equal('abcdefghijklmnopqrstuvwxyzµßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ', lowerchars) call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', printchars) call assert_equal('!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~', punctchars) @@ -168,3 +166,20 @@ func Test_eow_with_optional() call assert_equal(expected, actual) endfor endfunc + +func Test_reversed_range() + for re in range(0, 2) + exe 'set re=' . re + call assert_fails('call match("abc def", "[c-a]")', 'E944:') + endfor + set re=0 +endfunc + +func Test_large_class() + set re=1 + call assert_fails('call match("abc def", "[\u3000-\u4000]")', 'E945:') + set re=2 + call assert_equal(0, 'abc def' =~# '[\u3000-\u4000]') + call assert_equal(1, "\u3042" =~# '[\u3000-\u4000]') + set re=0 +endfunc diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim index d00eac9798..9e93a55eb0 100644 --- a/src/nvim/testdir/test_smartindent.vim +++ b/src/nvim/testdir/test_smartindent.vim @@ -1,3 +1,4 @@ +" Tests for smartindent " Tests for not doing smart indenting when it isn't set. function! Test_nosmartindent() @@ -12,3 +13,29 @@ function! Test_nosmartindent() call assert_equal(" #test", getline(1)) enew! | close endfunction + +function MyIndent() +endfunction + +" When 'indentexpr' is set, setting 'si' has no effect. +function Test_smartindent_has_no_effect() + new + exe "normal! i\<Tab>one\<Esc>" + set noautoindent + set smartindent + set indentexpr= + exe "normal! Gotwo\<Esc>" + call assert_equal("\ttwo", getline("$")) + + set indentexpr=MyIndent + exe "normal! Gothree\<Esc>" + call assert_equal("three", getline("$")) + + delfunction! MyIndent + set autoindent& + set smartindent& + set indentexpr& + bwipe! +endfunction + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/undo.c b/src/nvim/undo.c index e1ae4b4cc0..c5ec077d01 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -92,6 +92,7 @@ #include "nvim/eval.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/buffer_updates.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/message.h" @@ -1672,7 +1673,7 @@ void u_undo(int count) undo_undoes = TRUE; else undo_undoes = !undo_undoes; - u_doit(count, false); + u_doit(count, false, true); } /* @@ -1685,7 +1686,7 @@ void u_redo(int count) undo_undoes = false; } - u_doit(count, false); + u_doit(count, false, true); } /// Undo and remove the branch from the undo tree. @@ -1697,7 +1698,9 @@ bool u_undo_and_forget(int count) count = 1; } undo_undoes = true; - u_doit(count, true); + u_doit(count, true, + // Don't send nvim_buf_lines_event for u_undo_and_forget(). + false); if (curbuf->b_u_curhead == NULL) { // nothing was undone. @@ -1732,7 +1735,11 @@ bool u_undo_and_forget(int count) } /// Undo or redo, depending on `undo_undoes`, `count` times. -static void u_doit(int startcount, bool quiet) +/// +/// @param startcount How often to undo or redo +/// @param quiet If `true`, don't show messages +/// @param do_buf_event If `true`, send the changedtick with the buffer updates +static void u_doit(int startcount, bool quiet, bool do_buf_event) { int count = startcount; @@ -1768,7 +1775,7 @@ static void u_doit(int startcount, bool quiet) break; } - u_undoredo(true); + u_undoredo(true, do_buf_event); } else { if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0) { beep_flush(); /* nothing to redo */ @@ -1779,7 +1786,7 @@ static void u_doit(int startcount, bool quiet) break; } - u_undoredo(FALSE); + u_undoredo(false, do_buf_event); /* Advance for next redo. Set "newhead" when at the end of the * redoable changes. */ @@ -2026,8 +2033,8 @@ void undo_time(long step, int sec, int file, int absolute) || (uhp->uh_seq == target && !above)) break; curbuf->b_u_curhead = uhp; - u_undoredo(TRUE); - uhp->uh_walk = nomark; /* don't go back down here */ + u_undoredo(true, true); + uhp->uh_walk = nomark; // don't go back down here } /* @@ -2082,7 +2089,7 @@ void undo_time(long step, int sec, int file, int absolute) break; } - u_undoredo(FALSE); + u_undoredo(false, true); /* Advance "curhead" to below the header we last used. If it * becomes NULL then we need to set "newhead" to this leaf. */ @@ -2105,16 +2112,15 @@ void undo_time(long step, int sec, int file, int absolute) u_undo_end(did_undo, absolute, false); } -/* - * u_undoredo: common code for undo and redo - * - * The lines in the file are replaced by the lines in the entry list at - * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry - * list for the next undo/redo. - * - * When "undo" is TRUE we go up in the tree, when FALSE we go down. - */ -static void u_undoredo(int undo) +/// u_undoredo: common code for undo and redo +/// +/// The lines in the file are replaced by the lines in the entry list at +/// curbuf->b_u_curhead. The replaced lines in the file are saved in the entry +/// list for the next undo/redo. +/// +/// @param undo If `true`, go up the tree. Down if `false`. +/// @param do_buf_event If `true`, send buffer updates. +static void u_undoredo(int undo, bool do_buf_event) { char_u **newarray = NULL; linenr_T oldsize; @@ -2242,7 +2248,7 @@ static void u_undoredo(int undo) } } - changed_lines(top + 1, 0, bot, newsize - oldsize); + changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event); /* set '[ and '] mark */ if (top + 1 < curbuf->b_op_start.lnum) @@ -2277,6 +2283,13 @@ static void u_undoredo(int undo) unchanged(curbuf, FALSE); } + // because the calls to changed()/unchanged() above will bump b_changedtick + // again, we need to send a nvim_buf_lines_event with just the new value of + // b:changedtick + if (do_buf_event && kv_size(curbuf->update_channels)) { + buf_updates_changedtick(curbuf); + } + /* * restore marks from before undo/redo */ @@ -2521,7 +2534,7 @@ void ex_undolist(exarg_T *eap) msg_start(); msg_puts_attr(_("number changes when saved"), - hl_attr(HLF_T)); + HL_ATTR(HLF_T)); for (int i = 0; i < ga.ga_len && !got_int; i++) { msg_putchar('\n'); if (got_int) { diff --git a/src/nvim/version.c b/src/nvim/version.c index be160e678e..203b53472c 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -1983,7 +1983,8 @@ static void do_intro_line(long row, char_u *mesg, int attr) } } assert(row <= INT_MAX && col <= INT_MAX); - screen_puts_len(p, l, (int)row, (int)col, *p == '<' ? hl_attr(HLF_8) : attr); + screen_puts_len(p, l, (int)row, (int)col, + *p == '<' ? HL_ATTR(HLF_8) : attr); col += clen; } } diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 0c13d331c8..1fe4e53faf 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -272,8 +272,8 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // Enums need a typecast to be used as array index (for Ultrix). -#define hl_attr(n) highlight_attr[(int)(n)] -#define term_str(n) term_strings[(int)(n)] +#define HL_ATTR(n) highlight_attr[(int)(n)] +#define TERM_STR(n) term_strings[(int)(n)] /// Maximum number of bytes in a multi-byte character. It can be one 32-bit /// character of up to 6 bytes, or one 16-bit character of up to three bytes diff --git a/src/nvim/window.c b/src/nvim/window.c index 82fffe305c..ebde81bca6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3732,7 +3732,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, if (restart_edit) redraw_later(VALID); /* causes status line redraw */ - if (hl_attr(HLF_INACTIVE) + if (HL_ATTR(HLF_INACTIVE) || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE]) || curwin->w_hl_ids[HLF_INACTIVE]) { redraw_all_later(NOT_VALID); |