diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/buffer.c | 94 | ||||
-rw-r--r-- | src/nvim/buffer.c | 233 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 39 | ||||
-rw-r--r-- | src/nvim/bufhl_defs.h | 25 | ||||
-rw-r--r-- | src/nvim/eval.c | 50 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/getchar.c | 14 | ||||
-rw-r--r-- | src/nvim/if_cscope.c | 122 | ||||
-rw-r--r-- | src/nvim/lib/khash.h | 2 | ||||
-rw-r--r-- | src/nvim/lib/kvec.h | 4 | ||||
-rw-r--r-- | src/nvim/map.c | 24 | ||||
-rw-r--r-- | src/nvim/map.h | 4 | ||||
-rw-r--r-- | src/nvim/mark.c | 1 | ||||
-rw-r--r-- | src/nvim/misc1.c | 14 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/defs.h | 18 | ||||
-rw-r--r-- | src/nvim/option.c | 18 | ||||
-rw-r--r-- | src/nvim/screen.c | 20 | ||||
-rw-r--r-- | src/nvim/terminal.c | 36 | ||||
-rw-r--r-- | src/nvim/undo.c | 94 |
19 files changed, 611 insertions, 205 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b7a86af134..fa4b8e5f7d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -19,6 +19,7 @@ #include "nvim/mark.h" #include "nvim/fileio.h" #include "nvim/move.h" +#include "nvim/syntax.h" #include "nvim/window.h" #include "nvim/undo.h" @@ -514,6 +515,99 @@ ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err) return rv; } +/// 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 +/// 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 +/// buffer_clear_highlight. If the highlight never will be manually deleted +/// pass in -1 for "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. +/// +/// @param buffer The 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 The line to highlight +/// @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 +/// @param[out] err Details of an error that may have occurred +/// @return The src_id that was used +Integer buffer_add_highlight(Buffer buffer, + Integer src_id, + String hl_group, + Integer line, + Integer col_start, + Integer col_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return 0; + } + + if (line < 0 || line >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return 0; + } + if (col_start < 0 || col_start > MAXCOL) { + api_set_error(err, Validation, _("Column value outside range")); + return 0; + } + if (col_end < 0 || col_end > MAXCOL) { + col_end = MAXCOL; + } + + int hlg_id = syn_name2id((char_u*)hl_group.data); + src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1, + (colnr_T)col_start+1, (colnr_T)col_end); + return src_id; +} + +/// Clears highlights from a given source group and a range of lines +/// +/// To clear a source group in the entire buffer, pass in 1 and -1 to +/// line_start and line_end respectively. +/// +/// @param buffer The buffer handle +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param line_start Start of range of lines to clear +/// @param line_end End of range of lines to clear (exclusive) +/// or -1 to clear to end of file. +/// @param[out] err Details of an error that may have occurred +void buffer_clear_highlight(Buffer buffer, + Integer src_id, + Integer line_start, + Integer line_end, + Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return; + } + + if (line_start < 0 || line_start >= MAXLNUM) { + api_set_error(err, Validation, _("Line number outside range")); + return; + } + if (line_end < 0 || line_end > MAXLNUM) { + line_end = MAXLNUM; + } + + bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); +} // Check if deleting lines made the cursor position invalid. // Changed the lines from "lo" to "hi" and added "extra" lines (negative if diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c05090bbf6..62ab7495da 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -580,16 +580,17 @@ free_buffer_stuff ( ) { if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); + clear_wininfo(buf); // including window-local options + free_buf_options(buf, true); ga_clear(&buf->b_s.b_langp); } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ + vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ + uc_clear(&buf->b_ucmds); // clear local user commands + buf_delete_signs(buf); // delete any signs + bufhl_clear_all(buf); // delete any highligts + map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings + map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs xfree(buf->b_start_fenc); buf->b_start_fenc = NULL; } @@ -4870,6 +4871,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a } } +// bufhl: plugin highlights associated with a buffer + +/// Adds a highlight to buffer. +/// +/// Unlike matchaddpos() highlights follow changes to line numbering (as lines +/// are inserted/removed above the highlighted line), like signs and marks do. +/// +/// When called with "src_id" set to 0, a unique source id is generated and +/// returned. Succesive calls can pass it in as "src_id" to add new highlights +/// to the same source group. All highlights in the same group can be cleared +/// at once. If the highlight never will be manually deleted pass in -1 for +/// "src_id" +/// +/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id +/// is still returned. +/// +/// @param buf The 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 Id of the highlight group to use +/// @param lnum The line to highlight +/// @param col_start First column to highlight +/// @param col_end The last column to highlight, +/// or -1 to highlight to end of line +/// @return The src_id that was used +int bufhl_add_hl(buf_T *buf, + int src_id, + int hl_id, + linenr_T lnum, + colnr_T col_start, + colnr_T col_end) { + static int next_src_id = 1; + if (src_id == 0) { + src_id = next_src_id++; + } + if (hl_id <= 0) { + // no highlight group or invalid line, just return src_id + return src_id; + } + if (!buf->b_bufhl_info) { + buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)(); + } + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, + lnum, true); + + bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); + hlentry->src_id = src_id; + hlentry->hl_id = hl_id; + hlentry->start = col_start; + hlentry->stop = col_end; + + if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { + changed_lines_buf(buf, lnum, lnum+1, 0); + redraw_buf_later(buf, VALID); + } + return src_id; +} + +/// Clear bufhl highlights from a given source group and range of lines. +/// +/// @param buf The buffer to remove highlights from +/// @param src_id highlight source group to clear, or -1 to clear all groups. +/// @param line_start first line to clear +/// @param line_end last line to clear or MAXLNUM to clear to end of file. +void bufhl_clear_line_range(buf_T *buf, + int src_id, + linenr_T line_start, + linenr_T line_end) { + if (!buf->b_bufhl_info) { + return; + } + linenr_T line; + linenr_T first_changed = MAXLNUM, last_changed = -1; + // In the case line_start - line_end << bufhl_info->size + // it might be better to reverse this, i e loop over the lines + // to clear on. + bufhl_vec_T unused; + map_foreach(buf->b_bufhl_info, line, unused, { + (void)unused; + if (line_start <= line && line <= line_end) { + if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) { + if (line > last_changed) { + last_changed = line; + } + if (line < first_changed) { + first_changed = line; + } + } + } + }) + + if (last_changed != -1) { + changed_lines_buf(buf, first_changed, last_changed+1, 0); + redraw_buf_later(buf, VALID); + } +} + +/// Clear bufhl highlights from a given source group and given line +/// +/// @param bufhl_info The highlight info for the buffer +/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param lnum Linenr where the highlight should be cleared +static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) { + bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info, + lnum, false); + size_t oldsize = kv_size(*lineinfo); + if (src_id < 0) { + kv_size(*lineinfo) = 0; + } else { + size_t newind = 0; + for (size_t i = 0; i < kv_size(*lineinfo); i++) { + if (kv_A(*lineinfo, i).src_id != src_id) { + if (i != newind) { + kv_A(*lineinfo, newind) = kv_A(*lineinfo, i); + } + newind++; + } + } + kv_size(*lineinfo) = newind; + } + + if (kv_size(*lineinfo) == 0) { + kv_destroy(*lineinfo); + map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum); + } + return kv_size(*lineinfo) != oldsize; +} + +/// Remove all highlights and free the highlight data +void bufhl_clear_all(buf_T* buf) { + if (!buf->b_bufhl_info) { + return; + } + bufhl_clear_line_range(buf, -1, 1, MAXLNUM); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = NULL; +} + +/// Adjust a placed highlight for inserted/deleted lines. +void bufhl_mark_adjust(buf_T* buf, + linenr_T line1, + linenr_T line2, + long amount, + long amount_after) { + if (!buf->b_bufhl_info) { + return; + } + + bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)(); + linenr_T line; + bufhl_vec_T lineinfo; + map_foreach(buf->b_bufhl_info, line, lineinfo, { + if (line >= line1 && line <= line2) { + if (amount == MAXLNUM) { + bufhl_clear_line(buf->b_bufhl_info, -1, line); + continue; + } else { + line += amount; + } + } else if (line > line2) { + line += amount_after; + } + map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo); + }); + map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info); + buf->b_bufhl_info = newmap; +} + + +/// Get highlights to display at a specific line +/// +/// @param buf The buffer handle +/// @param lnum The line number +/// @param[out] info The highligts for the line +/// @return true if there was highlights to display +bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) { + if (!buf->b_bufhl_info) { + return false; + } + + info->valid_to = -1; + info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum); + return kv_size(info->entries) > 0; +} + +/// get highlighting at column col +/// +/// It is is assumed this will be called with +/// non-decreasing column nrs, so that it is +/// possible to only recalculate highlights +/// at endpoints. +/// +/// @param info The info returned by bufhl_start_line +/// @param col The column to get the attr for +/// @return The highilight attr to display at the column +int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) { + if (col <= info->valid_to) { + return info->current; + } + int attr = 0; + info->valid_to = MAXCOL; + for (size_t i = 0; i < kv_size(info->entries); i++) { + bufhl_hl_item_T entry = kv_A(info->entries, i); + if (entry.start <= col && col <= entry.stop) { + int entry_attr = syn_id2attr(entry.hl_id); + attr = hl_combine_attr(attr, entry_attr); + if (entry.stop < info->valid_to) { + info->valid_to = entry.stop; + } + } else if (col < entry.start && entry.start-1 < info->valid_to) { + info->valid_to = entry.start-1; + } + } + info->current = attr; + return attr; +} + + /* * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. */ diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index bdea609820..936a14b903 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration #include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" +// for Map(K, V) +#include "nvim/map.h" #define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma) @@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration #define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */ #define VALID_TOPLINE 0x80 /* w_topline is valid (for cursor position) */ -/* flags for b_flags */ -#define BF_RECOVERED 0x01 /* buffer has been recovered */ -#define BF_CHECK_RO 0x02 /* need to check readonly when loading file - into buffer (set by ":e", may be reset by - ":buf" */ -#define BF_NEVERLOADED 0x04 /* file has never been loaded into buffer, - many variables still need to be set */ -#define BF_NOTEDITED 0x08 /* Set when file name is changed after - starting to edit, reset when file is - written out. */ -#define BF_NEW 0x10 /* file didn't exist when editing started */ -#define BF_NEW_W 0x20 /* Warned for BF_NEW and file created */ -#define BF_READERR 0x40 /* got errors while reading the file */ -#define BF_DUMMY 0x80 /* dummy buffer, only used internally */ -#define BF_PRESERVED 0x100 /* ":preserve" was used */ +// flags for b_flags +#define BF_RECOVERED 0x01 // buffer has been recovered +#define BF_CHECK_RO 0x02 // need to check readonly when loading file + // into buffer (set by ":e", may be reset by + // ":buf") +#define BF_NEVERLOADED 0x04 // file has never been loaded into buffer, + // many variables still need to be set +#define BF_NOTEDITED 0x08 // Set when file name is changed after + // starting to edit, reset when file is + // written out. +#define BF_NEW 0x10 // file didn't exist when editing started +#define BF_NEW_W 0x20 // Warned for BF_NEW and file created +#define BF_READERR 0x40 // got errors while reading the file +#define BF_DUMMY 0x80 // dummy buffer, only used internally +#define BF_PRESERVED 0x100 // ":preserve" was used /* Mask to check for flags that prevent normal writing */ #define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR) @@ -101,6 +103,11 @@ typedef int scid_T; /* script ID */ // for signlist_T #include "nvim/sign_defs.h" +// for bufhl_*_T +#include "nvim/bufhl_defs.h" + +typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T; + // for FileID #include "nvim/os/fs_defs.h" @@ -754,6 +761,8 @@ struct file_buffer { dict_T *additional_data; // Additional data from shada file if any. int b_mapped_ctrl_c; // modes where CTRL-C is mapped + + bufhl_info_T *b_bufhl_info; // buffer stored highlights }; /* diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h new file mode 100644 index 0000000000..e47bb2a238 --- /dev/null +++ b/src/nvim/bufhl_defs.h @@ -0,0 +1,25 @@ +#ifndef NVIM_BUFHL_DEFS_H +#define NVIM_BUFHL_DEFS_H + +#include "nvim/pos.h" +#include "nvim/lib/kvec.h" +// bufhl: buffer specific highlighting + +struct bufhl_hl_item +{ + int src_id; + int hl_id; // highlight group + colnr_T start; // first column to highlight + colnr_T stop; // last column to highlight +}; +typedef struct bufhl_hl_item bufhl_hl_item_T; + +typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T; + +typedef struct { + bufhl_vec_T entries; + int current; + colnr_T valid_to; +} bufhl_lineinfo_T; + +#endif // NVIM_BUFHL_DEFS_H diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b011ee9d54..31175773f0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7324,10 +7324,10 @@ static struct fst { { "setcharsearch", 1, 1, f_setcharsearch }, { "setcmdpos", 1, 1, f_setcmdpos }, { "setline", 2, 2, f_setline }, - { "setloclist", 2, 3, f_setloclist }, + { "setloclist", 2, 4, f_setloclist }, { "setmatches", 1, 1, f_setmatches }, { "setpos", 2, 2, f_setpos }, - { "setqflist", 1, 2, f_setqflist }, + { "setqflist", 1, 3, f_setqflist }, { "setreg", 2, 3, f_setreg }, { "settabvar", 3, 3, f_settabvar }, { "settabwinvar", 4, 4, f_settabwinvar }, @@ -15215,14 +15215,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv) appended_lines_mark(lcount, added); } - -/* - * Used by "setqflist()" and "setloclist()" functions - */ -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv) +/// Create quickfix/location list from VimL values +/// +/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid +/// list_arg, action_arg and title_arg arguments in which case errors out, +/// including VAR_UNKNOWN parameters. +/// +/// @param[in,out] wp Window to create location list for. May be NULL in +/// which case quickfix list will be created. +/// @param[in] list_arg Quickfix list contents. +/// @param[in] action_arg Action to perform: append to an existing list, +/// replace its content or create a new one. +/// @param[in] title_arg New list title. Defaults to caller function name. +/// @param[out] rettv Return value: 0 in case of success, -1 otherwise. +static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, + typval_T *title_arg, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) { char_u *act; int action = ' '; + char_u *title = NULL; rettv->vval.v_number = -1; @@ -15231,7 +15243,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, else { list_T *l = list_arg->vval.v_list; - if (action_arg->v_type == VAR_STRING) { + if (action_arg->v_type != VAR_UNKNOWN) { act = get_tv_string_chk(action_arg); if (act == NULL) return; /* type error; errmsg already given */ @@ -15239,9 +15251,20 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, action = *act; } - if (l != NULL && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) + if (title_arg->v_type != VAR_UNKNOWN) { + title = get_tv_string_chk(title_arg); + if (!title) { + return; // type error; errmsg already given + } + } + + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } + + if (l && set_errorlist(wp, l, action, title) == OK) { rettv->vval.v_number = 0; + } } } @@ -15255,8 +15278,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = -1; win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); + if (win != NULL) { + set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); + } } /* @@ -15392,7 +15416,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); + set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv); } /* diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2d17b31f0f..3a24f194c1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4320,7 +4320,7 @@ static void ex_unmap(exarg_T *eap) */ static void ex_mapclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, eap->forceit, FALSE); + map_clear_mode(eap->cmd, eap->arg, eap->forceit, false); } /* @@ -4328,7 +4328,7 @@ static void ex_mapclear(exarg_T *eap) */ static void ex_abclear(exarg_T *eap) { - map_clear(eap->cmd, eap->arg, TRUE, TRUE); + map_clear_mode(eap->cmd, eap->arg, true, true); } static void ex_autocmd(exarg_T *eap) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 73b3de0b5d..437495faa4 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2915,9 +2915,9 @@ do_map ( did_it = TRUE; } } - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); - continue; /* continue with *mpp */ + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); + continue; // continue with *mpp } /* @@ -3012,7 +3012,7 @@ theend: * Delete one entry from the abbrlist or maphash[]. * "mpp" is a pointer to the m_next field of the PREVIOUS entry! */ -static void map_free(mapblock_T **mpp) +static void mapblock_free(mapblock_T **mpp) { mapblock_T *mp; @@ -3080,7 +3080,7 @@ int get_map_mode(char_u **cmdp, int forceit) * Clear all mappings or abbreviations. * 'abbr' should be FALSE for mappings, TRUE for abbreviations. */ -void map_clear(char_u *cmdp, char_u *arg, int forceit, int abbr) +void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr) { int mode; int local; @@ -3132,8 +3132,8 @@ map_clear_int ( mp = *mpp; if (mp->m_mode & mode) { mp->m_mode &= ~mode; - if (mp->m_mode == 0) { /* entry can be deleted */ - map_free(mpp); + if (mp->m_mode == 0) { // entry can be deleted + mapblock_free(mpp); continue; } /* diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index d236501b3f..7169a1d963 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1632,77 +1632,79 @@ static char *cs_pathcomponents(char *path) return s; } -/* - * PRIVATE: cs_print_tags_priv - * - * called from cs_manage_matches() - */ +/// Print cscope output that was converted into ctags style entries. +/// +/// Only called from cs_manage_matches(). +/// +/// @param matches Array of cscope lines in ctags style. Every entry was +// produced with a format string of the form +// "%s\t%s\t%s;\"\t%s" or +// "%s\t%s\t%s;\"" +// by cs_make_vim_style_matches(). +/// @param cntxts Context for matches. +/// @param num_matches Number of entries in matches/cntxts, always greater 0. static void cs_print_tags_priv(char **matches, char **cntxts, - size_t num_matches) + size_t num_matches) FUNC_ATTR_NONNULL_ALL { - char *ptag; - char *fname, *lno, *extra, *tbuf; - size_t num; - char *globalcntx = "GLOBAL"; - char *context; - char *cstag_msg = _("Cscope tag: %s"); + char *globalcntx = "GLOBAL"; + char *cstag_msg = _("Cscope tag: %s"); - assert (num_matches > 0); + assert(num_matches > 0); + assert(strcnt(matches[0], '\t') >= 2); - tbuf = xmalloc(strlen(matches[0]) + 1); + char *ptag = matches[0]; + char *ptag_end = strchr(ptag, '\t'); + assert(ptag_end >= ptag); + // NUL terminate tag string in matches[0]. + *ptag_end = NUL; - strcpy(tbuf, matches[0]); - ptag = strtok(tbuf, "\t"); - - size_t newsize = strlen(cstag_msg) + strlen(ptag); + // The "%s" in cstag_msg won't appear in the result string, so we don't need + // extra memory for terminating NUL. + size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag); char *buf = xmalloc(newsize); size_t bufsize = newsize; // Track available bufsize - (void)sprintf(buf, cstag_msg, ptag); + (void)snprintf(buf, bufsize, cstag_msg, ptag); MSG_PUTS_ATTR(buf, hl_attr(HLF_T)); + msg_clr_eos(); - xfree(tbuf); + // restore matches[0] + *ptag_end = '\t'; - MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */ + // Column headers for match number, line number and filename. + 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)); - num = 1; for (size_t i = 0; i < num_matches; i++) { - size_t idx = i; - - /* if we really wanted to, we could avoid this malloc and strcpy - * by parsing matches[i] on the fly and placing stuff into buf - * directly, but that's too much of a hassle - */ - tbuf = xmalloc(strlen(matches[idx]) + 1); - (void)strcpy(tbuf, matches[idx]); - - if (strtok(tbuf, (const char *)"\t") == NULL) - continue; - if ((fname = strtok(NULL, (const char *)"\t")) == NULL) - continue; - if ((lno = strtok(NULL, (const char *)"\t")) == NULL) - continue; - extra = strtok(NULL, (const char *)"\t"); - - lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */ + assert(strcnt(matches[i], '\t') >= 2); + + // Parse filename, line number and optional part. + char *fname = strchr(matches[i], '\t') + 1; + char *fname_end = strchr(fname, '\t'); + // Replace second '\t' in matches[i] with NUL to terminate fname. + *fname_end = NUL; + + char *lno = fname_end + 1; + char *extra = xstrchrnul(lno, '\t'); + // Ignore ;" at the end of lno. + char *lno_end = extra - 2; + *lno_end = NUL; + // Do we have an optional part? + extra = *extra ? extra + 1 : NULL; const char *csfmt_str = "%4zu %6s "; - /* hopefully 'num' (num of matches) will be less than 10^16 */ - newsize = strlen(csfmt_str) + 16 + strlen(lno); + // hopefully num_matches will be less than 10^16 + newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno); if (bufsize < newsize) { buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, csfmt_str, num, lno); + (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)); - /* compute the required space for the context */ - if (cntxts[idx] != NULL) - context = cntxts[idx]; - else - context = globalcntx; + // compute the required space for the context + char *context = cntxts[i] ? cntxts[i] : globalcntx; const char *cntxformat = " <<%s>>"; // '%s' won't appear in result string, so: @@ -1713,11 +1715,13 @@ static void cs_print_tags_priv(char **matches, char **cntxts, buf = xrealloc(buf, newsize); bufsize = newsize; } - (void)sprintf(buf, cntxformat, context); + int buf_len = snprintf(buf, bufsize, cntxformat, context); + assert(buf_len >= 0); - /* print the context only if it fits on the same line */ - if (msg_col + (int)strlen(buf) >= (int)Columns) + // Print the context only if it fits on the same line. + if (msg_col + buf_len >= (int)Columns) { msg_putchar('\n'); + } msg_advance(12); MSG_PUTS_LONG(buf); msg_putchar('\n'); @@ -1726,23 +1730,23 @@ static void cs_print_tags_priv(char **matches, char **cntxts, MSG_PUTS_LONG(extra); } - xfree(tbuf); /* only after printing extra due to strtok use */ + // restore matches[i] + *fname_end = '\t'; + *lno_end = ';'; - if (msg_col) + if (msg_col) { msg_putchar('\n'); + } os_breakcheck(); if (got_int) { - got_int = FALSE; /* don't print any more matches */ + got_int = false; // don't print any more matches break; } - - num++; - } /* for all matches */ + } xfree(buf); -} /* cs_print_tags_priv */ - +} /* * PRIVATE: cs_read_prompt diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index 56be29d14c..8287cb14da 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -184,7 +184,7 @@ typedef khint_t khiter_t; #define kfree(P) xfree(P) #endif -static const double __ac_HASH_UPPER = 0.77; +#define __ac_HASH_UPPER 0.77 #define __KHASH_TYPE(name, khkey_t, khval_t) \ typedef struct { \ diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 0466cb229c..53ecf232c6 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -77,10 +77,10 @@ int main() { (v).items[(v).size++] = (x); \ } while (0) -#define kv_pushp(type, v) (((v).size == (v).capacity)? \ +#define kv_pushp(type, v) ((((v).size == (v).capacity)? \ ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \ (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ - : 0), ((v).items + ((v).size++)) + : 0), ((v).items + ((v).size++))) #define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ diff --git a/src/nvim/map.c b/src/nvim/map.c index ed7bda4cce..d4262ae9a8 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -18,6 +18,9 @@ #define uint32_t_eq kh_int_hash_equal #define int_hash kh_int_hash_func #define int_eq kh_int_hash_equal +#define linenr_T_hash kh_int_hash_func +#define linenr_T_eq kh_int_hash_equal + #if defined(ARCH_64) #define ptr_t_hash(key) uint64_t_hash((uint64_t)key) @@ -78,6 +81,25 @@ return rv; \ } \ \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ + { \ + int ret; \ + khiter_t k; \ + if (put) { \ + k = kh_put(T##_##U##_map, map->table, key, &ret); \ + if (ret) { \ + kh_val(map->table, k) = INITIALIZER(T, U); \ + } \ + } else { \ + k = kh_get(T##_##U##_map, map->table, key); \ + if (k == kh_end(map->table)) { \ + return NULL; \ + } \ + } \ + \ + return &kh_val(map->table, k); \ + } \ + \ U map_##T##_##U##_del(Map(T, U) *map, T key) \ { \ U rv = INITIALIZER(T, U); \ @@ -118,3 +140,5 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) #define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false} MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) +#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } +MAP_IMPL(linenr_T, bufhl_vec_T, KVEC_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c0e2ca3aac..e90cc360ce 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -6,6 +6,7 @@ #include "nvim/map_defs.h" #include "nvim/api/private/defs.h" #include "nvim/msgpack_rpc/defs.h" +#include "nvim/bufhl_defs.h" #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ @@ -19,6 +20,7 @@ U map_##T##_##U##_get(Map(T, U) *map, T key); \ bool map_##T##_##U##_has(Map(T, U) *map, T key); \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ U map_##T##_##U##_del(Map(T, U) *map, T key); \ void map_##T##_##U##_clear(Map(T, U) *map); @@ -28,12 +30,14 @@ MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) +MAP_DECLS(linenr_T, bufhl_vec_T) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free #define map_get(T, U) map_##T##_##U##_get #define map_has(T, U) map_##T##_##U##_has #define map_put(T, U) map_##T##_##U##_put +#define map_ref(T, U) map_##T##_##U##_ref #define map_del(T, U) map_##T##_##U##_del #define map_clear(T, U) map_##T##_##U##_clear diff --git a/src/nvim/mark.c b/src/nvim/mark.c index e2f212340c..fe802e48ba 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -922,6 +922,7 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } sign_mark_adjust(line1, line2, amount, amount_after); + bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after); } /* previous context mark */ diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6c969a43fc..db303fd54a 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1985,13 +1985,13 @@ changed_lines ( changed_common(lnum, col, lnume, xtra); } -static void -changed_lines_buf ( - buf_T *buf, - linenr_T lnum, /* first line with change */ - linenr_T lnume, /* line below last changed line */ - long xtra /* number of extra lines (negative when deleting) */ -) +/// Mark line range in buffer as changed. +/// +/// @param buf the buffer where lines were changed +/// @param lnum first line with change +/// @param lnume line below last changed line +/// @param xtra number of extra lines (negative when deleting) +void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra) { if (buf->b_mod_set) { /* find the maximum area that must be redisplayed */ diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index d97cf28ca1..5611636d4f 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -1,8 +1,6 @@ #ifndef NVIM_MSGPACK_RPC_DEFS_H #define NVIM_MSGPACK_RPC_DEFS_H -#include <msgpack.h> - /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. @@ -24,22 +22,6 @@ void msgpack_rpc_add_method_handler(String method, void msgpack_rpc_init_function_metadata(Dictionary *metadata); -/// Dispatches to the actual API function after basic payload validation by -/// `msgpack_rpc_call`. It is responsible for validating/converting arguments -/// to C types, and converting the return value back to msgpack types. -/// The implementation is generated at compile time with metadata extracted -/// from the api/*.h headers, -/// -/// @param channel_id The channel id -/// @param method_id The method id -/// @param req The parsed request object -/// @param error Pointer to error structure -/// @return Some object -Object msgpack_rpc_dispatch(uint64_t channel_id, - msgpack_object *req, - Error *error) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); - MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len) FUNC_ATTR_NONNULL_ARG(1); diff --git a/src/nvim/option.c b/src/nvim/option.c index 0f6874e941..af7b272467 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5947,13 +5947,17 @@ option_value2string ( if (opp->flags & P_NUM) { long wc = 0; - if (wc_use_keyname(varp, &wc)) - STRCPY(NameBuff, get_special_key_name((int)wc, 0)); - else if (wc != 0) - STRCPY(NameBuff, transchar((int)wc)); - else - sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp); - } else { /* P_STRING */ + if (wc_use_keyname(varp, &wc)) { + STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff)); + } else if (wc != 0) { + STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff)); + } else { + snprintf((char *)NameBuff, + sizeof(NameBuff), + "%" PRId64, + (int64_t)*(long *)varp); + } + } else { // P_STRING varp = *(char_u **)(varp); if (varp == NULL) /* just in case */ NameBuff[0] = NUL; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3b5836f0b5..cd440fe8dc 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2184,6 +2184,10 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; + bool has_bufhl = false; // this buffer has highlight matches + int bufhl_attr = 0; // attributes desired by bufhl + bufhl_lineinfo_T bufhl_info; // bufhl data for this line + /* draw_state: items that are drawn in sequence: */ #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ @@ -2244,6 +2248,11 @@ win_line ( } } + if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) { + has_bufhl = true; + extra_check = true; + } + /* Check for columns to display for 'colorcolumn'. */ color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols; if (color_cols != NULL) @@ -3335,6 +3344,17 @@ win_line ( char_attr = hl_combine_attr(spell_attr, char_attr); } + if (has_bufhl && v > 0) { + bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v); + if (bufhl_attr != 0) { + if (!attr_pri) { + char_attr = hl_combine_attr(char_attr, bufhl_attr); + } else { + char_attr = hl_combine_attr(bufhl_attr, char_attr); + } + } + } + if (wp->w_buffer->terminal) { char_attr = hl_combine_attr(char_attr, term_attrs[vcol]); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index fb74569e3b..42fd81f643 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -141,10 +141,6 @@ struct terminal { int pressed_button; // pending width/height bool pending_resize; - // color palette. this isn't set directly in the vterm instance because - // the default values are used to obtain the color numbers passed to cterm - // colors - RgbValue colors[256]; // With a reference count of 0 the terminal can be freed. size_t refcount; }; @@ -205,6 +201,7 @@ void terminal_teardown(void) Terminal *terminal_open(TerminalOptions opts) { + bool true_color = ui_rgb_attached(); // Create a new terminal instance and configure it Terminal *rv = xcalloc(1, sizeof(Terminal)); rv->opts = opts; @@ -220,7 +217,7 @@ Terminal *terminal_open(TerminalOptions opts) // Set up screen rv->vts = vterm_obtain_screen(rv->vt); vterm_screen_enable_altscreen(rv->vts, true); - // delete empty lines at the end of the buffer + // delete empty lines at the end of the buffer vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(rv->vts, 1); @@ -250,12 +247,18 @@ Terminal *terminal_open(TerminalOptions opts) rv->sb_size = MIN(rv->sb_size, 100000); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); + if (!true_color) { + return rv; + } + + vterm_state_set_bold_highbright(state, true); + // Configure the color palette. Try to get the color from: // // - b:terminal_color_{NUM} // - g:terminal_color_{NUM} // - the VTerm instance - for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) { + for (int i = 0; i < 16; i++) { RgbValue color_val = -1; char var[64]; snprintf(var, sizeof(var), "terminal_color_%d", i); @@ -265,16 +268,13 @@ Terminal *terminal_open(TerminalOptions opts) xfree(name); if (color_val != -1) { - rv->colors[i] = color_val; + VTermColor color; + color.red = (uint8_t)((color_val >> 16) & 0xFF); + color.green = (uint8_t)((color_val >> 8) & 0xFF); + color.blue = (uint8_t)((color_val >> 0) & 0xFF); + vterm_state_set_palette_color(state, i, &color); } } - - if (color_val == -1) { - // the default is taken from vterm - VTermColor color; - vterm_state_get_palette_color(state, i, &color); - rv->colors[i] = RGB(color.red, color.green, color.blue); - } } return rv; @@ -548,10 +548,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, map_get(int, int)(color_indexes, vt_fg) : 0; int vt_bg_idx = vt_bg != default_vt_bg ? map_get(int, int)(color_indexes, vt_bg) : 0; - // The index is now used to get the final rgb value from the - // user-customizable palette. - int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1; - int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) | (cell.attrs.italic ? HL_ITALIC : 0) @@ -566,8 +562,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, .cterm_fg_color = vt_fg_idx, .cterm_bg_color = vt_bg_idx, .rgb_ae_attr = (int16_t)hl_attrs, - .rgb_fg_color = vt_fg_rgb, - .rgb_bg_color = vt_bg_rgb, + .rgb_fg_color = vt_fg, + .rgb_bg_color = vt_bg, }); } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index b8cdffcda0..4a8a24d79d 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -4,29 +4,29 @@ * The saved lines are stored in a list of lists (one for each buffer): * * b_u_oldhead------------------------------------------------+ - * | - * V - * +--------------+ +--------------+ +--------------+ - * b_u_newhead--->| u_header | | u_header | | u_header | - * | uh_next------>| uh_next------>| uh_next---->NULL - * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | - * | uh_entry | | uh_entry | | uh_entry | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ +--------------+ +--------------+ - * | u_entry | | u_entry | | u_entry | - * | ue_next | | ue_next | | ue_next | - * +--------|-----+ +--------|-----+ +--------|-----+ - * | | | - * V V V - * +--------------+ NULL NULL - * | u_entry | - * | ue_next | - * +--------|-----+ - * | - * V - * etc. + * | + * V + * +--------------+ +--------------+ +--------------+ + * b_u_newhead--->| u_header | | u_header | | u_header | + * | uh_next------>| uh_next------>| uh_next---->NULL + * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | + * | uh_entry | | uh_entry | | uh_entry | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ +--------------+ +--------------+ + * | u_entry | | u_entry | | u_entry | + * | ue_next | | ue_next | | ue_next | + * +--------|-----+ +--------|-----+ +--------|-----+ + * | | | + * V V V + * +--------------+ NULL NULL + * | u_entry | + * | ue_next | + * +--------|-----+ + * | + * V + * etc. * * Each u_entry list contains the information for one undo or redo. * curbuf->b_u_curhead points to the header of the last undo (the next redo), @@ -37,30 +37,30 @@ * uh_seq field is numbered sequentially to be able to find a newer or older * branch. * - * +---------------+ +---------------+ - * b_u_oldhead --->| u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next ----> NULL - * NULL <----- uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next | | uh_alt_next | - * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * V V - * NULL +---------------+ +---------------+ - * | u_header | | u_header | - * | uh_alt_next ---->| uh_alt_next | - * | uh_alt_prev |<------ uh_alt_prev | - * | uh_prev | | uh_prev | - * +-----|---------+ +-----|---------+ - * | | - * etc. etc. + * +---------------+ +---------------+ + * b_u_oldhead --->| u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next ----> NULL + * NULL <----- uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next | | uh_alt_next | + * b_u_newhead --->| uh_alt_prev | | uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * V V + * NULL +---------------+ +---------------+ + * | u_header | | u_header | + * | uh_alt_next ---->| uh_alt_next | + * | uh_alt_prev |<------ uh_alt_prev | + * | uh_prev | | uh_prev | + * +-----|---------+ +-----|---------+ + * | | + * etc. etc. * * * All data is allocated and will all be freed when the buffer is unloaded. |