diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/buffer.c | 37 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 7 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 30 | ||||
-rw-r--r-- | src/nvim/buffer.c | 38 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 253 | ||||
-rw-r--r-- | src/nvim/ex_cmds2.c | 52 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 29 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 4 | ||||
-rw-r--r-- | src/nvim/ops.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_profile.vim | 38 |
11 files changed, 352 insertions, 140 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c381e92dc7..4b6a88e5fa 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -439,6 +439,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) /// Gets a changed tick of a buffer /// /// @param[in] buffer Buffer handle. +/// @param[out] err Error details, if any /// /// @return `b:changedtick` value. Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) @@ -453,14 +454,14 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) return buf->b_changedtick; } -/// Get a list of dictionaries describing buffer-local mappings -/// Note that the buffer key in the dictionary will represent the buffer -/// handle where the mapping is present +/// Gets a list of dictionaries describing buffer-local mappings. +/// The "buffer" key in the returned dictionary reflects the buffer +/// handle where the mapping is present. /// -/// @param mode The abbreviation for the mode -/// @param buffer_id Buffer handle +/// @param mode Mode short-name ("n", "i", "v", ...) +/// @param buffer Buffer handle /// @param[out] err Error details, if any -/// @returns An array of maparg() like dictionaries describing mappings +/// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) FUNC_API_SINCE(3) { @@ -739,29 +740,29 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// Adds a highlight to buffer. /// -/// This can be used for plugins which dynamically generate highlights to a -/// buffer (like a semantic highlighter or linter). The function adds a single +/// Useful for plugins that dynamically generate highlights to a buffer +/// (like a semantic highlighter or linter). The function adds a single /// highlight to a buffer. Unlike matchaddpos() highlights follow changes to /// line numbering (as lines are inserted/removed above the highlighted line), /// like signs and marks do. /// -/// "src_id" is useful for batch deletion/updating of a set of highlights. When -/// called with src_id = 0, an unique source id is generated and returned. -/// Succesive calls can pass in it as "src_id" to add new highlights to the same -/// source group. All highlights in the same group can then be cleared with -/// nvim_buf_clear_highlight. If the highlight never will be manually deleted -/// pass in -1 for "src_id". +/// `src_id` is useful for batch deletion/updating of a set of highlights. When +/// called with `src_id = 0`, an unique source id is generated and returned. +/// Successive calls can pass that `src_id` to associate new highlights with +/// the same source group. All highlights in the same group can be cleared +/// with `nvim_buf_clear_highlight`. If the highlight never will be manually +/// deleted, pass `src_id = -1`. /// -/// If "hl_group" is the empty string no highlight is added, but a new src_id +/// If `hl_group` is the empty string no highlight is added, but a new `src_id` /// is still returned. This is useful for an external plugin to synchrounously -/// request an unique src_id at initialization, and later asynchronously add and -/// clear highlights in response to buffer changes. +/// request an unique `src_id` at initialization, and later asynchronously add +/// and clear highlights in response to buffer changes. /// /// @param buffer Buffer handle /// @param src_id Source group to use or 0 to use a new group, /// or -1 for ungrouped highlight /// @param hl_group Name of the highlight group to use -/// @param line Line to highlight +/// @param line Line to highlight (zero-indexed) /// @param col_start Start of range of columns to highlight /// @param col_end End of range of columns to highlight, /// or -1 to highlight to end of line diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 65357d008a..847b21072a 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -65,6 +65,7 @@ void popupmenu_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void popupmenu_select(Integer selected) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + void tabline_update(Tabpage current, Array tabs) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; @@ -84,4 +85,10 @@ void cmdline_block_append(Array lines) void cmdline_block_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void wildmenu_show(Array items) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void wildmenu_select(Integer selected) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void wildmenu_hide(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; #endif // NVIM_API_UI_EVENTS_IN_H diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 8de37e2cf3..0596c3ebd8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -168,7 +168,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) /// On VimL error: Does not fail, but updates v:errmsg. /// /// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call -/// is not deferred. This is the most reliable way to emulate real user input. +/// is not deferred. This is the most reliable way to send real user input. +/// +/// @note |keycodes| like <CR> are translated, so "<" is special. +/// To input a literal "<", send <LT>. /// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than @@ -322,15 +325,15 @@ Object nvim_execute_lua(String code, Array args, Error *err) /// @param text Some text /// @param[out] err Error details, if any /// @return Number of cells -Integer nvim_strwidth(String str, Error *err) +Integer nvim_strwidth(String text, Error *err) FUNC_API_SINCE(1) { - if (str.size > INT_MAX) { + if (text.size > INT_MAX) { api_set_error(err, kErrorTypeValidation, "String length is too high"); return 0; } - return (Integer) mb_string2cells((char_u *) str.data); + return (Integer)mb_string2cells((char_u *)text.data); } /// Gets the paths contained in 'runtimepath'. @@ -580,7 +583,7 @@ Buffer nvim_get_current_buf(void) /// Sets the current buffer /// -/// @param id Buffer handle +/// @param buffer Buffer handle /// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) FUNC_API_SINCE(1) @@ -634,7 +637,7 @@ Window nvim_get_current_win(void) /// Sets the current window /// -/// @param handle Window handle +/// @param window Window handle void nvim_set_current_win(Window window, Error *err) FUNC_API_SINCE(1) { @@ -687,7 +690,7 @@ Tabpage nvim_get_current_tabpage(void) /// Sets the current tabpage /// -/// @param handle Tabpage handle +/// @param tabpage Tabpage handle /// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) @@ -757,9 +760,8 @@ Dictionary nvim_get_color_map(void) } -/// Gets the current mode. -/// mode: Mode string. |mode()| -/// blocking: true if Nvim is waiting for input. +/// Gets the current mode. |mode()| +/// "blocking" is true if Nvim is waiting for input. /// /// @returns Dictionary { "mode": String, "blocking": Boolean } Dictionary nvim_get_mode(void) @@ -775,11 +777,11 @@ Dictionary nvim_get_mode(void) return rv; } -/// Get a list of dictionaries describing global (i.e. non-buffer) mappings -/// Note that the "buffer" key will be 0 to represent false. +/// Gets a list of dictionaries describing global (non-buffer) mappings. +/// The "buffer" key in the returned dictionary is always zero. /// -/// @param mode The abbreviation for the mode -/// @returns An array of maparg() like dictionaries describing mappings +/// @param mode Mode short-name ("n", "i", "v", ...) +/// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) nvim_get_keymap(String mode) FUNC_API_SINCE(3) { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9d42fbc1b3..f874268910 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5278,6 +5278,44 @@ int bufhl_add_hl(buf_T *buf, return src_id; } +/// Add highlighting to a buffer, bounded by two cursor positions, +/// with an offset. +/// +/// @param buf Buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Highlight group id +/// @param pos_start Cursor position to start the hightlighting at +/// @param pos_end Cursor position to end the highlighting at +/// @param offset Move the whole highlighting this many columns to the right +void bufhl_add_hl_pos_offset(buf_T *buf, + int src_id, + int hl_id, + lpos_T pos_start, + lpos_T pos_end, + colnr_T offset) +{ + colnr_T hl_start = 0; + colnr_T hl_end = 0; + + for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { + if (pos_start.lnum < lnum && lnum < pos_end.lnum) { + hl_start = offset; + hl_end = MAXCOL; + } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = MAXCOL; + } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { + hl_start = offset; + hl_end = pos_end.col + offset; + } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = pos_end.col + offset; + } + (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end); + } +} + /// Clear bufhl highlights from a given source group and range of lines. /// /// @param buf The buffer to remove highlights from diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 9c9c2c2dc8..0933b1bf9c 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -266,7 +266,7 @@ typval_T decode_string(const char *const s, const size_t len, { assert(s != NULL || len == 0); const bool really_hasnul = (hasnul == kNone - ? memchr(s, NUL, len) != NULL + ? ((s != NULL) && (memchr(s, NUL, len) != NULL)) : (bool)hasnul); if (really_hasnul) { list_T *const list = tv_list_alloc(); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 918e7a0c91..39059a2c80 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -90,14 +90,20 @@ typedef struct { SubIgnoreType do_ic; ///< ignore case flag } subflags_T; -/// Lines matched during :substitute. +/// Partial result of a substitution during :substitute. +/// Numbers refer to the buffer _after_ substitution typedef struct { - linenr_T lnum; - long nmatch; - char_u *line; - kvec_t(colnr_T) cols; ///< columns of in-line matches -} MatchedLine; -typedef kvec_t(MatchedLine) MatchedLineVec; + lpos_T start; // start of the match + lpos_T end; // end of the match + linenr_T pre_match; // where to begin showing lines before the match +} SubResult; + +// Collected results of a substitution for showing them in +// the preview window +typedef struct { + kvec_t(SubResult) subresults; + linenr_T lines_needed; // lines neede in the preview window +} PreviewLines; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" @@ -3168,7 +3174,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) linenr_T old_line_count = curbuf->b_ml.ml_line_count; char_u *sub_firstline; // allocated copy of first sub line bool endcolumn = false; // cursor in last column when done - MatchedLineVec matched_lines = KV_INITIAL_VALUE; + PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; + static int pre_src_id = 0; // Source id for the preview highlight + static int pre_hl_id = 0; + buf_T *orig_buf = curbuf; // save to reset highlighting pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; @@ -3336,7 +3345,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; lnum <= line2 && !got_quit && !aborting() - && (!preview || matched_lines.size < (size_t)p_cwh + && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, @@ -3401,8 +3410,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstlnum = lnum; copycol = 0; matchcol = 0; - // the current match - MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE }; /* At first match, remember current cursor position. */ if (!got_match) { @@ -3419,10 +3426,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) * 5. break if there isn't another match in this line */ for (;; ) { - /* Advance "lnum" to the line where the match starts. The - * match does not start in the first line when there is a line - * break before \zs. */ + SubResult current_match = { + .start = { 0, 0 }, + .end = { 0, 0 }, + .pre_match = 0, + }; + // lnum is where the match start, but maybe not the pattern match, + // since we can have \n before \zs in the pattern + + // Advance "lnum" to the line where the match starts. The + // match does not start in the first line when there is a line + // break before \zs. if (regmatch.startpos[0].lnum > 0) { + current_match.pre_match = lnum; lnum += regmatch.startpos[0].lnum; sub_firstlnum += regmatch.startpos[0].lnum; nmatch -= regmatch.startpos[0].lnum; @@ -3430,6 +3446,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstline = NULL; } + // Now we're at the line where the pattern match starts + // Note: If not first match on a line, column can't be known here + current_match.start.lnum = sub_firstlnum; + if (sub_firstline == NULL) { sub_firstline = vim_strsave(ml_get(sub_firstlnum)); } @@ -3439,12 +3459,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) curwin->w_cursor.lnum = lnum; do_again = FALSE; - if (preview) { - // Increment the in-line match count and store the column. - matched_line.nmatch++; - kv_push(matched_line.cols, regmatch.startpos[0].col); - } - /* * 1. Match empty string does not count, except for first * match. This reproduces the strange vi behaviour. @@ -3464,6 +3478,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) else ++matchcol; } + // match will be pushed to preview_lines, bring it into a proper state + current_match.start.col = matchcol; + current_match.end.lnum = sub_firstlnum; + current_match.end.col = matchcol; goto skip; } @@ -3669,6 +3687,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // go beyond the last line of the buffer. if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; + current_match.end.lnum = sub_firstlnum + nmatch; skip_match = true; } @@ -3696,8 +3715,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) } \ } while (0) + // Save the line numbers for the preview buffer + // NOTE: If the pattern matches a final newline, the next line will + // be shown also, but should not be highlighted. Intentional for now. if (preview && !has_second_delim) { + current_match.start.col = regmatch.startpos[0].col; + if (current_match.end.lnum == 0) { + current_match.end.lnum = sub_firstlnum + nmatch - 1; + } + current_match.end.col = regmatch.endpos[0].col; + ADJUST_SUB_FIRSTLNUM(); + lnum += nmatch - 1; + goto skip; } @@ -3747,6 +3777,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) memmove(new_end, sub_firstline + copycol, (size_t)copy_len); new_end += copy_len; + // Finally, at this point we can know where the match actually will + // start in the new text + current_match.start.col = new_end - new_start; + (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, sub, new_end, true, p_magic, true); @@ -3799,6 +3833,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) p1 += (*mb_ptr2len)(p1) - 1; } } + current_match.end.col = STRLEN(new_start); + current_match.end.lnum = lnum; } // 4. If subflags.do_all is set, find next match. @@ -3907,9 +3943,30 @@ skip: * found the match. */ if (nmatch == -1) lnum -= regmatch.startpos[0].lnum; + +#define PUSH_PREVIEW_LINES() \ + do { \ + linenr_T match_lines = current_match.end.lnum \ + - current_match.start.lnum +1; \ + if (preview_lines.subresults.size > 0) { \ + linenr_T last = kv_last(preview_lines.subresults).end.lnum; \ + if (last == current_match.start.lnum) { \ + preview_lines.lines_needed += match_lines - 1; \ + } \ + } else { \ + preview_lines.lines_needed += match_lines; \ + } \ + kv_push(preview_lines.subresults, current_match); \ + } while (0) + + // Push the match to preview_lines. + PUSH_PREVIEW_LINES(); + break; } } + // Push the match to preview_lines. + PUSH_PREVIEW_LINES(); line_breakcheck(); } @@ -3919,12 +3976,6 @@ skip: xfree(new_start); /* for when substitute was cancelled */ xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; - - if (preview) { - matched_line.lnum = lnum; - matched_line.line = vim_strsave(ml_get(lnum)); - kv_push(matched_lines, matched_line); - } } line_breakcheck(); @@ -3999,25 +4050,34 @@ skip: // Show 'inccommand' preview if there are matched lines. buf_T *preview_buf = NULL; + size_t subsize = preview_lines.subresults.size; if (preview && !aborting()) { if (got_quit) { // Substitution is too slow, disable 'inccommand'. set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, SID_NONE); - } else if (*p_icm != NUL && matched_lines.size != 0 && pat != NULL) { + } else if (*p_icm != NUL && pat != NULL) { + if (pre_src_id == 0) { + // Get a unique new src_id, saved in a static + pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0); + } + if (pre_hl_id == 0) { + pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute")); + } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines); + preview_buf = show_sub(eap, old_cursor, &preview_lines, + pre_hl_id, pre_src_id); + if (subsize > 0) { + bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, + kv_last(preview_lines.subresults).end.lnum); + } } } - for (MatchedLine m; kv_size(matched_lines);) { - m = kv_pop(matched_lines); - xfree(m.line); - kv_destroy(m.cols); - } - kv_destroy(matched_lines); + kv_destroy(preview_lines.subresults); return preview_buf; #undef ADJUST_SUB_FIRSTLNUM +#undef PUSH_PREVIEW_LINES } // NOLINT(readability/fn_size) /* @@ -6017,8 +6077,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, - MatchedLineVec *matched_lines) +static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, + PreviewLines *preview_lines, int hl_id, int src_id) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6026,8 +6086,8 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, win_T *save_curwin = curwin; cmdmod_T save_cmdmod = cmdmod; char_u *save_shm_p = vim_strsave(p_shm); - size_t sub_size = mb_string2cells(sub); - size_t pat_size = mb_string2cells(pat); + PreviewLines lines = *preview_lines; + buf_T *orig_buf = curbuf; // We keep a special-purpose buffer around, but don't assume it exists. buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; @@ -6039,22 +6099,26 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, bool outside_curline = (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); - bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size); + bool split = outside_curline && (*p_icm != 'n'); if (preview_buf == curbuf) { // Preview buffer cannot preview itself! split = false; preview_buf = NULL; } // Place cursor on nearest matching line, to undo do_sub() cursor placement. - for (size_t i = 0; i < matched_lines->size; i++) { - MatchedLine curmatch = matched_lines->items[i]; - if (curmatch.lnum >= old_cusr.lnum) { - curwin->w_cursor.lnum = curmatch.lnum; - curwin->w_cursor.col = curmatch.cols.items[0]; + for (size_t i = 0; i < lines.subresults.size; i++) { + SubResult curres = lines.subresults.items[i]; + if (curres.start.lnum >= old_cusr.lnum) { + curwin->w_cursor.lnum = curres.start.lnum; + curwin->w_cursor.col = curres.start.col; break; } // Else: All matches are above, do_sub() already placed cursor. } + // Width of the "| lnum|..." column which displays the line numbers. + linenr_T highest_num_line = 0; + int col_width = 0; + if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]"); buf_clear(); @@ -6069,44 +6133,77 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, curwin->w_p_spell = false; curwin->w_p_fen = false; - // Width of the "| lnum|..." column which displays the line numbers. - linenr_T highest_num_line = kv_last(*matched_lines).lnum; - int col_width = log10(highest_num_line) + 1 + 3; - - char *str = NULL; - size_t old_line_size = 0; - size_t line_size; - int src_id_highlight = 0; - int hl_id = syn_check_group((char_u *)"Substitute", 13); - - // Dump the lines into the preview buffer. - for (size_t line = 0; line < matched_lines->size; line++) { - MatchedLine mat = matched_lines->items[line]; - line_size = mb_string2cells(mat.line) + col_width + 1; - - // Reallocate if str not long enough - if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); - old_line_size = line_size; + if (lines.subresults.size > 0) { + highest_num_line = kv_last(lines.subresults).end.lnum; + col_width = log10(highest_num_line) + 1 + 3; + } + } + + char *str = NULL; // construct the line to show in here + size_t old_line_size = 0; + size_t line_size = 0; + linenr_T linenr_preview = 0; // last line added to preview buffer + linenr_T linenr_origbuf = 0; // last line added to original buffer + linenr_T next_linenr = 0; // next line to show for the match + + for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) { + SubResult match = lines.subresults.items[matchidx]; + + if (split && preview_buf) { + lpos_T p_start = { 0, match.start.col }; // match starts here in preview + lpos_T p_end = { 0, match.end.col }; // ... and ends here + + if (match.pre_match == 0) { + next_linenr = match.start.lnum; + } else { + next_linenr = match.pre_match; + } + // Don't add a line twice + if (next_linenr == linenr_origbuf) { + next_linenr++; + p_start.lnum = linenr_preview; // might be redefined below + p_end.lnum = linenr_preview; // might be redefined below } - // Put "|lnum| line" into `str` and append it to the preview buffer. - snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line); - ml_append(line, (char_u *)str, (colnr_T)line_size, false); - - // highlight the replaced part - if (sub_size > 0) { - for (size_t i = 0; i < mat.cols.size; i++) { - colnr_T col_start = mat.cols.items[i] + col_width - + i * (sub_size - pat_size) + 1; - colnr_T col_end = col_start - 1 + sub_size; - src_id_highlight = bufhl_add_hl(curbuf, src_id_highlight, hl_id, - line + 1, col_start, col_end); + for (; next_linenr <= match.end.lnum; next_linenr++) { + if (next_linenr == match.start.lnum) { + p_start.lnum = linenr_preview + 1; } + if (next_linenr == match.end.lnum) { + p_end.lnum = linenr_preview + 1; + } + char *line; + if (next_linenr == orig_buf->b_ml.ml_line_count + 1) { + line = ""; + } else { + line = (char *)ml_get_buf(orig_buf, next_linenr, false); + line_size = strlen(line) + col_width + 1; + + // Reallocate if line not long enough + if (line_size > old_line_size) { + str = xrealloc(str, line_size * sizeof(char)); + old_line_size = line_size; + } + } + // Put "|lnum| line" into `str` and append it to the preview buffer. + snprintf(str, line_size, "|%*ld| %s", col_width - 3, + next_linenr, line); + if (linenr_preview == 0) { + ml_replace(1, (char_u *)str, true); + } else { + ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false); + } + linenr_preview += 1; } + linenr_origbuf = match.end.lnum; + + bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, + p_end, col_width); } - xfree(str); + bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, + match.end, 0); } + xfree(str); redraw_later(SOME_VALID); win_enter(save_curwin, false); // Return to original window @@ -6156,7 +6253,11 @@ void ex_substitute(exarg_T *eap) curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' + // 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)); + p_hls = save_hls; if (save_changedtick != curbuf->b_changedtick) { // Undo invisibly. This also moves the cursor! diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 371f7b3bce..9b8e463aee 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1039,9 +1039,14 @@ static void profile_reset(void) uf->uf_tm_total = profile_zero(); uf->uf_tm_self = profile_zero(); uf->uf_tm_children = profile_zero(); + + xfree(uf->uf_tml_count); + xfree(uf->uf_tml_total); + xfree(uf->uf_tml_self); uf->uf_tml_count = NULL; uf->uf_tml_total = NULL; uf->uf_tml_self = NULL; + uf->uf_tml_start = profile_zero(); uf->uf_tml_children = profile_zero(); uf->uf_tml_wait = profile_zero(); @@ -1068,7 +1073,7 @@ static void profile_init(scriptitem_T *si) si->sn_pr_nest = 0; } -/// save time when starting to invoke another script or function. +/// Save time when starting to invoke another script or function. void script_prof_save( proftime_T *tm // place to store wait time ) @@ -1141,12 +1146,14 @@ static void script_dump_profile(FILE *fd) if (sfd == NULL) { fprintf(fd, "Cannot open file!\n"); } else { - for (int i = 0; i < si->sn_prl_ga.ga_len; i++) { + // Keep going till the end of file, so that trailing + // continuation lines are listed. + for (int i = 0; ; i++) { if (vim_fgets(IObuff, IOSIZE, sfd)) { break; } - pp = &PRL_ITEM(si, i); - if (pp->snp_count > 0) { + if (i < si->sn_prl_ga.ga_len + && (pp = &PRL_ITEM(si, i))->snp_count > 0) { fprintf(fd, "%5d ", pp->snp_count); if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) { fprintf(fd, " "); @@ -2871,22 +2878,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc) save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 0; - cookie.conv.vc_type = CONV_NONE; // no conversion - - // Read the first line so we can check for a UTF-8 BOM. - firstline = getsourceline(0, (void *)&cookie, 0); - if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef - && firstline[1] == 0xbb && firstline[2] == 0xbf) { - // Found BOM; setup conversion, skip over BOM and recode the line. - convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); - p = string_convert(&cookie.conv, firstline + 3, NULL); - if (p == NULL) { - p = vim_strsave(firstline + 3); - } - xfree(firstline); - firstline = p; - } - // start measuring script load time if --startuptime was passed and // time_fd was successfully opened afterwards. proftime_T rel_time; @@ -2959,6 +2950,22 @@ int do_source(char_u *fname, int check_other, int is_vimrc) } } + cookie.conv.vc_type = CONV_NONE; // no conversion + + // Read the first line so we can check for a UTF-8 BOM. + firstline = getsourceline(0, (void *)&cookie, 0); + if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef + && firstline[1] == 0xbb && firstline[2] == 0xbf) { + // Found BOM; setup conversion, skip over BOM and recode the line. + convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); + p = string_convert(&cookie.conv, firstline + 3, NULL); + if (p == NULL) { + p = vim_strsave(firstline + 3); + } + xfree(firstline); + firstline = p; + } + // Call do_cmdline, which will call getsourceline() to get the lines. do_cmdline(firstline, getsourceline, (void *)&cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); @@ -3068,6 +3075,8 @@ char_u *get_scriptname(scid_T id) # if defined(EXITFREE) void free_scriptnames(void) { + profile_reset(); + # define FREE_SCRIPTNAME(item) xfree((item)->sn_name) GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME); } @@ -3281,7 +3290,8 @@ void script_line_start(void) if (si->sn_prof_on && sourcing_lnum >= 1) { // Grow the array before starting the timer, so that the time spent // here isn't counted. - ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); + (void)ga_grow(&si->sn_prl_ga, + (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); si->sn_prl_idx = sourcing_lnum - 1; while (si->sn_prl_ga.ga_len <= si->sn_prl_idx && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3b751530e8..e6fb2571c4 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -536,6 +536,9 @@ static int command_line_execute(VimState *state, int key) if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A && s->c != Ctrl_L) { + if (ui_is_external(kUIWildmenu)) { + ui_call_wildmenu_hide(); + } if (s->xpc.xp_numfiles != -1) { (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); } @@ -2836,7 +2839,7 @@ void ui_ext_cmdline_block_append(int indent, const char *line) { char *buf = xmallocz(indent + strlen(line)); memset(buf, ' ', indent); - memcpy(buf+indent, line, strlen(line)); + memcpy(buf + indent, line, strlen(line)); // -V575 Array item = ARRAY_DICT_INIT; ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); @@ -3567,11 +3570,17 @@ ExpandOne ( else findex = -1; } - if (p_wmnu) - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, - findex, cmd_showtail); - if (findex == -1) + if (p_wmnu) { + if (ui_is_external(kUIWildmenu)) { + ui_call_wildmenu_select(findex); + } else { + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, + findex, cmd_showtail); + } + } + if (findex == -1) { return vim_strsave(orig_save); + } return vim_strsave(xp->xp_files[findex]); } else return NULL; @@ -3928,6 +3937,15 @@ static int showmatches(expand_T *xp, int wildmenu) showtail = cmd_showtail; } + if (ui_is_external(kUIWildmenu)) { + Array args = ARRAY_DICT_INIT; + for (i = 0; i < num_files; i++) { + ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i]))); + } + ui_call_wildmenu_show(args); + return EXPAND_OK; + } + if (!wildmenu) { msg_didany = FALSE; /* lines_left will be set */ msg_start(); /* prepare for paging */ @@ -6180,4 +6198,3 @@ static void set_search_match(pos_T *t) coladvance((colnr_T)MAXCOL); } } - diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 16bb4169c4..8e7b8a1824 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -336,8 +336,8 @@ static lua_State *nlua_init(void) /// Calls nlua_init() if needed. Is responsible for pre-lua call initalization /// like updating `package.[c]path` with directories derived from &runtimepath. /// -/// @return Interprter instance to use. Will either be initialized now or taken -/// from previous initalization. +/// @return Interpreter instance to use. Will either be initialized now or +/// taken from previous initialization. static lua_State *nlua_enter(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c6df71ea46..432c9a8b47 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2735,7 +2735,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // Autocommands may be executed when saving lines for undo, which may make // y_array invalid. Start undo now to avoid that. if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) { - ELOG(_("Failed to save undo information")); + ELOG("Failed to save undo information"); return; } } diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim index 183f52b8ac..4cbd800da5 100644 --- a/src/nvim/testdir/test_profile.vim +++ b/src/nvim/testdir/test_profile.vim @@ -115,7 +115,7 @@ func Test_profile_file() call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) call assert_equal('', lines[4]) call assert_equal('count total (s) self (s)', lines[5]) - call assert_equal(' func! Foo()', lines[6]) + call assert_match(' 2 0.\d\+ func! Foo()', lines[6]) call assert_equal(' endfunc', lines[7]) " Loop iterates 10 times. Since script runs twice, body executes 20 times. " First line of loop executes one more time than body to detect end of loop. @@ -132,6 +132,42 @@ func Test_profile_file() call delete('Xprofile_file.log') endfunc +func Test_profile_file_with_cont() + let lines = [ + \ 'echo "hello', + \ ' \ world"', + \ 'echo "foo ', + \ ' \bar"', + \ ] + + call writefile(lines, 'Xprofile_file.vim') + call system(v:progpath + \ . ' -es -u NONE -U NONE -i NONE --noplugin' + \ . ' -c "profile start Xprofile_file.log"' + \ . ' -c "profile file Xprofile_file.vim"' + \ . ' -c "so Xprofile_file.vim"' + \ . ' -c "qall!"') + call assert_equal(0, v:shell_error) + + let lines = readfile('Xprofile_file.log') + call assert_equal(11, len(lines)) + + call assert_match('^SCRIPT .*Xprofile_file.vim$', lines[0]) + call assert_equal('Sourced 1 time', lines[1]) + call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2]) + call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) + call assert_equal('', lines[4]) + call assert_equal('count total (s) self (s)', lines[5]) + call assert_match(' 1 0.\d\+ echo "hello', lines[6]) + call assert_equal(' \ world"', lines[7]) + call assert_match(' 1 0.\d\+ echo "foo ', lines[8]) + call assert_equal(' \bar"', lines[9]) + call assert_equal('', lines[10]) + + call delete('Xprofile_file.vim') + call delete('Xprofile_file.log') +endfunc + func Test_profile_completion() call feedkeys(":profile \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"profile continue dump file func pause start stop', @:) |