From 18766e742bdc8d179ff73b739a530052c9a669e5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 06:07:59 +0800 Subject: fix(folds): fix fold remains when :delete makes buffer empty (#19673) --- src/nvim/change.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/change.c b/src/nvim/change.c index c063ece907..f0ca5f3af3 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -421,12 +421,12 @@ void deleted_lines(linenr_T lnum, linenr_T count) /// be triggered to display the cursor. void deleted_lines_mark(linenr_T lnum, long count) { - // if we deleted the entire buffer, we need to implicitly add a new empty line bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY; - mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, - -(linenr_T)count + (made_empty?1:0), - kExtmarkUndo); + mark_adjust(lnum, (linenr_T)(lnum + count - 1), MAXLNUM, -(linenr_T)count, kExtmarkNOOP); + // if we deleted the entire buffer, we need to implicitly add a new empty line + extmark_adjust(curbuf, lnum, (linenr_T)(lnum + count - 1), MAXLNUM, + -(linenr_T)count + (made_empty ? 1 : 0), kExtmarkUndo); changed_lines(lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true); } -- cgit From bc8fbb7c1de0e5cbc8650be883a675bdc3e9d7d8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 11:35:44 +0800 Subject: refactor: move non-symbols in mbyte.h to mbyte_defs.h This just avoids including mbyte.h in eval/typval.h, so that mbyte.h can include eval/typval.h in Vim patch 8.2.1535. --- src/nvim/eval/typval.h | 2 +- src/nvim/grid.h | 1 + src/nvim/mbyte.h | 48 +------------------------------------------ src/nvim/mbyte_defs.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 48 deletions(-) create mode 100644 src/nvim/mbyte_defs.h (limited to 'src/nvim') diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c02351947b..2a4dd7b146 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -13,7 +13,7 @@ #include "nvim/hashtab.h" #include "nvim/lib/queue.h" #include "nvim/macros.h" -#include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/message.h" #include "nvim/pos.h" // for linenr_T #include "nvim/profile.h" // for proftime_T diff --git a/src/nvim/grid.h b/src/nvim/grid.h index c38748940d..4a8072bd96 100644 --- a/src/nvim/grid.h +++ b/src/nvim/grid.h @@ -6,6 +6,7 @@ #include "nvim/ascii.h" #include "nvim/buffer_defs.h" #include "nvim/grid_defs.h" +#include "nvim/mbyte.h" /// By default, all windows are drawn on a single rectangular grid, represented by /// this ScreenGrid instance. In multigrid mode each window will have its own diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 1e5e332ad9..ffa8411675 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -6,7 +6,7 @@ #include #include "nvim/func_attr.h" -#include "nvim/iconv.h" +#include "nvim/mbyte_defs.h" #include "nvim/os/os_defs.h" // For indirect #include "nvim/types.h" // for char_u @@ -19,52 +19,6 @@ #define MB_BYTE2LEN(b) utf8len_tab[b] #define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b]) -// max length of an unicode char -#define MB_MAXCHAR 6 - -// properties used in enc_canon_table[] (first three mutually exclusive) -#define ENC_8BIT 0x01 -#define ENC_DBCS 0x02 -#define ENC_UNICODE 0x04 - -#define ENC_ENDIAN_B 0x10 // Unicode: Big endian -#define ENC_ENDIAN_L 0x20 // Unicode: Little endian - -#define ENC_2BYTE 0x40 // Unicode: UCS-2 -#define ENC_4BYTE 0x80 // Unicode: UCS-4 -#define ENC_2WORD 0x100 // Unicode: UTF-16 - -#define ENC_LATIN1 0x200 // Latin1 -#define ENC_LATIN9 0x400 // Latin9 -#define ENC_MACROMAN 0x800 // Mac Roman (not Macro Man! :-) - -/// Flags for vimconv_T -typedef enum { - CONV_NONE = 0, - CONV_TO_UTF8 = 1, - CONV_9_TO_UTF8 = 2, - CONV_TO_LATIN1 = 3, - CONV_TO_LATIN9 = 4, - CONV_ICONV = 5, -} ConvFlags; - -#define MBYTE_NONE_CONV { \ - .vc_type = CONV_NONE, \ - .vc_factor = 1, \ - .vc_fail = false, \ -} - -/// Structure used for string conversions -typedef struct { - int vc_type; ///< Zero or more ConvFlags. - int vc_factor; ///< Maximal expansion factor. -#ifdef HAVE_ICONV - iconv_t vc_fd; ///< Value for CONV_ICONV. -#endif - bool vc_fail; ///< What to do with invalid characters: if true, fail, - ///< otherwise use '?'. -} vimconv_T; - extern const uint8_t utf8len_tab_zero[256]; extern const uint8_t utf8len_tab[256]; diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h new file mode 100644 index 0000000000..53b01a211f --- /dev/null +++ b/src/nvim/mbyte_defs.h @@ -0,0 +1,56 @@ +#ifndef NVIM_MBYTE_DEFS_H +#define NVIM_MBYTE_DEFS_H + +#include + +#include "nvim/iconv.h" + +/// max length of an unicode char +enum { MB_MAXCHAR = 6, }; + +/// properties used in enc_canon_table[] (first three mutually exclusive) +enum { + ENC_8BIT = 0x01, + ENC_DBCS = 0x02, + ENC_UNICODE = 0x04, + + ENC_ENDIAN_B = 0x10, ///< Unicode: Big endian + ENC_ENDIAN_L = 0x20, ///< Unicode: Little endian + + ENC_2BYTE = 0x40, ///< Unicode: UCS-2 + ENC_4BYTE = 0x80, ///< Unicode: UCS-4 + ENC_2WORD = 0x100, ///< Unicode: UTF-16 + + ENC_LATIN1 = 0x200, ///< Latin1 + ENC_LATIN9 = 0x400, ///< Latin9 + ENC_MACROMAN = 0x800, ///< Mac Roman (not Macro Man! :-) +}; + +/// Flags for vimconv_T +typedef enum { + CONV_NONE = 0, + CONV_TO_UTF8 = 1, + CONV_9_TO_UTF8 = 2, + CONV_TO_LATIN1 = 3, + CONV_TO_LATIN9 = 4, + CONV_ICONV = 5, +} ConvFlags; + +#define MBYTE_NONE_CONV { \ + .vc_type = CONV_NONE, \ + .vc_factor = 1, \ + .vc_fail = false, \ +} + +/// Structure used for string conversions +typedef struct { + int vc_type; ///< Zero or more ConvFlags. + int vc_factor; ///< Maximal expansion factor. +#ifdef HAVE_ICONV + iconv_t vc_fd; ///< Value for CONV_ICONV. +#endif + bool vc_fail; ///< What to do with invalid characters: if true, fail, + ///< otherwise use '?'. +} vimconv_T; + +#endif // NVIM_MBYTE_DEFS_H -- cgit From 53c9500c1dc95eb446e13e857a7d6a7642d859ab Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 08:22:10 +0800 Subject: vim-patch:8.2.1535: it is not possible to specify cell widths of characters Problem: It is not possible to specify cell widths of characters. Solution: Add setcellwidths(). https://github.com/vim/vim/commit/08aac3c6192f0103cb87e280270a32b50e653be1 Co-Authored-By: delphinus --- src/nvim/eval.lua | 1 + src/nvim/generators/gen_unicode_tables.lua | 6 +- src/nvim/mbyte.c | 166 ++++++++++++++++++++++++++++- src/nvim/mbyte.h | 1 + src/nvim/testdir/test_utf8.vim | 35 ++++++ 5 files changed, 205 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 6d8776d08b..a2272f0c98 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -327,6 +327,7 @@ return { serverstop={args=1}, setbufline={args=3, base=3}, setbufvar={args=3, base=3}, + setcellwidths={args=1, base=1}, setcharpos={args=2, base=2}, setcharsearch={args=1, base=1}, setcmdpos={args=1, base=1}, diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua index aa96c97bc1..36553f4649 100644 --- a/src/nvim/generators/gen_unicode_tables.lua +++ b/src/nvim/generators/gen_unicode_tables.lua @@ -12,8 +12,8 @@ -- 2 then interval applies only to first, third, fifth, … character in range. -- Fourth value is number that should be added to the codepoint to yield -- folded/lower/upper codepoint. --- 4. emoji_width and emoji_all tables: sorted lists of non-overlapping closed --- intervals of Emoji characters. emoji_width contains all the characters +-- 4. emoji_wide and emoji_all tables: sorted lists of non-overlapping closed +-- intervals of Emoji characters. emoji_wide contains all the characters -- which don't have ambiguous or double width, and emoji_all has all Emojis. if arg[1] == '--help' then print('Usage:') @@ -288,7 +288,7 @@ local build_emoji_table = function(ut_fp, emojiprops, doublewidth, ambiwidth) end ut_fp:write('};\n') - ut_fp:write('static const struct interval emoji_width[] = {\n') + ut_fp:write('static const struct interval emoji_wide[] = {\n') for _, p in ipairs(emojiwidth) do ut_fp:write(make_range(p[1], p[2])) end diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 223b4d6845..66262ebfad 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -74,6 +74,19 @@ struct interval { # include "unicode_tables.generated.h" #endif +static char e_list_item_nr_is_not_list[] + = N_("E1109: List item %d is not a List"); +static char e_list_item_nr_does_not_contain_3_numbers[] + = N_("E1110: List item %d does not contain 3 numbers"); +static char e_list_item_nr_range_invalid[] + = N_("E1111: List item %d range invalid"); +static char e_list_item_nr_cell_width_invalid[] + = N_("E1112: List item %d cell width invalid"); +static char e_overlapping_ranges_for_nr[] + = N_("E1113: Overlapping ranges for %lx"); +static char e_only_values_of_0x100_and_higher_supported[] + = N_("E1114: Only values of 0x100 and higher supported"); + // 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 @@ -472,13 +485,18 @@ static bool intable(const struct interval *table, size_t n_items, int c) int utf_char2cells(int c) { if (c >= 0x100) { + int n = cw_value(c); + if (n != 0) { + return n; + } + if (!utf_printable(c)) { return 6; // unprintable, displays } if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) { return 2; } - if (p_emoji && intable(emoji_width, ARRAY_SIZE(emoji_width), c)) { + if (p_emoji && intable(emoji_wide, ARRAY_SIZE(emoji_wide), c)) { return 2; } } else if (c >= 0x80 && !vim_isprintc(c)) { @@ -2678,3 +2696,149 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp return retval; } + +/// Table set by setcellwidths(). +typedef struct { + long first; + long last; + char width; +} cw_interval_T; + +static cw_interval_T *cw_table = NULL; +static size_t cw_table_size = 0; + +/// Return the value of the cellwidth table for the character `c`. +/// +/// @param c The source character. +/// @return 1 or 2 when `c` is in the cellwidth table, 0 if not. +static int cw_value(int c) +{ + if (cw_table == NULL) { + return 0; + } + + // first quick check for Latin1 etc. characters + if (c < cw_table[0].first) { + return 0; + } + + // binary search in table + int bot = 0; + int top = (int)cw_table_size - 1; + while (top >= bot) { + int mid = (bot + top) / 2; + if (cw_table[mid].last < c) { + bot = mid + 1; + } else if (cw_table[mid].first > c) { + top = mid - 1; + } else { + return cw_table[mid].width; + } + } + return 0; +} + +static int tv_nr_compare(const void *a1, const void *a2) +{ + const listitem_T *const li1 = (const listitem_T *)a1; + const listitem_T *const li2 = (const listitem_T *)a2; + + return (int)(TV_LIST_ITEM_TV(li1)->vval.v_number - TV_LIST_ITEM_TV(li2)->vval.v_number); +} + +/// "setcellwidths()" function +void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { + emsg(_(e_listreq)); + return; + } + const list_T *const l = argvars[0].vval.v_list; + if (tv_list_len(l) == 0) { + // Clearing the table. + xfree(cw_table); + cw_table = NULL; + cw_table_size = 0; + return; + } + + const listitem_T **ptrs = xmalloc(sizeof(const listitem_T *) * (size_t)tv_list_len(l)); + + // Check that all entries are a list with three numbers, the range is + // valid and the cell width is valid. + int item = 0; + TV_LIST_ITER_CONST(l, li, { + const typval_T *const li_tv = TV_LIST_ITEM_TV(li); + + if (li_tv->v_type != VAR_LIST || li_tv->vval.v_list == NULL) { + semsg(_(e_list_item_nr_is_not_list), item); + xfree(ptrs); + return; + } + + const list_T *const li_l = li_tv->vval.v_list; + const listitem_T *lili = tv_list_first(li_l); + int i = 0; + varnumber_T n1; + for (; lili != NULL; lili = TV_LIST_ITEM_NEXT(li_l, lili), i++) { + const typval_T *const lili_tv = TV_LIST_ITEM_TV(lili); + if (lili_tv->v_type != VAR_NUMBER) { + break; + } + if (i == 0) { + n1 = lili_tv->vval.v_number; + if (n1 < 0x100) { + emsg(_(e_only_values_of_0x100_and_higher_supported)); + xfree(ptrs); + return; + } + } else if (i == 1 && lili_tv->vval.v_number < n1) { + semsg(_(e_list_item_nr_range_invalid), item); + xfree(ptrs); + return; + } else if (i == 2 && (lili_tv->vval.v_number < 1 || lili_tv->vval.v_number > 2)) { + semsg(_(e_list_item_nr_cell_width_invalid), item); + xfree(ptrs); + return; + } + } + + if (i != 3) { + semsg(_(e_list_item_nr_does_not_contain_3_numbers), item); + xfree(ptrs); + return; + } + + ptrs[item++] = lili; + }); + + // Sort the list on the first number. + qsort((void *)ptrs, (size_t)tv_list_len(l), sizeof(const listitem_T *), tv_nr_compare); + + cw_interval_T *table = xmalloc(sizeof(cw_interval_T) * (size_t)tv_list_len(l)); + + // Store the items in the new table. + item = 0; + TV_LIST_ITER_CONST(l, li, { + const list_T *const li_l = TV_LIST_ITEM_TV(li)->vval.v_list; + const listitem_T *lili = tv_list_first(li_l); + const varnumber_T n1 = TV_LIST_ITEM_TV(lili)->vval.v_number; + if (item > 0 && n1 <= table[item - 1].last) { + semsg(_(e_overlapping_ranges_for_nr), (long)n1); + xfree(ptrs); + xfree(table); + return; + } + table[item].first = n1; + lili = TV_LIST_ITEM_NEXT(li_l, lili); + table[item].last = TV_LIST_ITEM_TV(lili)->vval.v_number; + lili = TV_LIST_ITEM_NEXT(li_l, lili); + table[item].width = (char)TV_LIST_ITEM_TV(lili)->vval.v_number; + item++; + }); + + xfree(ptrs); + xfree(cw_table); + cw_table = table; + cw_table_size = (size_t)tv_list_len(l); +} diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index ffa8411675..2a9afcbd03 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -5,6 +5,7 @@ #include #include +#include "nvim/eval/typval.h" #include "nvim/func_attr.h" #include "nvim/mbyte_defs.h" #include "nvim/os/os_defs.h" // For indirect diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 9b010a5dbc..c5dfd85e5e 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -140,6 +140,41 @@ func Test_list2str_str2list_latin1() call assert_equal(s, sres) endfunc +func Test_setcellwidths() + call setcellwidths([ + \ [0x1330, 0x1330, 2], + \ [0x1337, 0x1339, 2], + \ [9999, 10000, 1], + \]) + + call assert_equal(2, strwidth("\u1330")) + call assert_equal(1, strwidth("\u1336")) + call assert_equal(2, strwidth("\u1337")) + call assert_equal(2, strwidth("\u1339")) + call assert_equal(1, strwidth("\u133a")) + + call setcellwidths([]) + + call assert_fails('call setcellwidths(1)', 'E714:') + + call assert_fails('call setcellwidths([1, 2, 0])', 'E1109:') + + call assert_fails('call setcellwidths([[0x101]])', 'E1110:') + call assert_fails('call setcellwidths([[0x101, 0x102]])', 'E1110:') + call assert_fails('call setcellwidths([[0x101, 0x102, 1, 4]])', 'E1110:') + call assert_fails('call setcellwidths([["a"]])', 'E1110:') + + call assert_fails('call setcellwidths([[0x102, 0x101, 1]])', 'E1111:') + + call assert_fails('call setcellwidths([[0x101, 0x102, 0]])', 'E1112:') + call assert_fails('call setcellwidths([[0x101, 0x102, 3]])', 'E1112:') + + call assert_fails('call setcellwidths([[0x111, 0x122, 1], [0x115, 0x116, 2]])', 'E1113:') + call assert_fails('call setcellwidths([[0x111, 0x122, 1], [0x122, 0x123, 2]])', 'E1113:') + + call assert_fails('call setcellwidths([[0x33, 0x44, 2]])', 'E1114:') +endfunc + func Test_print_overlong() " Text with more composing characters than MB_MAXBYTES. new -- cgit From 967415d5277df25ee96d961ec0ab95ddfe01a611 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 12:01:57 +0800 Subject: vim-patch:8.2.1537: memory acccess error when using setcellwidths() Problem: Memory acccess error when using setcellwidths(). Solution: Use array and pointers correctly. https://github.com/vim/vim/commit/b06a6d59d12dbd67d55b3c46f6e5547e9103c931 --- src/nvim/mbyte.c | 26 ++++++++++++-------------- src/nvim/testdir/test_utf8.vim | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 66262ebfad..e5658fa15d 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -83,7 +83,7 @@ static char e_list_item_nr_range_invalid[] static char e_list_item_nr_cell_width_invalid[] = N_("E1112: List item %d cell width invalid"); static char e_overlapping_ranges_for_nr[] - = N_("E1113: Overlapping ranges for %lx"); + = N_("E1113: Overlapping ranges for 0x%lx"); static char e_only_values_of_0x100_and_higher_supported[] = N_("E1114: Only values of 0x100 and higher supported"); @@ -2740,8 +2740,8 @@ static int cw_value(int c) static int tv_nr_compare(const void *a1, const void *a2) { - const listitem_T *const li1 = (const listitem_T *)a1; - const listitem_T *const li2 = (const listitem_T *)a2; + const listitem_T *const li1 = *(const listitem_T **)a1; + const listitem_T *const li2 = *(const listitem_T **)a2; return (int)(TV_LIST_ITEM_TV(li1)->vval.v_number - TV_LIST_ITEM_TV(li2)->vval.v_number); } @@ -2778,9 +2778,10 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) const list_T *const li_l = li_tv->vval.v_list; const listitem_T *lili = tv_list_first(li_l); - int i = 0; + ptrs[item] = lili; + int i; varnumber_T n1; - for (; lili != NULL; lili = TV_LIST_ITEM_NEXT(li_l, lili), i++) { + for (i = 0; lili != NULL; lili = TV_LIST_ITEM_NEXT(li_l, lili), i++) { const typval_T *const lili_tv = TV_LIST_ITEM_TV(lili); if (lili_tv->v_type != VAR_NUMBER) { break; @@ -2809,7 +2810,7 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ptrs[item++] = lili; + item++; }); // Sort the list on the first number. @@ -2818,10 +2819,8 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) cw_interval_T *table = xmalloc(sizeof(cw_interval_T) * (size_t)tv_list_len(l)); // Store the items in the new table. - item = 0; - TV_LIST_ITER_CONST(l, li, { - const list_T *const li_l = TV_LIST_ITEM_TV(li)->vval.v_list; - const listitem_T *lili = tv_list_first(li_l); + for (item = 0; item < tv_list_len(l); item++) { + const listitem_T *lili = ptrs[item]; const varnumber_T n1 = TV_LIST_ITEM_TV(lili)->vval.v_number; if (item > 0 && n1 <= table[item - 1].last) { semsg(_(e_overlapping_ranges_for_nr), (long)n1); @@ -2830,12 +2829,11 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } table[item].first = n1; - lili = TV_LIST_ITEM_NEXT(li_l, lili); + lili = TV_LIST_ITEM_NEXT(, lili); // TODO: get list which lili belongs to table[item].last = TV_LIST_ITEM_TV(lili)->vval.v_number; - lili = TV_LIST_ITEM_NEXT(li_l, lili); + lili = TV_LIST_ITEM_NEXT(, lili); // TODO: get list which lili belongs to table[item].width = (char)TV_LIST_ITEM_TV(lili)->vval.v_number; - item++; - }); + } xfree(ptrs); xfree(cw_table); diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index c5dfd85e5e..882e9623f6 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -143,8 +143,8 @@ endfunc func Test_setcellwidths() call setcellwidths([ \ [0x1330, 0x1330, 2], - \ [0x1337, 0x1339, 2], \ [9999, 10000, 1], + \ [0x1337, 0x1339, 2], \]) call assert_equal(2, strwidth("\u1330")) -- cgit From 01a7009af9f7bbf5f1b38c82956caf67a7c8bcca Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 14:31:36 +0800 Subject: refactor(setcellwidths): use TV_LIST_ITEM_NEXT properly --- src/nvim/mbyte.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index e5658fa15d..e19c356343 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2740,8 +2740,8 @@ static int cw_value(int c) static int tv_nr_compare(const void *a1, const void *a2) { - const listitem_T *const li1 = *(const listitem_T **)a1; - const listitem_T *const li2 = *(const listitem_T **)a2; + const listitem_T *const li1 = tv_list_first(*(const list_T **)a1); + const listitem_T *const li2 = tv_list_first(*(const list_T **)a2); return (int)(TV_LIST_ITEM_TV(li1)->vval.v_number - TV_LIST_ITEM_TV(li2)->vval.v_number); } @@ -2762,7 +2762,8 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - const listitem_T **ptrs = xmalloc(sizeof(const listitem_T *) * (size_t)tv_list_len(l)); + // Note: use list_T instead of listitem_T so that TV_LIST_ITEM_NEXT can be used properly below. + const list_T **ptrs = xmalloc(sizeof(const list_T *) * (size_t)tv_list_len(l)); // Check that all entries are a list with three numbers, the range is // valid and the cell width is valid. @@ -2777,8 +2778,8 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) } const list_T *const li_l = li_tv->vval.v_list; + ptrs[item] = li_l; const listitem_T *lili = tv_list_first(li_l); - ptrs[item] = lili; int i; varnumber_T n1; for (i = 0; lili != NULL; lili = TV_LIST_ITEM_NEXT(li_l, lili), i++) { @@ -2814,13 +2815,14 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) }); // Sort the list on the first number. - qsort((void *)ptrs, (size_t)tv_list_len(l), sizeof(const listitem_T *), tv_nr_compare); + qsort((void *)ptrs, (size_t)tv_list_len(l), sizeof(const list_T *), tv_nr_compare); cw_interval_T *table = xmalloc(sizeof(cw_interval_T) * (size_t)tv_list_len(l)); // Store the items in the new table. for (item = 0; item < tv_list_len(l); item++) { - const listitem_T *lili = ptrs[item]; + const list_T *const li_l = ptrs[item]; + const listitem_T *lili = tv_list_first(li_l); const varnumber_T n1 = TV_LIST_ITEM_TV(lili)->vval.v_number; if (item > 0 && n1 <= table[item - 1].last) { semsg(_(e_overlapping_ranges_for_nr), (long)n1); @@ -2829,9 +2831,9 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } table[item].first = n1; - lili = TV_LIST_ITEM_NEXT(, lili); // TODO: get list which lili belongs to + lili = TV_LIST_ITEM_NEXT(li_l, lili); table[item].last = TV_LIST_ITEM_TV(lili)->vval.v_number; - lili = TV_LIST_ITEM_NEXT(, lili); // TODO: get list which lili belongs to + lili = TV_LIST_ITEM_NEXT(li_l, lili); table[item].width = (char)TV_LIST_ITEM_TV(lili)->vval.v_number; } -- cgit From 9fedb6fd783b9ac48239bc7574779118eec3729a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 12:08:28 +0800 Subject: vim-patch:8.2.3545: setcellwidths() may make 'listchars' or 'fillchars' invalid Problem: setcellwidths() may make 'listchars' or 'fillchars' invalid. Solution: Check the value and give an error. (closes vim/vim#9024) https://github.com/vim/vim/commit/94358a1e6e640ca5ebeb295efdddd4e92b700673 Cherry-pick f_setcellwidths() change from patch 9.0.0036. Cherry-pick 'ambiwidth' docs update from runtime update 079ba76ae7a7. --- src/nvim/globals.h | 5 +++++ src/nvim/mbyte.c | 33 ++++++++++++++++++++++++++++++++- src/nvim/option.c | 6 +++--- src/nvim/testdir/test_utf8.vim | 10 ++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 317423ffa0..23870a572e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1000,6 +1000,11 @@ EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); +EXTERN char e_conflicts_with_value_of_listchars[] +INIT(= N_("E834: Conflicts with value of 'listchars'")); +EXTERN char e_conflicts_with_value_of_fillchars[] +INIT(= N_("E835: Conflicts with value of 'fillchars'")); + EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s")); EXTERN char e_cmdmap_err[] INIT(= N_("E5520: mapping must end with ")); EXTERN char e_cmdmap_repeated[] diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index e19c356343..5de7231a0a 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2838,7 +2838,38 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) } xfree(ptrs); - xfree(cw_table); + + cw_interval_T *const cw_table_save = cw_table; + const size_t cw_table_size_save = cw_table_size; cw_table = table; cw_table_size = (size_t)tv_list_len(l); + + // Check that the new value does not conflict with 'fillchars' or + // 'listchars'. + char *error = NULL; + if (set_chars_option(curwin, &p_fcs, false) != NULL) { + error = e_conflicts_with_value_of_fillchars; + } else if (set_chars_option(curwin, &p_lcs, false) != NULL) { + error = e_conflicts_with_value_of_listchars; + } else { + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_chars_option(wp, &wp->w_p_lcs, false) != NULL) { + error = e_conflicts_with_value_of_listchars; + break; + } + if (set_chars_option(wp, &wp->w_p_fcs, false) != NULL) { + error = e_conflicts_with_value_of_fillchars; + break; + } + } + } + if (error != NULL) { + emsg(_(error)); + cw_table = cw_table_save; + cw_table_size = cw_table_size_save; + xfree(table); + return; + } + + xfree(cw_table_save); } diff --git a/src/nvim/option.c b/src/nvim/option.c index 12c5889703..3ab1a32eeb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2580,11 +2580,11 @@ static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, c } else { FOR_ALL_TAB_WINDOWS(tp, wp) { if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { - errmsg = _("E834: Conflicts with value of 'listchars'"); + errmsg = _(e_conflicts_with_value_of_listchars); goto ambw_end; } if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { - errmsg = _("E835: Conflicts with value of 'fillchars'"); + errmsg = _(e_conflicts_with_value_of_fillchars); goto ambw_end; } } @@ -3605,7 +3605,7 @@ static int get_encoded_char_adv(char_u **p) /// /// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs /// @return error message, NULL if it's OK. -static char *set_chars_option(win_T *wp, char_u **varp, bool set) +char *set_chars_option(win_T *wp, char_u **varp, bool set) { int round, i, len, len2, entries; char_u *p, *s; diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 882e9623f6..ab3503c282 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -173,6 +173,16 @@ func Test_setcellwidths() call assert_fails('call setcellwidths([[0x111, 0x122, 1], [0x122, 0x123, 2]])', 'E1113:') call assert_fails('call setcellwidths([[0x33, 0x44, 2]])', 'E1114:') + + set listchars=tab:--\\u2192 + call assert_fails('call setcellwidths([[0x2192, 0x2192, 2]])', 'E834:') + + set fillchars=stl:\\u2501 + call assert_fails('call setcellwidths([[0x2501, 0x2501, 2]])', 'E835:') + + set listchars& + set fillchars& + call setcellwidths([]) endfunc func Test_print_overlong() -- cgit From 603f7bd253e6dd3693e2957f0beb3223945fe705 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 Aug 2022 14:59:07 +0800 Subject: fix(fillchars): change fallback after setcellwidths() --- src/nvim/mbyte.c | 5 +++-- src/nvim/option.c | 32 ++++++++++---------------------- 2 files changed, 13 insertions(+), 24 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 5de7231a0a..e156fa58d1 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2853,11 +2853,11 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) error = e_conflicts_with_value_of_listchars; } else { FOR_ALL_TAB_WINDOWS(tp, wp) { - if (set_chars_option(wp, &wp->w_p_lcs, false) != NULL) { + if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { error = e_conflicts_with_value_of_listchars; break; } - if (set_chars_option(wp, &wp->w_p_fcs, false) != NULL) { + if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { error = e_conflicts_with_value_of_fillchars; break; } @@ -2872,4 +2872,5 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) } xfree(cw_table_save); + redraw_all_later(NOT_VALID); } diff --git a/src/nvim/option.c b/src/nvim/option.c index 3ab1a32eeb..d7443bc593 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3624,21 +3624,22 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set) }; struct chars_tab *tab; + // XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case. struct chars_tab fcs_tab[] = { { &wp->w_p_fcs_chars.stl, "stl", ' ' }, { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, { &wp->w_p_fcs_chars.wbr, "wbr", ' ' }, - { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─ - { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴ - { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬ - { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ - { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤ - { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├ - { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼ - { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · + { &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─ + { &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // ┴ + { &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬ + { &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │ + { &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤ + { &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├ + { &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼ + { &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // · { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, - { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ + { &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │ { &wp->w_p_fcs_chars.diff, "diff", '-' }, { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, { &wp->w_p_fcs_chars.eob, "eob", '~' }, @@ -3667,19 +3668,6 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set) if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) { varp = &p_fcs; } - if (*p_ambw == 'd') { - // XXX: If ambiwidth=double then some characters take 2 columns, - // which is forbidden (TUI limitation?). Set old defaults. - fcs_tab[3].def = '-'; - fcs_tab[4].def = '-'; - fcs_tab[5].def = '-'; - fcs_tab[6].def = '|'; - fcs_tab[7].def = '|'; - fcs_tab[8].def = '|'; - fcs_tab[9].def = '+'; - fcs_tab[10].def = '-'; - fcs_tab[13].def = '|'; - } } // first round: check for valid value, second round: assign values -- cgit From cd14efd281c260c92936f05bf5f91780f8912f81 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Aug 2022 17:13:44 +0800 Subject: vim-patch:8.1.1823: command line history code is spread out (#19688) Problem: Command line history code is spread out. Solution: Put the code in a new file. (Yegappan Lakshmanan, closes vim/vim#4779) Also graduate the +cmdline_hist feature. https://github.com/vim/vim/commit/d7663c22c6c1ff0f86b81371586fbc851d3a3e9e --- src/nvim/cmdhist.c | 743 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/cmdhist.h | 33 +++ src/nvim/eval.c | 1 + src/nvim/eval/funcs.c | 82 +----- src/nvim/ex_cmds.c | 2 + src/nvim/ex_docmd.c | 4 +- src/nvim/ex_getln.c | 709 ++--------------------------------------------- src/nvim/ex_getln.h | 24 -- src/nvim/memory.c | 1 + src/nvim/normal.c | 3 + src/nvim/search.c | 1 + src/nvim/shada.c | 24 +- 12 files changed, 835 insertions(+), 792 deletions(-) create mode 100644 src/nvim/cmdhist.c create mode 100644 src/nvim/cmdhist.h (limited to 'src/nvim') diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c new file mode 100644 index 0000000000..625bd514ad --- /dev/null +++ b/src/nvim/cmdhist.c @@ -0,0 +1,743 @@ +// 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 + +// cmdhist.c: Functions for the history of the command-line. + +#include "nvim/ascii.h" +#include "nvim/charset.h" +#include "nvim/cmdhist.h" +#include "nvim/ex_getln.h" +#include "nvim/regexp.h" +#include "nvim/strings.h" +#include "nvim/ui.h" +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "cmdhist.c.generated.h" +#endif + +static histentry_T *(history[HIST_COUNT]) = { NULL, NULL, NULL, NULL, NULL }; +static int hisidx[HIST_COUNT] = { -1, -1, -1, -1, -1 }; ///< lastused entry +/// identifying (unique) number of newest history entry +static int hisnum[HIST_COUNT] = { 0, 0, 0, 0, 0 }; +static int hislen = 0; ///< actual length of history tables + +/// Return the length of the history tables +int get_hislen(void) +{ + return hislen; +} + +/// Return a pointer to a specified history table +histentry_T *get_histentry(int hist_type) +{ + return history[hist_type]; +} + +void set_histentry(int hist_type, histentry_T *entry) +{ + history[hist_type] = entry; +} + +int *get_hisidx(int hist_type) +{ + return &hisidx[hist_type]; +} + +int *get_hisnum(int hist_type) +{ + return &hisnum[hist_type]; +} + +/// Translate a history character to the associated type number +HistoryType hist_char2type(const int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (c) { + case ':': + return HIST_CMD; + case '=': + return HIST_EXPR; + case '@': + return HIST_INPUT; + case '>': + return HIST_DEBUG; + case NUL: + case '/': + case '?': + return HIST_SEARCH; + default: + return HIST_INVALID; + } + // Silence -Wreturn-type + return 0; +} + +/// Table of history names. +/// These names are used in :history and various hist...() functions. +/// It is sufficient to give the significant prefix of a history name. +static char *(history_names[]) = { + "cmd", + "search", + "expr", + "input", + "debug", + NULL +}; + +/// Function given to ExpandGeneric() to obtain the possible first +/// arguments of the ":history command. +char *get_history_arg(expand_T *xp, int idx) +{ + static char_u compl[2] = { NUL, NUL }; + char *short_names = ":=@>?/"; + int short_names_count = (int)STRLEN(short_names); + int history_name_count = ARRAY_SIZE(history_names) - 1; + + if (idx < short_names_count) { + compl[0] = (char_u)short_names[idx]; + return (char *)compl; + } + if (idx < short_names_count + history_name_count) { + return history_names[idx - short_names_count]; + } + if (idx == short_names_count + history_name_count) { + return "all"; + } + return NULL; +} + +/// Initialize command line history. +/// Also used to re-allocate history tables when size changes. +void init_history(void) +{ + assert(p_hi >= 0 && p_hi <= INT_MAX); + int newlen = (int)p_hi; + int oldlen = hislen; + + // If history tables size changed, reallocate them. + // Tables are circular arrays (current position marked by hisidx[type]). + // On copying them to the new arrays, we take the chance to reorder them. + if (newlen != oldlen) { + for (int type = 0; type < HIST_COUNT; type++) { + histentry_T *temp = (newlen + ? xmalloc((size_t)newlen * sizeof(*temp)) + : NULL); + + int j = hisidx[type]; + if (j >= 0) { + // old array gets partitioned this way: + // [0 , i1 ) --> newest entries to be deleted + // [i1 , i1 + l1) --> newest entries to be copied + // [i1 + l1 , i2 ) --> oldest entries to be deleted + // [i2 , i2 + l2) --> oldest entries to be copied + int l1 = MIN(j + 1, newlen); // how many newest to copy + int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy + int i1 = j + 1 - l1; // copy newest from here + int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here + + // copy as much entries as they fit to new table, reordering them + if (newlen) { + // copy oldest entries + memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp)); + // copy newest entries + memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp)); + } + + // delete entries that don't fit in newlen, if any + for (int i = 0; i < i1; i++) { + hist_free_entry(history[type] + i); + } + for (int i = i1 + l1; i < i2; i++) { + hist_free_entry(history[type] + i); + } + } + + // clear remaining space, if any + int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries + if (newlen) { + memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp)); + } + + hisidx[type] = l3 - 1; + xfree(history[type]); + history[type] = temp; + } + hislen = newlen; + } +} + +static inline void hist_free_entry(histentry_T *hisptr) + FUNC_ATTR_NONNULL_ALL +{ + xfree(hisptr->hisstr); + tv_list_unref(hisptr->additional_elements); + clear_hist_entry(hisptr); +} + +static inline void clear_hist_entry(histentry_T *hisptr) + FUNC_ATTR_NONNULL_ALL +{ + memset(hisptr, 0, sizeof(*hisptr)); +} + +/// Check if command line 'str' is already in history. +/// If 'move_to_front' is true, matching entry is moved to end of history. +/// +/// @param move_to_front Move the entry to the front if it exists +static int in_history(int type, char_u *str, int move_to_front, int sep) +{ + int last_i = -1; + + if (hisidx[type] < 0) { + return false; + } + int i = hisidx[type]; + do { + if (history[type][i].hisstr == NULL) { + return false; + } + + // For search history, check that the separator character matches as + // well. + char_u *p = history[type][i].hisstr; + if (STRCMP(str, p) == 0 + && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) { + if (!move_to_front) { + return true; + } + last_i = i; + break; + } + if (--i < 0) { + i = hislen - 1; + } + } while (i != hisidx[type]); + + if (last_i >= 0) { + list_T *const list = history[type][i].additional_elements; + str = history[type][i].hisstr; + while (i != hisidx[type]) { + if (++i >= hislen) { + i = 0; + } + history[type][last_i] = history[type][i]; + last_i = i; + } + tv_list_unref(list); + history[type][i].hisnum = ++hisnum[type]; + history[type][i].hisstr = str; + history[type][i].timestamp = os_time(); + history[type][i].additional_elements = NULL; + return true; + } + return false; +} + +/// Convert history name to its HIST_ equivalent +/// +/// Names are taken from the table above. When `name` is empty returns currently +/// active history or HIST_DEFAULT, depending on `return_default` argument. +/// +/// @param[in] name Converted name. +/// @param[in] len Name length. +/// @param[in] return_default Determines whether HIST_DEFAULT should be +/// returned or value based on `ccline.cmdfirstc`. +/// +/// @return Any value from HistoryType enum, including HIST_INVALID. May not +/// return HIST_DEFAULT unless return_default is true. +static HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + // No argument: use current history. + if (len == 0) { + return return_default ? HIST_DEFAULT : hist_char2type(get_cmdline_firstc()); + } + + for (HistoryType i = 0; history_names[i] != NULL; i++) { + if (STRNICMP(name, history_names[i], len) == 0) { + return i; + } + } + + if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) { + return hist_char2type(name[0]); + } + + return HIST_INVALID; +} + +static int last_maptick = -1; // last seen maptick + +/// Add the given string to the given history. If the string is already in the +/// history then it is moved to the front. +/// +/// @param histype may be one of the HIST_ values. +/// @param in_map consider maptick when inside a mapping +/// @param sep separator character used (search hist) +void add_to_history(int histype, char_u *new_entry, int in_map, int sep) +{ + histentry_T *hisptr; + + if (hislen == 0 || histype == HIST_INVALID) { // no history + return; + } + assert(histype != HIST_DEFAULT); + + if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) { + return; + } + + // Searches inside the same mapping overwrite each other, so that only + // the last line is kept. Be careful not to remove a line that was moved + // down, only lines that were added. + if (histype == HIST_SEARCH && in_map) { + if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) { + // Current line is from the same mapping, remove it + hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; + hist_free_entry(hisptr); + hisnum[histype]--; + if (--hisidx[HIST_SEARCH] < 0) { + hisidx[HIST_SEARCH] = hislen - 1; + } + } + last_maptick = -1; + } + if (!in_history(histype, new_entry, true, sep)) { + if (++hisidx[histype] == hislen) { + hisidx[histype] = 0; + } + hisptr = &history[histype][hisidx[histype]]; + hist_free_entry(hisptr); + + // Store the separator after the NUL of the string. + size_t len = STRLEN(new_entry); + hisptr->hisstr = vim_strnsave(new_entry, len + 2); + hisptr->timestamp = os_time(); + hisptr->additional_elements = NULL; + hisptr->hisstr[len + 1] = (char_u)sep; + + hisptr->hisnum = ++hisnum[histype]; + if (histype == HIST_SEARCH && in_map) { + last_maptick = maptick; + } + } +} + +/// Get identifier of newest history entry. +/// +/// @param histype may be one of the HIST_ values. +static int get_history_idx(int histype) +{ + if (hislen == 0 || histype < 0 || histype >= HIST_COUNT + || hisidx[histype] < 0) { + return -1; + } + + return history[histype][hisidx[histype]].hisnum; +} + +/// Calculate history index from a number: +/// +/// @param num > 0: seen as identifying number of a history entry +/// < 0: relative position in history wrt newest entry +/// @param histype may be one of the HIST_ values. +static int calc_hist_idx(int histype, int num) +{ + int i; + int wrapped = false; + + if (hislen == 0 || histype < 0 || histype >= HIST_COUNT + || (i = hisidx[histype]) < 0 || num == 0) { + return -1; + } + + histentry_T *hist = history[histype]; + if (num > 0) { + while (hist[i].hisnum > num) { + if (--i < 0) { + if (wrapped) { + break; + } + i += hislen; + wrapped = true; + } + } + if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) { + return i; + } + } else if (-num <= hislen) { + i += num + 1; + if (i < 0) { + i += hislen; + } + if (hist[i].hisstr != NULL) { + return i; + } + } + return -1; +} + +/// Get a history entry by its index. +/// +/// @param histype may be one of the HIST_ values. +static char_u *get_history_entry(int histype, int idx) +{ + idx = calc_hist_idx(histype, idx); + if (idx >= 0) { + return history[histype][idx].hisstr; + } else { + return (char_u *)""; + } +} + +/// Clear all entries in a history +/// +/// @param[in] histype One of the HIST_ values. +/// +/// @return OK if there was something to clean and histype was one of HIST_ +/// values, FAIL otherwise. +int clr_history(const int histype) +{ + if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) { + histentry_T *hisptr = history[histype]; + for (int i = hislen; i--; hisptr++) { + hist_free_entry(hisptr); + } + hisidx[histype] = -1; // mark history as cleared + hisnum[histype] = 0; // reset identifier counter + return OK; + } + return FAIL; +} + +/// Remove all entries matching {str} from a history. +/// +/// @param histype may be one of the HIST_ values. +static int del_history_entry(int histype, char_u *str) +{ + regmatch_T regmatch; + histentry_T *hisptr; + int idx; + int i; + int last; + bool found = false; + + regmatch.regprog = NULL; + regmatch.rm_ic = false; // always match case + if (hislen != 0 + && histype >= 0 + && histype < HIST_COUNT + && *str != NUL + && (idx = hisidx[histype]) >= 0 + && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING)) != NULL) { + i = last = idx; + do { + hisptr = &history[histype][i]; + if (hisptr->hisstr == NULL) { + break; + } + if (vim_regexec(®match, (char *)hisptr->hisstr, (colnr_T)0)) { + found = true; + hist_free_entry(hisptr); + } else { + if (i != last) { + history[histype][last] = *hisptr; + clear_hist_entry(hisptr); + } + if (--last < 0) { + last += hislen; + } + } + if (--i < 0) { + i += hislen; + } + } while (i != idx); + if (history[histype][idx].hisstr == NULL) { + hisidx[histype] = -1; + } + } + vim_regfree(regmatch.regprog); + return found; +} + +/// Remove an indexed entry from a history. +/// +/// @param histype may be one of the HIST_ values. +static int del_history_idx(int histype, int idx) +{ + int i = calc_hist_idx(histype, idx); + if (i < 0) { + return false; + } + idx = hisidx[histype]; + hist_free_entry(&history[histype][i]); + + // When deleting the last added search string in a mapping, reset + // last_maptick, so that the last added search string isn't deleted again. + if (histype == HIST_SEARCH && maptick == last_maptick && i == idx) { + last_maptick = -1; + } + + while (i != idx) { + int j = (i + 1) % hislen; + history[histype][i] = history[histype][j]; + i = j; + } + clear_hist_entry(&history[histype][idx]); + if (--i < 0) { + i += hislen; + } + hisidx[histype] = i; + return true; +} + +/// "histadd()" function +void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + HistoryType histype; + + rettv->vval.v_number = false; + if (check_secure()) { + return; + } + const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID; + if (histype != HIST_INVALID) { + char buf[NUMBUFLEN]; + str = tv_get_string_buf(&argvars[1], buf); + if (*str != NUL) { + init_history(); + add_to_history(histype, (char_u *)str, false, NUL); + rettv->vval.v_number = true; + return; + } + } +} + +/// "histdel()" function +void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int n; + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { + n = 0; + } else if (argvars[1].v_type == VAR_UNKNOWN) { + // only one argument: clear entire history + n = clr_history(get_histtype(str, strlen(str), false)); + } else if (argvars[1].v_type == VAR_NUMBER) { + // index given: remove that entry + n = del_history_idx(get_histtype(str, strlen(str), false), + (int)tv_get_number(&argvars[1])); + } else { + // string given: remove all matching entries + char buf[NUMBUFLEN]; + n = del_history_entry(get_histtype(str, strlen(str), false), + (char_u *)tv_get_string_buf(&argvars[1], buf)); + } + rettv->vval.v_number = n; +} + +/// "histget()" function +void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + HistoryType type; + int idx; + + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error + if (str == NULL) { + rettv->vval.v_string = NULL; + } else { + type = get_histtype(str, strlen(str), false); + if (argvars[1].v_type == VAR_UNKNOWN) { + idx = get_history_idx(type); + } else { + idx = (int)tv_get_number_chk(&argvars[1], NULL); + } + // -1 on type error + rettv->vval.v_string = (char *)vim_strsave(get_history_entry(type, idx)); + } + rettv->v_type = VAR_STRING; +} + +/// "histnr()" function +void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const histname = tv_get_string_chk(&argvars[0]); + HistoryType i = histname == NULL + ? HIST_INVALID + : get_histtype(histname, strlen(histname), false); + if (i != HIST_INVALID) { + i = get_history_idx(i); + } + rettv->vval.v_number = i; +} + +/// :history command - print a history +void ex_history(exarg_T *eap) +{ + histentry_T *hist; + int histype1 = HIST_CMD; + int histype2 = HIST_CMD; + int hisidx1 = 1; + int hisidx2 = -1; + int idx; + int i, j, k; + char_u *end; + char_u *arg = (char_u *)eap->arg; + + if (hislen == 0) { + msg(_("'history' option is zero")); + return; + } + + if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) { + end = arg; + while (ASCII_ISALPHA(*end) + || vim_strchr(":=@>/?", *end) != NULL) { + end++; + } + histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false); + if (histype1 == HIST_INVALID) { + if (STRNICMP(arg, "all", end - arg) == 0) { + histype1 = 0; + histype2 = HIST_COUNT - 1; + } else { + semsg(_(e_trailing_arg), arg); + return; + } + } else { + histype2 = histype1; + } + } else { + end = arg; + } + if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { + semsg(_(e_trailing_arg), end); + return; + } + + for (; !got_int && histype1 <= histype2; histype1++) { + STRCPY(IObuff, "\n # "); + assert(history_names[histype1] != NULL); + STRCAT(STRCAT(IObuff, history_names[histype1]), " history"); + msg_puts_title((char *)IObuff); + idx = hisidx[histype1]; + hist = history[histype1]; + j = hisidx1; + k = hisidx2; + if (j < 0) { + j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum; + } + if (k < 0) { + k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum; + } + if (idx >= 0 && j <= k) { + for (i = idx + 1; !got_int; i++) { + if (i == hislen) { + i = 0; + } + if (hist[i].hisstr != NULL + && hist[i].hisnum >= j && hist[i].hisnum <= k) { + msg_putchar('\n'); + snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ', + hist[i].hisnum); + if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) { + trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff), + Columns - 10, IOSIZE - (int)STRLEN(IObuff)); + } else { + STRCAT(IObuff, hist[i].hisstr); + } + msg_outtrans((char *)IObuff); + ui_flush(); + } + if (i == idx) { + break; + } + } + } + } +} + +/// Iterate over history items +/// +/// @warning No history-editing functions must be run while iteration is in +/// progress. +/// +/// @param[in] iter Pointer to the last history entry. +/// @param[in] history_type Type of the history (HIST_*). Ignored if iter +/// parameter is not NULL. +/// @param[in] zero If true then zero (but not free) returned items. +/// +/// @warning When using this parameter user is +/// responsible for calling clr_history() +/// itself after iteration is over. If +/// clr_history() is not called behaviour is +/// undefined. No functions that work with +/// history must be called during iteration +/// in this case. +/// @param[out] hist Next history entry. +/// +/// @return Pointer used in next iteration or NULL to indicate that iteration +/// was finished. +const void *hist_iter(const void *const iter, const uint8_t history_type, const bool zero, + histentry_T *const hist) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4) +{ + *hist = (histentry_T) { + .hisstr = NULL + }; + if (hisidx[history_type] == -1) { + return NULL; + } + histentry_T *const hstart = &(history[history_type][0]); + histentry_T *const hlast = &(history[history_type][hisidx[history_type]]); + const histentry_T *const hend = &(history[history_type][hislen - 1]); + histentry_T *hiter; + if (iter == NULL) { + histentry_T *hfirst = hlast; + do { + hfirst++; + if (hfirst > hend) { + hfirst = hstart; + } + if (hfirst->hisstr != NULL) { + break; + } + } while (hfirst != hlast); + hiter = hfirst; + } else { + hiter = (histentry_T *)iter; + } + if (hiter == NULL) { + return NULL; + } + *hist = *hiter; + if (zero) { + memset(hiter, 0, sizeof(*hiter)); + } + if (hiter == hlast) { + return NULL; + } + hiter++; + return (const void *)((hiter > hend) ? hstart : hiter); +} + +/// Get array of history items +/// +/// @param[in] history_type Type of the history to get array for. +/// @param[out] new_hisidx Location where last index in the new array should +/// be saved. +/// @param[out] new_hisnum Location where last history number in the new +/// history should be saved. +/// +/// @return Pointer to the array or NULL. +histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx, + int **const new_hisnum) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + init_history(); + *new_hisidx = &(hisidx[history_type]); + *new_hisnum = &(hisnum[history_type]); + return history[history_type]; +} diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h new file mode 100644 index 0000000000..797b79a5f0 --- /dev/null +++ b/src/nvim/cmdhist.h @@ -0,0 +1,33 @@ +#ifndef NVIM_CMDHIST_H +#define NVIM_CMDHIST_H + +#include "nvim/eval/typval.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/os/time.h" + +/// Present history tables +typedef enum { + HIST_DEFAULT = -2, ///< Default (current) history. + HIST_INVALID = -1, ///< Unknown history. + HIST_CMD = 0, ///< Colon commands. + HIST_SEARCH, ///< Search commands. + HIST_EXPR, ///< Expressions (e.g. from entering = register). + HIST_INPUT, ///< input() lines. + HIST_DEBUG, ///< Debug commands. +} HistoryType; + +/// Number of history tables +#define HIST_COUNT (HIST_DEBUG + 1) + +/// History entry definition +typedef struct hist_entry { + int hisnum; ///< Entry identifier number. + char_u *hisstr; ///< Actual entry, separator char after the NUL. + Timestamp timestamp; ///< Time when entry was added. + list_T *additional_elements; ///< Additional entries from ShaDa file. +} histentry_T; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "cmdhist.h.generated.h" +#endif +#endif // NVIM_CMDHIST_H diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5911a93099..b0a207e5ba 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19,6 +19,7 @@ #include "nvim/change.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 060dc50f52..52386a67f6 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -13,6 +13,7 @@ #include "nvim/change.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/context.h" #include "nvim/cursor.h" #include "nvim/diff.h" @@ -4303,87 +4304,6 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "histadd()" function -static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - HistoryType histype; - - rettv->vval.v_number = false; - if (check_secure()) { - return; - } - const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error - histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID; - if (histype != HIST_INVALID) { - char buf[NUMBUFLEN]; - str = tv_get_string_buf(&argvars[1], buf); - if (*str != NUL) { - init_history(); - add_to_history(histype, (char_u *)str, false, NUL); - rettv->vval.v_number = true; - return; - } - } -} - -/// "histdel()" function -static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int n; - const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error - if (str == NULL) { - n = 0; - } else if (argvars[1].v_type == VAR_UNKNOWN) { - // only one argument: clear entire history - n = clr_history(get_histtype(str, strlen(str), false)); - } else if (argvars[1].v_type == VAR_NUMBER) { - // index given: remove that entry - n = del_history_idx(get_histtype(str, strlen(str), false), - (int)tv_get_number(&argvars[1])); - } else { - // string given: remove all matching entries - char buf[NUMBUFLEN]; - n = del_history_entry(get_histtype(str, strlen(str), false), - (char_u *)tv_get_string_buf(&argvars[1], buf)); - } - rettv->vval.v_number = n; -} - -/// "histget()" function -static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - HistoryType type; - int idx; - - const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error - if (str == NULL) { - rettv->vval.v_string = NULL; - } else { - type = get_histtype(str, strlen(str), false); - if (argvars[1].v_type == VAR_UNKNOWN) { - idx = get_history_idx(type); - } else { - idx = (int)tv_get_number_chk(&argvars[1], NULL); - } - // -1 on type error - rettv->vval.v_string = (char *)vim_strsave(get_history_entry(type, idx)); - } - rettv->v_type = VAR_STRING; -} - -/// "histnr()" function -static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const char *const history = tv_get_string_chk(&argvars[0]); - HistoryType i = history == NULL - ? HIST_INVALID - : get_histtype(history, strlen(history), false); - if (i != HIST_INVALID) { - i = get_history_idx(i); - } - rettv->vval.v_number = i; -} - /// "highlightID(name)" function static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9d1ac185d4..9ad65500e9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -20,6 +20,7 @@ #include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/diff.h" @@ -3343,6 +3344,7 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, char *sub, char *cmd, boo if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { save_re_pat(RE_SUBST, (char_u *)pat, p_magic); } + // put pattern in history add_to_history(HIST_SEARCH, (char_u *)pat, true, NUL); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4ac9847e53..86c5489cf2 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -13,6 +13,7 @@ #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/debugger.h" #include "nvim/diff.h" @@ -601,10 +602,9 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) if (next_cmdline == NULL) { XFREE_CLEAR(cmdline_copy); - // + // If the command was typed, remember it for the ':' register. // Do this AFTER executing the command to make :@: work. - // if (getline_equal(fgetline, cookie, getexline) && new_last_cmdline != NULL) { xfree(last_cmdline); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 07a0e68884..841345d625 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -19,6 +19,7 @@ #include "nvim/assert.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/digraph.h" @@ -214,12 +215,6 @@ static Array cmdline_block = ARRAY_DICT_INIT; */ typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *); -static histentry_T *(history[HIST_COUNT]) = { NULL, NULL, NULL, NULL, NULL }; -static int hisidx[HIST_COUNT] = { -1, -1, -1, -1, -1 }; // lastused entry -static int hisnum[HIST_COUNT] = { 0, 0, 0, 0, 0 }; -// identifying (unique) number of newest history entry -static int hislen = 0; // actual length of history tables - /// Flag for command_line_handle_key to ignore /// /// Used if it was received while processing highlight function in order for @@ -869,7 +864,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init may_trigger_modechanged(); init_history(); - s->hiscnt = hislen; // set hiscnt to impossible history value + s->hiscnt = get_hislen(); // set hiscnt to impossible history value s->histype = hist_char2type(s->firstc); do_digraph(-1); // init digraph typeahead @@ -1682,12 +1677,12 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match) for (;;) { // one step backwards if (!next_match) { - if (s->hiscnt == hislen) { + if (s->hiscnt == get_hislen()) { // first time - s->hiscnt = hisidx[s->histype]; - } else if (s->hiscnt == 0 && hisidx[s->histype] != hislen - 1) { - s->hiscnt = hislen - 1; - } else if (s->hiscnt != hisidx[s->histype] + 1) { + s->hiscnt = *get_hisidx(s->histype); + } else if (s->hiscnt == 0 && *get_hisidx(s->histype) != get_hislen() - 1) { + s->hiscnt = get_hislen() - 1; + } else if (s->hiscnt != *get_hisidx(s->histype) + 1) { s->hiscnt--; } else { // at top of list @@ -1696,17 +1691,17 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match) } } else { // one step forwards // on last entry, clear the line - if (s->hiscnt == hisidx[s->histype]) { - s->hiscnt = hislen; + if (s->hiscnt == *get_hisidx(s->histype)) { + s->hiscnt = get_hislen(); break; } // not on a history line, nothing to do - if (s->hiscnt == hislen) { + if (s->hiscnt == get_hislen()) { break; } - if (s->hiscnt == hislen - 1) { + if (s->hiscnt == get_hislen() - 1) { // wrap around s->hiscnt = 0; } else { @@ -1714,14 +1709,14 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match) } } - if (s->hiscnt < 0 || history[s->histype][s->hiscnt].hisstr == NULL) { + if (s->hiscnt < 0 || get_histentry(s->histype)[s->hiscnt].hisstr == NULL) { s->hiscnt = s->save_hiscnt; break; } if ((s->c != K_UP && s->c != K_DOWN) || s->hiscnt == s->save_hiscnt - || STRNCMP(history[s->histype][s->hiscnt].hisstr, + || STRNCMP(get_histentry(s->histype)[s->hiscnt].hisstr, s->lookfor, (size_t)j) == 0) { break; } @@ -2103,7 +2098,7 @@ static int command_line_handle_key(CommandLineState *s) case K_KPAGEUP: case K_PAGEDOWN: case K_KPAGEDOWN: - if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) { + if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) { // no history return command_line_not_changed(s); } @@ -2128,10 +2123,10 @@ static int command_line_handle_key(CommandLineState *s) XFREE_CLEAR(ccline.cmdbuff); s->xpc.xp_context = EXPAND_NOTHING; - if (s->hiscnt == hislen) { + if (s->hiscnt == get_hislen()) { p = s->lookfor; // back to the old one } else { - p = history[s->histype][s->hiscnt].hisstr; + p = get_histentry(s->histype)[s->hiscnt].hisstr; } if (s->histype == HIST_SEARCH @@ -5868,309 +5863,6 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options) xfree(buf); } -/********************************* -* Command line history stuff * -*********************************/ - -/// Translate a history character to the associated type number -static HistoryType hist_char2type(const int c) - FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT -{ - switch (c) { - case ':': - return HIST_CMD; - case '=': - return HIST_EXPR; - case '@': - return HIST_INPUT; - case '>': - return HIST_DEBUG; - case NUL: - case '/': - case '?': - return HIST_SEARCH; - default: - return HIST_INVALID; - } - // Silence -Wreturn-type - return 0; -} - -/* - * Table of history names. - * These names are used in :history and various hist...() functions. - * It is sufficient to give the significant prefix of a history name. - */ - -static char *(history_names[]) = -{ - "cmd", - "search", - "expr", - "input", - "debug", - NULL -}; - -/* - * Function given to ExpandGeneric() to obtain the possible first - * arguments of the ":history command. - */ -static char *get_history_arg(expand_T *xp, int idx) -{ - static char_u compl[2] = { NUL, NUL }; - char *short_names = ":=@>?/"; - int short_names_count = (int)STRLEN(short_names); - int history_name_count = ARRAY_SIZE(history_names) - 1; - - if (idx < short_names_count) { - compl[0] = (char_u)short_names[idx]; - return (char *)compl; - } - if (idx < short_names_count + history_name_count) { - return history_names[idx - short_names_count]; - } - if (idx == short_names_count + history_name_count) { - return "all"; - } - return NULL; -} - -/// Initialize command line history. -/// Also used to re-allocate history tables when size changes. -void init_history(void) -{ - assert(p_hi >= 0 && p_hi <= INT_MAX); - int newlen = (int)p_hi; - int oldlen = hislen; - - // If history tables size changed, reallocate them. - // Tables are circular arrays (current position marked by hisidx[type]). - // On copying them to the new arrays, we take the chance to reorder them. - if (newlen != oldlen) { - for (int type = 0; type < HIST_COUNT; type++) { - histentry_T *temp = (newlen - ? xmalloc((size_t)newlen * sizeof(*temp)) - : NULL); - - int j = hisidx[type]; - if (j >= 0) { - // old array gets partitioned this way: - // [0 , i1 ) --> newest entries to be deleted - // [i1 , i1 + l1) --> newest entries to be copied - // [i1 + l1 , i2 ) --> oldest entries to be deleted - // [i2 , i2 + l2) --> oldest entries to be copied - int l1 = MIN(j + 1, newlen); // how many newest to copy - int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy - int i1 = j + 1 - l1; // copy newest from here - int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here - - // copy as much entries as they fit to new table, reordering them - if (newlen) { - // copy oldest entries - memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp)); - // copy newest entries - memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp)); - } - - // delete entries that don't fit in newlen, if any - for (int i = 0; i < i1; i++) { - hist_free_entry(history[type] + i); - } - for (int i = i1 + l1; i < i2; i++) { - hist_free_entry(history[type] + i); - } - } - - // clear remaining space, if any - int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries - if (newlen) { - memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp)); - } - - hisidx[type] = l3 - 1; - xfree(history[type]); - history[type] = temp; - } - hislen = newlen; - } -} - -static inline void hist_free_entry(histentry_T *hisptr) - FUNC_ATTR_NONNULL_ALL -{ - xfree(hisptr->hisstr); - tv_list_unref(hisptr->additional_elements); - clear_hist_entry(hisptr); -} - -static inline void clear_hist_entry(histentry_T *hisptr) - FUNC_ATTR_NONNULL_ALL -{ - memset(hisptr, 0, sizeof(*hisptr)); -} - -/// Check if command line 'str' is already in history. -/// If 'move_to_front' is TRUE, matching entry is moved to end of history. -/// -/// @param move_to_front Move the entry to the front if it exists -static int in_history(int type, char_u *str, int move_to_front, int sep) -{ - int i; - int last_i = -1; - char_u *p; - - if (hisidx[type] < 0) { - return FALSE; - } - i = hisidx[type]; - do { - if (history[type][i].hisstr == NULL) { - return FALSE; - } - - /* For search history, check that the separator character matches as - * well. */ - p = history[type][i].hisstr; - if (STRCMP(str, p) == 0 - && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) { - if (!move_to_front) { - return TRUE; - } - last_i = i; - break; - } - if (--i < 0) { - i = hislen - 1; - } - } while (i != hisidx[type]); - - if (last_i >= 0) { - list_T *const list = history[type][i].additional_elements; - str = history[type][i].hisstr; - while (i != hisidx[type]) { - if (++i >= hislen) { - i = 0; - } - history[type][last_i] = history[type][i]; - last_i = i; - } - tv_list_unref(list); - history[type][i].hisnum = ++hisnum[type]; - history[type][i].hisstr = str; - history[type][i].timestamp = os_time(); - history[type][i].additional_elements = NULL; - return true; - } - return false; -} - -/// Convert history name to its HIST_ equivalent -/// -/// Names are taken from the table above. When `name` is empty returns currently -/// active history or HIST_DEFAULT, depending on `return_default` argument. -/// -/// @param[in] name Converted name. -/// @param[in] len Name length. -/// @param[in] return_default Determines whether HIST_DEFAULT should be -/// returned or value based on `ccline.cmdfirstc`. -/// -/// @return Any value from HistoryType enum, including HIST_INVALID. May not -/// return HIST_DEFAULT unless return_default is true. -HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - // No argument: use current history. - if (len == 0) { - return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc); - } - - for (HistoryType i = 0; history_names[i] != NULL; i++) { - if (STRNICMP(name, history_names[i], len) == 0) { - return i; - } - } - - if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) { - return hist_char2type(name[0]); - } - - return HIST_INVALID; -} - -static int last_maptick = -1; // last seen maptick - -/// Add the given string to the given history. If the string is already in the -/// history then it is moved to the front. "histype" may be one of the HIST_ -/// values. -/// -/// @parma in_map consider maptick when inside a mapping -/// @param sep separator character used (search hist) -void add_to_history(int histype, char_u *new_entry, int in_map, int sep) -{ - histentry_T *hisptr; - - if (hislen == 0 || histype == HIST_INVALID) { // no history - return; - } - assert(histype != HIST_DEFAULT); - - if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) { - return; - } - - /* - * Searches inside the same mapping overwrite each other, so that only - * the last line is kept. Be careful not to remove a line that was moved - * down, only lines that were added. - */ - if (histype == HIST_SEARCH && in_map) { - if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) { - // Current line is from the same mapping, remove it - hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; - hist_free_entry(hisptr); - --hisnum[histype]; - if (--hisidx[HIST_SEARCH] < 0) { - hisidx[HIST_SEARCH] = hislen - 1; - } - } - last_maptick = -1; - } - if (!in_history(histype, new_entry, true, sep)) { - if (++hisidx[histype] == hislen) { - hisidx[histype] = 0; - } - hisptr = &history[histype][hisidx[histype]]; - hist_free_entry(hisptr); - - // Store the separator after the NUL of the string. - size_t len = STRLEN(new_entry); - hisptr->hisstr = vim_strnsave(new_entry, len + 2); - hisptr->timestamp = os_time(); - hisptr->additional_elements = NULL; - hisptr->hisstr[len + 1] = (char_u)sep; - - hisptr->hisnum = ++hisnum[histype]; - if (histype == HIST_SEARCH && in_map) { - last_maptick = maptick; - } - } -} - -/* - * Get identifier of newest history entry. - * "histype" may be one of the HIST_ values. - */ -int get_history_idx(int histype) -{ - if (hislen == 0 || histype < 0 || histype >= HIST_COUNT - || hisidx[histype] < 0) { - return -1; - } - - return history[histype][hisidx[histype]].hisnum; -} - /// Get pointer to the command line info to use. save_cmdline() may clear /// ccline and put the previous value in ccline.prev_ccline. static struct cmdline_info *get_ccline_ptr(void) @@ -6292,168 +5984,10 @@ int get_cmdline_type(void) return p->cmdfirstc; } -/* - * Calculate history index from a number: - * num > 0: seen as identifying number of a history entry - * num < 0: relative position in history wrt newest entry - * "histype" may be one of the HIST_ values. - */ -static int calc_hist_idx(int histype, int num) -{ - int i; - histentry_T *hist; - int wrapped = FALSE; - - if (hislen == 0 || histype < 0 || histype >= HIST_COUNT - || (i = hisidx[histype]) < 0 || num == 0) { - return -1; - } - - hist = history[histype]; - if (num > 0) { - while (hist[i].hisnum > num) { - if (--i < 0) { - if (wrapped) { - break; - } - i += hislen; - wrapped = TRUE; - } - } - if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) { - return i; - } - } else if (-num <= hislen) { - i += num + 1; - if (i < 0) { - i += hislen; - } - if (hist[i].hisstr != NULL) { - return i; - } - } - return -1; -} - -/* - * Get a history entry by its index. - * "histype" may be one of the HIST_ values. - */ -char_u *get_history_entry(int histype, int idx) +/// Return the first character of the current command line. +int get_cmdline_firstc(void) { - idx = calc_hist_idx(histype, idx); - if (idx >= 0) { - return history[histype][idx].hisstr; - } else { - return (char_u *)""; - } -} - -/// Clear all entries in a history -/// -/// @param[in] histype One of the HIST_ values. -/// -/// @return OK if there was something to clean and histype was one of HIST_ -/// values, FAIL otherwise. -int clr_history(const int histype) -{ - if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) { - histentry_T *hisptr = history[histype]; - for (int i = hislen; i--; hisptr++) { - hist_free_entry(hisptr); - } - hisidx[histype] = -1; // mark history as cleared - hisnum[histype] = 0; // reset identifier counter - return OK; - } - return FAIL; -} - -/* - * Remove all entries matching {str} from a history. - * "histype" may be one of the HIST_ values. - */ -int del_history_entry(int histype, char_u *str) -{ - regmatch_T regmatch; - histentry_T *hisptr; - int idx; - int i; - int last; - bool found = false; - - regmatch.regprog = NULL; - regmatch.rm_ic = FALSE; // always match case - if (hislen != 0 - && histype >= 0 - && histype < HIST_COUNT - && *str != NUL - && (idx = hisidx[histype]) >= 0 - && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING)) - != NULL) { - i = last = idx; - do { - hisptr = &history[histype][i]; - if (hisptr->hisstr == NULL) { - break; - } - if (vim_regexec(®match, (char *)hisptr->hisstr, (colnr_T)0)) { - found = true; - hist_free_entry(hisptr); - } else { - if (i != last) { - history[histype][last] = *hisptr; - clear_hist_entry(hisptr); - } - if (--last < 0) { - last += hislen; - } - } - if (--i < 0) { - i += hislen; - } - } while (i != idx); - if (history[histype][idx].hisstr == NULL) { - hisidx[histype] = -1; - } - } - vim_regfree(regmatch.regprog); - return found; -} - -/* - * Remove an indexed entry from a history. - * "histype" may be one of the HIST_ values. - */ -int del_history_idx(int histype, int idx) -{ - int i, j; - - i = calc_hist_idx(histype, idx); - if (i < 0) { - return FALSE; - } - idx = hisidx[histype]; - hist_free_entry(&history[histype][i]); - - /* When deleting the last added search string in a mapping, reset - * last_maptick, so that the last added search string isn't deleted again. - */ - if (histype == HIST_SEARCH && maptick == last_maptick && i == idx) { - last_maptick = -1; - } - - while (i != idx) { - j = (i + 1) % hislen; - history[histype][i] = history[histype][j]; - i = j; - } - clear_hist_entry(&history[histype][idx]); - if (--i < 0) { - i += hislen; - } - hisidx[histype] = i; - return TRUE; + return ccline.cmdfirstc; } /// Get indices that specify a range within a list (not a range of text lines @@ -6493,115 +6027,6 @@ int get_list_range(char_u **str, int *num1, int *num2) return OK; } -/* - * :history command - print a history - */ -void ex_history(exarg_T *eap) -{ - histentry_T *hist; - int histype1 = HIST_CMD; - int histype2 = HIST_CMD; - int hisidx1 = 1; - int hisidx2 = -1; - int idx; - int i, j, k; - char_u *end; - char_u *arg = (char_u *)eap->arg; - - if (hislen == 0) { - msg(_("'history' option is zero")); - return; - } - - if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) { - end = arg; - while (ASCII_ISALPHA(*end) - || vim_strchr(":=@>/?", *end) != NULL) { - end++; - } - histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false); - if (histype1 == HIST_INVALID) { - if (STRNICMP(arg, "all", end - arg) == 0) { - histype1 = 0; - histype2 = HIST_COUNT - 1; - } else { - semsg(_(e_trailing_arg), arg); - return; - } - } else { - histype2 = histype1; - } - } else { - end = arg; - } - if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { - semsg(_(e_trailing_arg), end); - return; - } - - for (; !got_int && histype1 <= histype2; ++histype1) { - STRCPY(IObuff, "\n # "); - assert(history_names[histype1] != NULL); - STRCAT(STRCAT(IObuff, history_names[histype1]), " history"); - msg_puts_title((char *)IObuff); - idx = hisidx[histype1]; - hist = history[histype1]; - j = hisidx1; - k = hisidx2; - if (j < 0) { - j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum; - } - if (k < 0) { - k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum; - } - if (idx >= 0 && j <= k) { - for (i = idx + 1; !got_int; ++i) { - if (i == hislen) { - i = 0; - } - if (hist[i].hisstr != NULL - && hist[i].hisnum >= j && hist[i].hisnum <= k) { - msg_putchar('\n'); - snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ', - hist[i].hisnum); - if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) { - trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff), - Columns - 10, IOSIZE - (int)STRLEN(IObuff)); - } else { - STRCAT(IObuff, hist[i].hisstr); - } - msg_outtrans((char *)IObuff); - ui_flush(); - } - if (i == idx) { - break; - } - } - } - } -} - -/// Translate a history type number to the associated character -int hist_type2char(int type) - FUNC_ATTR_CONST -{ - switch (type) { - case HIST_CMD: - return ':'; - case HIST_SEARCH: - return '/'; - case HIST_EXPR: - return '='; - case HIST_INPUT: - return '@'; - case HIST_DEBUG: - return '>'; - default: - abort(); - } - return NUL; -} - void cmdline_init(void) { memset(&ccline, 0, sizeof(struct cmdline_info)); @@ -6688,18 +6113,18 @@ static int open_cmdwin(void) // Fill the buffer with the history. init_history(); - if (hislen > 0 && histtype != HIST_INVALID) { - i = hisidx[histtype]; + if (get_hislen() > 0 && histtype != HIST_INVALID) { + i = *get_hisidx(histtype); if (i >= 0) { lnum = 0; do { - if (++i == hislen) { + if (++i == get_hislen()) { i = 0; } - if (history[histtype][i].hisstr != NULL) { - ml_append(lnum++, (char *)history[histtype][i].hisstr, (colnr_T)0, false); + if (get_histentry(histtype)[i].hisstr != NULL) { + ml_append(lnum++, (char *)get_histentry(histtype)[i].hisstr, (colnr_T)0, false); } - } while (i != hisidx[histtype]); + } while (i != *get_hisidx(histtype)); } } @@ -6904,90 +6329,6 @@ char *script_get(exarg_T *const eap, size_t *const lenp) return (char *)ga.ga_data; } -/// Iterate over history items -/// -/// @warning No history-editing functions must be run while iteration is in -/// progress. -/// -/// @param[in] iter Pointer to the last history entry. -/// @param[in] history_type Type of the history (HIST_*). Ignored if iter -/// parameter is not NULL. -/// @param[in] zero If true then zero (but not free) returned items. -/// -/// @warning When using this parameter user is -/// responsible for calling clr_history() -/// itself after iteration is over. If -/// clr_history() is not called behaviour is -/// undefined. No functions that work with -/// history must be called during iteration -/// in this case. -/// @param[out] hist Next history entry. -/// -/// @return Pointer used in next iteration or NULL to indicate that iteration -/// was finished. -const void *hist_iter(const void *const iter, const uint8_t history_type, const bool zero, - histentry_T *const hist) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4) -{ - *hist = (histentry_T) { - .hisstr = NULL - }; - if (hisidx[history_type] == -1) { - return NULL; - } - histentry_T *const hstart = &(history[history_type][0]); - histentry_T *const hlast = ( - &(history[history_type][hisidx[history_type]])); - const histentry_T *const hend = &(history[history_type][hislen - 1]); - histentry_T *hiter; - if (iter == NULL) { - histentry_T *hfirst = hlast; - do { - hfirst++; - if (hfirst > hend) { - hfirst = hstart; - } - if (hfirst->hisstr != NULL) { - break; - } - } while (hfirst != hlast); - hiter = hfirst; - } else { - hiter = (histentry_T *)iter; - } - if (hiter == NULL) { - return NULL; - } - *hist = *hiter; - if (zero) { - memset(hiter, 0, sizeof(*hiter)); - } - if (hiter == hlast) { - return NULL; - } - hiter++; - return (const void *)((hiter > hend) ? hstart : hiter); -} - -/// Get array of history items -/// -/// @param[in] history_type Type of the history to get array for. -/// @param[out] new_hisidx Location where last index in the new array should -/// be saved. -/// @param[out] new_hisnum Location where last history number in the new -/// history should be saved. -/// -/// @return Pointer to the array or NULL. -histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx, - int **const new_hisnum) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - init_history(); - *new_hisidx = &(hisidx[history_type]); - *new_hisnum = &(hisnum[history_type]); - return history[history_type]; -} - static void set_search_match(pos_T *t) { // First move cursor to end of match, then to the start. This diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 1cc6faf87c..2f6929924d 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -3,8 +3,6 @@ #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" -#include "nvim/ex_cmds_defs.h" -#include "nvim/os/time.h" #include "nvim/regexp_defs.h" // Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. @@ -39,30 +37,8 @@ #define VSE_SHELL 1 ///< escape for a shell command #define VSE_BUFFER 2 ///< escape for a ":buffer" command -/// Present history tables -typedef enum { - HIST_DEFAULT = -2, ///< Default (current) history. - HIST_INVALID = -1, ///< Unknown history. - HIST_CMD = 0, ///< Colon commands. - HIST_SEARCH, ///< Search commands. - HIST_EXPR, ///< Expressions (e.g. from entering = register). - HIST_INPUT, ///< input() lines. - HIST_DEBUG, ///< Debug commands. -} HistoryType; - -/// Number of history tables -#define HIST_COUNT (HIST_DEBUG + 1) - typedef char *(*CompleteListItemGetter)(expand_T *, int); -/// History entry definition -typedef struct hist_entry { - int hisnum; ///< Entry identifier number. - char_u *hisstr; ///< Actual entry, separator char after the NUL. - Timestamp timestamp; ///< Time when entry was added. - list_T *additional_elements; ///< Additional entries from ShaDa file. -} histentry_T; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.h.generated.h" #endif diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 5ae7f7bbe3..4d00c0212e 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -619,6 +619,7 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size) # include "nvim/buffer.h" # include "nvim/charset.h" +# include "nvim/cmdhist.h" # include "nvim/diff.h" # include "nvim/edit.h" # include "nvim/eval/typval.h" diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2d752eade7..064b73a56f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -18,6 +18,7 @@ #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/digraph.h" @@ -4379,9 +4380,11 @@ static void nv_ident(cmdarg_T *cap) && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) { STRCAT(buf, "\\>"); } + // put pattern in search history init_history(); add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL); + (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0, NULL); } else { diff --git a/src/nvim/search.c b/src/nvim/search.c index 403e2f3aa4..b9aac59e1e 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -15,6 +15,7 @@ #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" +#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 6e80b550d8..ee5322752f 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -16,11 +16,12 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/cmdhist.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" +#include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/garray.h" #include "nvim/globals.h" @@ -2451,6 +2452,27 @@ static inline void find_removable_bufs(khash_t(bufset) *removable_bufs) } } +/// Translate a history type number to the associated character +static int hist_type2char(const int type) + FUNC_ATTR_CONST +{ + switch (type) { + case HIST_CMD: + return ':'; + case HIST_SEARCH: + return '/'; + case HIST_EXPR: + return '='; + case HIST_INPUT: + return '@'; + case HIST_DEBUG: + return '>'; + default: + abort(); + } + return NUL; +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. -- cgit From 24bf0490ea3a16c14494358fe45437e43ca8d1d1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Aug 2022 20:35:34 +0800 Subject: vim-patch:9.0.0176: checking character options is duplicated and incomplete (#19690) Problem: Checking character options is duplicated and incomplete. Solution: Move checking to check_chars_options(). (closes vim/vim#10863) https://github.com/vim/vim/commit/8ca29b6a3599b82b8822b7697cad63d0244c2d59 --- src/nvim/globals.h | 5 ----- src/nvim/mbyte.c | 22 +++------------------- src/nvim/option.c | 39 +++++++++++++++++++++++++++------------ src/nvim/testdir/test_options.vim | 12 ++++++++++-- 4 files changed, 40 insertions(+), 38 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 23870a572e..317423ffa0 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1000,11 +1000,6 @@ EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); -EXTERN char e_conflicts_with_value_of_listchars[] -INIT(= N_("E834: Conflicts with value of 'listchars'")); -EXTERN char e_conflicts_with_value_of_fillchars[] -INIT(= N_("E835: Conflicts with value of 'fillchars'")); - EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s")); EXTERN char e_cmdmap_err[] INIT(= N_("E5520: mapping must end with ")); EXTERN char e_cmdmap_repeated[] diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index e156fa58d1..8e8cf962c7 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2844,25 +2844,9 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, FunPtr fptr) cw_table = table; cw_table_size = (size_t)tv_list_len(l); - // Check that the new value does not conflict with 'fillchars' or - // 'listchars'. - char *error = NULL; - if (set_chars_option(curwin, &p_fcs, false) != NULL) { - error = e_conflicts_with_value_of_fillchars; - } else if (set_chars_option(curwin, &p_lcs, false) != NULL) { - error = e_conflicts_with_value_of_listchars; - } else { - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { - error = e_conflicts_with_value_of_listchars; - break; - } - if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { - error = e_conflicts_with_value_of_fillchars; - break; - } - } - } + // Check that the new value does not conflict with 'listchars' or + // 'fillchars'. + const char *const error = check_chars_options(); if (error != NULL) { emsg(_(error)); cw_table = cw_table_save; diff --git a/src/nvim/option.c b/src/nvim/option.c index d7443bc593..8eb3b64e5b 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -339,6 +339,9 @@ static char_u SHM_ALL[] = { static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence"); static char e_unbalanced_groups[] = N_("E542: unbalanced groups"); +static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'"); +static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'"); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" #endif @@ -2578,18 +2581,7 @@ static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, c if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { errmsg = e_invarg; } else { - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { - errmsg = _(e_conflicts_with_value_of_listchars); - goto ambw_end; - } - if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { - errmsg = _(e_conflicts_with_value_of_fillchars); - goto ambw_end; - } - } -ambw_end: - {} // clint prefers {} over ; as an empty statement + errmsg = check_chars_options(); } } else if (varp == &p_bg) { // 'background' if (check_opt_strings(p_bg, p_bg_values, false) == OK) { @@ -3823,6 +3815,29 @@ char *set_chars_option(win_T *wp, char_u **varp, bool set) return NULL; // no error } +/// Check all global and local values of 'listchars' and 'fillchars'. +/// May set different defaults in case character widths change. +/// +/// @return an untranslated error message if any of them is invalid, NULL otherwise. +char *check_chars_options(void) +{ + if (set_chars_option(curwin, &p_lcs, false) != NULL) { + return e_conflicts_with_value_of_listchars; + } + if (set_chars_option(curwin, &p_fcs, false) != NULL) { + return e_conflicts_with_value_of_fillchars; + } + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { + return e_conflicts_with_value_of_listchars; + } + if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { + return e_conflicts_with_value_of_fillchars; + } + } + return NULL; +} + /// Check validity of options with the 'statusline' format. /// Return an untranslated error message or NULL. char *check_stl_option(char *s) diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index b10f0f5030..6e9f2d2377 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -368,9 +368,17 @@ func Test_set_errors() call assert_fails('set sessionoptions=curdir,sesdir', 'E474:') call assert_fails('set foldmarker={{{,', 'E474:') call assert_fails('set sessionoptions=sesdir,curdir', 'E474:') - call assert_fails('set listchars=trail:· ambiwidth=double', 'E834:') + setlocal listchars=trail:· + call assert_fails('set ambiwidth=double', 'E834:') + setlocal listchars=trail:- + setglobal listchars=trail:· + call assert_fails('set ambiwidth=double', 'E834:') set listchars& - call assert_fails('set fillchars=stl:· ambiwidth=double', 'E835:') + setlocal fillchars=stl:· + call assert_fails('set ambiwidth=double', 'E835:') + setlocal fillchars=stl:- + setglobal fillchars=stl:· + call assert_fails('set ambiwidth=double', 'E835:') set fillchars& call assert_fails('set fileencoding=latin1,utf-8', 'E474:') set nomodifiable -- cgit From 3030b4d65345baaa92606ad47e53573463c842d8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Aug 2022 21:08:46 +0800 Subject: feat(tui): allow grid and host to disagree on ambiguous-width chars (#19686) Note: This only applies to ambiguous-width characters. --- src/nvim/tui/tui.c | 68 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 13 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e2289eb9ce..39133a3275 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -875,6 +875,53 @@ safe_move: ugrid_goto(grid, row, col); } +static void print_spaces(UI *ui, int width) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + + out(ui, data->space_buf, (size_t)width); + grid->col += width; + if (data->immediate_wrap_after_last_column) { + // Printing at the right margin immediately advances the cursor. + final_column_wrap(ui); + } +} + +/// Move cursor to the position given by `row` and `col` and print the character in `cell`. +/// This allows the grid and the host terminal to assume different widths of ambiguous-width chars. +/// +/// @param is_doublewidth whether the character is double-width on the grid. +/// If true and the character is ambiguous-width, clear two cells. +static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_doublewidth) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + + if (grid->row == -1 && cell->data[0] == NUL) { + // If cursor needs to repositioned and there is nothing to print, don't move cursor. + return; + } + + cursor_goto(ui, row, col); + + bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data)); + if (is_ambiwidth && is_doublewidth) { + // Clear the two screen cells. + // If the character is single-width in the host terminal it won't change the second cell. + update_attrs(ui, cell->attr); + print_spaces(ui, 2); + cursor_goto(ui, row, col); + } + + print_cell(ui, cell); + + if (is_ambiwidth) { + // Force repositioning cursor after printing an ambiguous-width character. + grid->row = -1; + } +} + static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id) { TUIData *data = ui->data; @@ -888,7 +935,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr && left == 0 && right == ui->width && bot == ui->height) { if (top == 0) { unibi_out(ui, unibi_clear_screen); - ugrid_goto(&data->grid, top, left); + ugrid_goto(grid, top, left); } else { cursor_goto(ui, top, 0); unibi_out(ui, unibi_clr_eos); @@ -905,12 +952,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr UNIBI_SET_NUM_VAR(data->params[0], width); unibi_out(ui, unibi_erase_chars); } else { - out(ui, data->space_buf, (size_t)width); - grid->col += width; - if (data->immediate_wrap_after_last_column) { - // Printing at the right margin immediately advances the cursor. - final_column_wrap(ui); - } + print_spaces(ui, width); } } } @@ -1302,8 +1344,8 @@ static void tui_flush(UI *ui) } UGRID_FOREACH_CELL(grid, row, r.left, clear_col, { - cursor_goto(ui, row, curcol); - print_cell(ui, cell); + print_cell_at_pos(ui, row, curcol, cell, + curcol < clear_col - 1 && (cell + 1)->data[0] == NUL); }); if (clear_col < r.right) { clear_region(ui, row, row + 1, clear_col, r.right, clear_attr); @@ -1439,8 +1481,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I grid->cells[linerow][c].attr = attrs[c - startcol]; } UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, { - cursor_goto(ui, (int)linerow, curcol); - print_cell(ui, cell); + print_cell_at_pos(ui, (int)linerow, curcol, cell, + curcol < endcol - 1 && (cell + 1)->data[0] == NUL); }); if (clearcol > endcol) { @@ -1458,8 +1500,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I if (endcol != grid->width) { // Print the last char of the row, if we haven't already done so. int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1; - cursor_goto(ui, (int)linerow, grid->width - size); - print_cell(ui, &grid->cells[linerow][grid->width - size]); + print_cell_at_pos(ui, (int)linerow, grid->width - size, + &grid->cells[linerow][grid->width - size], size == 2); } // Wrap the cursor over to the next line. The next line will be -- cgit From 78658ef3834baf7d202eb16a8778813e08e58412 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 10 Aug 2022 16:37:59 +0600 Subject: fix(api): `vim.cmd.make` crashes when argument count isn't 1 (#19701) Closes #19696 --- src/nvim/api/command.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index bc766ff39c..fc1f6a04f2 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -819,9 +819,12 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin char *p = replace_makeprg(eap, eap->arg, cmdlinep); if (p != eap->arg) { // If replace_makeprg modified the cmdline string, correct the argument pointers. - assert(argc == 1); eap->arg = p; - eap->args[0] = p; + // We can only know the position of the first argument because the argument list can be used + // multiple times in makeprg / grepprg. + if (argc >= 1) { + eap->args[0] = p; + } } } -- cgit From ff1266aaaaba80f86ab9624eea8939d644d7e730 Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:44:57 +0200 Subject: vim-patch:9.0.0182: quarto files are not recognized (#19702) Problem: Quarto files are not recognized. Solution: Recognize quarto files by the extension. (Jonas Strittmatter, closes vim/vim#10880) https://github.com/vim/vim/commit/3a9687fb2749cb3da6e3bbf60cb9eaa81f7889ae --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index eedad15e9e..beaf5fdcfa 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -441,6 +441,7 @@ let s:filename_checks = { \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], \ 'ql': ['file.ql', 'file.qll'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], + \ 'quarto': ['file.qmd'], \ 'r': ['file.r'], \ 'radiance': ['file.rad', 'file.mat'], \ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'], -- cgit From 94c317647845b92d675548a481f664a6a1808426 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 11 Aug 2022 15:44:55 +0800 Subject: refactor: use CLEAR_FIELD and CLEAR_POINTER macros (#19709) vim-patch:8.2.0559: clearing a struct is verbose Problem: Clearing a struct is verbose. Solution: Define and use CLEAR_FIELD() and CLEAR_POINTER(). https://github.com/vim/vim/commit/a80faa8930ed5a554beeb2727762538873135e83 --- src/nvim/api/command.c | 4 ++-- src/nvim/api/ui.c | 2 +- src/nvim/buffer.c | 4 ++-- src/nvim/charset.c | 2 +- src/nvim/cmdhist.c | 4 ++-- src/nvim/decoration.c | 2 +- src/nvim/diff.c | 10 +++++----- src/nvim/eval.c | 2 +- src/nvim/eval/funcs.c | 20 ++++++++++---------- src/nvim/eval/typval.c | 4 ++-- src/nvim/eval/userfunc.c | 2 +- src/nvim/ex_docmd.c | 38 +++++++++++++++++++------------------- src/nvim/ex_getln.c | 26 +++++++++++++------------- src/nvim/fileio.c | 2 +- src/nvim/hardcopy.c | 8 ++++---- src/nvim/hashtab.c | 10 ++++++---- src/nvim/highlight_group.c | 6 +++--- src/nvim/insexpand.c | 2 +- src/nvim/lua/converter.c | 2 +- src/nvim/lua/executor.c | 2 +- src/nvim/lua/xdiff.c | 6 +++--- src/nvim/main.c | 2 +- src/nvim/mapping.c | 2 +- src/nvim/mark.c | 4 ++-- src/nvim/memfile.c | 4 ++-- src/nvim/normal.c | 6 +++--- src/nvim/ops.c | 2 +- src/nvim/option.c | 2 +- src/nvim/os/fs.c | 6 +++--- src/nvim/popupmnu.c | 2 +- src/nvim/quickfix.c | 4 ++-- src/nvim/regexp_bt.c | 2 +- src/nvim/regexp_nfa.c | 4 ++-- src/nvim/screen.c | 2 +- src/nvim/search.c | 10 +++++----- src/nvim/shada.c | 6 +++--- src/nvim/sign.c | 2 +- src/nvim/spell.c | 14 +++++++------- src/nvim/spellfile.c | 6 +++--- src/nvim/syntax.c | 9 ++++----- src/nvim/tag.c | 6 +++--- src/nvim/tui/tui.c | 2 +- src/nvim/undo.c | 24 ++++++++++++------------ src/nvim/vim.h | 1 + src/nvim/window.c | 6 +++--- 45 files changed, 144 insertions(+), 142 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index fc1f6a04f2..d264237e37 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -300,10 +300,10 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error FUNC_API_SINCE(10) { exarg_T ea; - memset(&ea, 0, sizeof(ea)); + CLEAR_FIELD(ea); CmdParseInfo cmdinfo; - memset(&cmdinfo, 0, sizeof(cmdinfo)); + CLEAR_FIELD(cmdinfo); char *cmdline = NULL; char *cmdname = NULL; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 6239e414a7..9cd4c97508 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -224,7 +224,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona ui->event = remote_ui_event; ui->inspect = remote_ui_inspect; - memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); + CLEAR_FIELD(ui->ui_ext); for (size_t i = 0; i < options.size; i++) { ui_set_option(ui, true, options.items[i].key, options.items[i].value, err); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f23a1caf8b..657a18e0b6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -793,8 +793,8 @@ static void free_buffer(buf_T *buf) if (autocmd_busy) { // Do not free the buffer structure while autocommands are executing, // it's still needed. Free it when autocmd_busy is reset. - memset(&buf->b_namedm[0], 0, sizeof(buf->b_namedm)); - memset(&buf->b_changelist[0], 0, sizeof(buf->b_changelist)); + CLEAR_FIELD(buf->b_namedm); + CLEAR_FIELD(buf->b_changelist); buf->b_next = au_pending_free_buf; au_pending_free_buf = buf; } else { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index a26a7f6aaf..6238d85b3a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -126,7 +126,7 @@ int buf_init_chartab(buf_T *buf, int global) } // Init word char flags all to false - memset(buf->b_chartab, 0, (size_t)32); + CLEAR_FIELD(buf->b_chartab); // In lisp mode the '-' character is included in keywords. if (buf->b_p_lisp) { diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 625bd514ad..cd494d2247 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -178,7 +178,7 @@ static inline void hist_free_entry(histentry_T *hisptr) static inline void clear_hist_entry(histentry_T *hisptr) FUNC_ATTR_NONNULL_ALL { - memset(hisptr, 0, sizeof(*hisptr)); + CLEAR_POINTER(hisptr); } /// Check if command line 'str' is already in history. @@ -714,7 +714,7 @@ const void *hist_iter(const void *const iter, const uint8_t history_type, const } *hist = *hiter; if (zero) { - memset(hiter, 0, sizeof(*hiter)); + CLEAR_POINTER(hiter); } if (hiter == hlast) { return NULL; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index e7c76fe38e..6f946bb41a 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -391,7 +391,7 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs sattrs[j] = sattrs[j - 1]; } if (j < SIGN_SHOW_MAX) { - memset(&sattrs[j], 0, sizeof(sign_attrs_T)); + CLEAR_FIELD(sattrs[j]); sattrs[j].sat_text = decor->sign_text; if (decor->sign_hl_id != 0) { sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id); diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 849204f789..e3a6f9039d 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -956,13 +956,13 @@ void ex_diffupdate(exarg_T *eap) // Only use the internal method if it did not fail for one of the buffers. diffio_T diffio; - memset(&diffio, 0, sizeof(diffio)); + CLEAR_FIELD(diffio); diffio.dio_internal = diff_internal() && !diff_internal_failed(); diff_try_update(&diffio, idx_orig, eap); if (diffio.dio_internal && diff_internal_failed()) { // Internal diff failed, use external diff instead. - memset(&diffio, 0, sizeof(diffio)); + CLEAR_FIELD(diffio); diff_try_update(&diffio, idx_orig, eap); } @@ -1075,9 +1075,9 @@ static int diff_file_internal(diffio_T *diffio) xdemitconf_t emit_cfg; xdemitcb_t emit_cb; - memset(¶m, 0, sizeof(param)); - memset(&emit_cfg, 0, sizeof(emit_cfg)); - memset(&emit_cb, 0, sizeof(emit_cb)); + CLEAR_FIELD(param); + CLEAR_FIELD(emit_cfg); + CLEAR_FIELD(emit_cb); param.flags = (unsigned long)diff_algorithm; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b0a207e5ba..5abaf88e60 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1308,7 +1308,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const int quiet = flags & GLV_QUIET; // Clear everything in "lp". - memset(lp, 0, sizeof(lval_T)); + CLEAR_POINTER(lp); if (skip) { // When skipping just find the end of the name. diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 52386a67f6..4a44c08457 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2640,7 +2640,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_PARTIAL) { pt = argvars[0].vval.v_partial; } else { - memset(&fref_pt, 0, sizeof(fref_pt)); + CLEAR_FIELD(fref_pt); fref_pt.pt_name = (char_u *)argvars[0].vval.v_string; pt = &fref_pt; } @@ -7127,7 +7127,6 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) long time_limit = 0; int options = SEARCH_KEEP; int subpatnum; - searchit_arg_T sia; bool use_skip = false; const char *const pat = tv_get_string(&argvars[0]); @@ -7178,9 +7177,10 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) pos = save_cursor = curwin->w_cursor; pos_T firstpos = { 0 }; - memset(&sia, 0, sizeof(sia)); - sia.sa_stop_lnum = (linenr_T)lnum_stop; - sia.sa_tm = &tm; + searchit_arg_T sia = { + .sa_stop_lnum = (linenr_T)lnum_stop, + .sa_tm = &tm, + }; // Repeat until {skip} returns false. for (;;) { @@ -7799,10 +7799,10 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir clearpos(&foundpos); pat = pat3; for (;;) { - searchit_arg_T sia; - memset(&sia, 0, sizeof(sia)); - sia.sa_stop_lnum = lnum_stop; - sia.sa_tm = &tm; + searchit_arg_T sia = { + .sa_stop_lnum = lnum_stop, + .sa_tm = &tm, + }; n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L, options, RE_SEARCH, &sia); @@ -9500,7 +9500,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) const linenr_T lnum = tv_get_lnum(argvars); const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; - memset(str, NUL, sizeof(str)); + CLEAR_FIELD(str); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ff1808ed91..8822bb0491 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -233,7 +233,7 @@ void tv_list_init_static10(staticList10_T *const sl) #define SL_SIZE ARRAY_SIZE(sl->sl_items) list_T *const l = &sl->sl_list; - memset(sl, 0, sizeof(staticList10_T)); + CLEAR_POINTER(sl); l->lv_first = &sl->sl_items[0]; l->lv_last = &sl->sl_items[SL_SIZE - 1]; l->lv_refcount = DO_NOT_FREE_CNT; @@ -261,7 +261,7 @@ void tv_list_init_static10(staticList10_T *const sl) void tv_list_init_static(list_T *const l) FUNC_ATTR_NONNULL_ALL { - memset(l, 0, sizeof(*l)); + CLEAR_POINTER(l); l->lv_refcount = DO_NOT_FREE_CNT; list_log(l, NULL, NULL, "sinit"); } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 2f4799db57..38a00099bc 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1702,7 +1702,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, lval_T lv; if (fdp != NULL) { - memset(fdp, 0, sizeof(funcdict_T)); + CLEAR_POINTER(fdp); } start = *pp; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 86c5489cf2..e8dc0b8e86 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -391,7 +391,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) if (flags & DOCMD_EXCRESET) { save_dbg_stuff(&debug_saved); } else { - memset(&debug_saved, 0, sizeof(debug_saved)); + CLEAR_FIELD(debug_saved); } initial_trylevel = trylevel; @@ -1428,16 +1428,17 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er char *after_modifier = NULL; // Initialize cmdinfo - memset(cmdinfo, 0, sizeof(*cmdinfo)); + CLEAR_POINTER(cmdinfo); // Initialize eap - memset(eap, 0, sizeof(*eap)); - eap->line1 = 1; - eap->line2 = 1; - eap->cmd = cmdline; - eap->cmdlinep = &cmdline; - eap->getline = NULL; - eap->cookie = NULL; + *eap = (exarg_T){ + .line1 = 1, + .line2 = 1, + .cmd = cmdline, + .cmdlinep = &cmdline, + .getline = NULL, + .cookie = NULL, + }; const bool save_ex_pressedreturn = ex_pressedreturn; // Parse command modifiers @@ -1877,10 +1878,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter const int save_reg_executing = reg_executing; const bool save_pending_end_reg_executing = pending_end_reg_executing; - exarg_T ea; - memset(&ea, 0, sizeof(ea)); - ea.line1 = 1; - ea.line2 = 1; + exarg_T ea = { + .line1 = 1, + .line2 = 1, + }; ex_nesting_level++; // When the last file has not been edited :q has to be typed twice. @@ -6021,12 +6022,11 @@ theend: /// Open a new tab page. void tabpage_new(void) { - exarg_T ea; - - memset(&ea, 0, sizeof(ea)); - ea.cmdidx = CMD_tabnew; - ea.cmd = "tabn"; - ea.arg = ""; + exarg_T ea = { + .cmdidx = CMD_tabnew, + .cmd = "tabn", + .arg = "", + }; ex_splitview(&ea); } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 841345d625..22bbefe827 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -311,7 +311,6 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s int delim; char *end; char *dummy; - exarg_T ea; pos_T save_cursor; bool use_last_pat; bool retval = false; @@ -336,11 +335,12 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s } emsg_off++; - memset(&ea, 0, sizeof(ea)); - ea.line1 = 1; - ea.line2 = 1; - ea.cmd = (char *)ccline.cmdbuff; - ea.addr_type = ADDR_LINES; + exarg_T ea = { + .line1 = 1, + .line2 = 1, + .cmd = (char *)ccline.cmdbuff, + .addr_type = ADDR_LINES, + }; cmdmod_T dummy_cmdmod; parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true); @@ -453,7 +453,6 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat { pos_T end_pos; proftime_T tm; - searchit_arg_T sia; int skiplen, patlen; char_u next_char; char_u use_last_pat; @@ -516,8 +515,9 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat search_flags += SEARCH_START; } ccline.cmdbuff[skiplen + patlen] = NUL; - memset(&sia, 0, sizeof(sia)); - sia.sa_tm = &tm; + searchit_arg_T sia = { + .sa_tm = &tm, + }; found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim, ccline.cmdbuff + skiplen, count, search_flags, &sia); @@ -731,7 +731,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init save_cmdline(&save_ccline); did_save_ccline = true; } else if (init_ccline) { - memset(&ccline, 0, sizeof(struct cmdline_info)); + CLEAR_FIELD(ccline); } if (s->firstc == -1) { @@ -2677,7 +2677,7 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at save_cmdline(&save_ccline); did_save_ccline = true; } else { - memset(&ccline, 0, sizeof(struct cmdline_info)); + CLEAR_FIELD(ccline); } ccline.prompt_id = last_prompt_id++; ccline.cmdprompt = (char_u *)prompt; @@ -3640,7 +3640,7 @@ void put_on_cmdline(char_u *str, int len, int redraw) static void save_cmdline(struct cmdline_info *ccp) { *ccp = ccline; - memset(&ccline, 0, sizeof(struct cmdline_info)); + CLEAR_FIELD(ccline); ccline.prev_ccline = ccp; ccline.cmdbuff = NULL; // signal that ccline is not in use } @@ -6029,7 +6029,7 @@ int get_list_range(char_u **str, int *num1, int *num2) void cmdline_init(void) { - memset(&ccline, 0, sizeof(struct cmdline_info)); + CLEAR_FIELD(ccline); } /// Open a window on the current command line and history. Allow editing in diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index b98984017b..b467f53bbd 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5122,7 +5122,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) // file, not reset the syntax highlighting, clear marks, diff status, etc. // Force the fileformat and encoding to be the same. if (reload_options) { - memset(&ea, 0, sizeof(ea)); + CLEAR_FIELD(ea); } else { prep_exarg(&ea, buf); } diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index d7f7b8eb92..96d3bfb4d3 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -632,8 +632,8 @@ void ex_hardcopy(exarg_T *eap) int page_line; int jobsplit; - memset(&settings, 0, sizeof(prt_settings_T)); - settings.has_color = TRUE; + CLEAR_FIELD(settings); + settings.has_color = true; if (*eap->arg == '>') { char *errormsg = NULL; @@ -734,7 +734,7 @@ void ex_hardcopy(exarg_T *eap) prt_pos_T page_prtpos; // print position at page start int side; - memset(&page_prtpos, 0, sizeof(prt_pos_T)); + CLEAR_FIELD(page_prtpos); page_prtpos.file_line = eap->line1; prtpos = page_prtpos; @@ -1718,7 +1718,7 @@ static bool prt_open_resource(struct prt_ps_resource_S *resource) semsg(_("E624: Can't open file \"%s\""), resource->filename); return false; } - memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN); + CLEAR_FIELD(prt_resfile.buffer); // Parse first line to ensure valid resource file prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u), diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index 95ae7a152c..951e72ea52 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -45,7 +45,7 @@ char hash_removed; void hash_init(hashtab_T *ht) { // This zeroes all "ht_" entries and all the "hi_key" in "ht_smallarray". - memset(ht, 0, sizeof(hashtab_T)); + CLEAR_POINTER(ht); ht->ht_array = ht->ht_smallarray; ht->ht_mask = HT_INIT_SIZE - 1; } @@ -342,11 +342,13 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems) hashitem_T *oldarray = keep_smallarray ? memcpy(temparray, ht->ht_smallarray, sizeof(temparray)) : ht->ht_array; + + if (newarray_is_small) { + CLEAR_FIELD(ht->ht_smallarray); + } hashitem_T *newarray = newarray_is_small ? ht->ht_smallarray - : xmalloc(sizeof(hashitem_T) * newsize); - - memset(newarray, 0, sizeof(hashitem_T) * newsize); + : xcalloc(newsize, sizeof(hashitem_T)); // Move all the items from the old array to the new one, placing them in // the right spot. The new array won't have any removed items, thus this diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index f6ec03fb14..5afbaaf121 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1765,7 +1765,7 @@ static int syn_add_group(const char *name, size_t len) // Append another syntax_highlight entry. HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga); - memset(hlgp, 0, sizeof(*hlgp)); + CLEAR_POINTER(hlgp); hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len); hlgp->sg_rgb_bg = -1; hlgp->sg_rgb_fg = -1; @@ -1863,7 +1863,7 @@ static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int HlGroup *const hlt = hl_table; if (id_alt == 0) { - memset(&hlt[hlcnt + i], 0, sizeof(HlGroup)); + CLEAR_POINTER(&hlt[hlcnt + i]); hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; } else { @@ -1945,7 +1945,7 @@ void highlight_changed(void) hlcnt = highlight_ga.ga_len; if (id_S == -1) { // Make sure id_S is always valid to simplify code below. Use the last entry - memset(&hl_table[hlcnt + 9], 0, sizeof(HlGroup)); + CLEAR_POINTER(&hl_table[hlcnt + 9]); id_S = hlcnt + 10; } for (int i = 0; i < 9; i++) { diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index a64d8e8f00..bddeee119d 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -2139,7 +2139,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast) } } else { word = tv_get_string_chk(tv); - memset(cptext, 0, sizeof(cptext)); + CLEAR_FIELD(cptext); } if (word == NULL || (!empty && *word == NUL)) { for (size_t i = 0; i < CPT_COUNT; i++) { diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 4d6e6090b8..49d49f76b9 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -59,7 +59,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) size_t other_keys_num = 0; // Number of keys that are not string, integral // or type keys. LuaTableProps ret; - memset(&ret, 0, sizeof(ret)); + CLEAR_FIELD(ret); if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2); ret.type = kObjectTypeNil; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 661dbfc4c2..c7b654482c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -447,7 +447,7 @@ static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread) FUNC_ATTR_NONNULL_ALL { nlua_ref_state_t *ref_state = lua_newuserdata(lstate, sizeof(*ref_state)); - memset(ref_state, 0, sizeof(*ref_state)); + CLEAR_POINTER(ref_state); ref_state->nil_ref = LUA_NOREF; ref_state->empty_dict_ref = LUA_NOREF; if (!is_thread) { diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index 71f85385b6..b2b5dfedee 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -269,9 +269,9 @@ int nlua_xdl_diff(lua_State *lstate) xpparam_t params; xdemitcb_t ecb; - memset(&cfg, 0, sizeof(cfg)); - memset(¶ms, 0, sizeof(params)); - memset(&ecb, 0, sizeof(ecb)); + CLEAR_FIELD(cfg); + CLEAR_FIELD(params); + CLEAR_FIELD(ecb); NluaXdiffMode mode = kNluaXdiffModeUnified; diff --git a/src/nvim/main.c b/src/nvim/main.c index 494ff0b4af..378ca6ba71 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1418,7 +1418,7 @@ scripterror: * copied, so that they can be changed. */ static void init_params(mparm_T *paramp, int argc, char **argv) { - memset(paramp, 0, sizeof(*paramp)); + CLEAR_POINTER(paramp); paramp->argc = argc; paramp->argv = argv; paramp->use_debug_break_level = -1; diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 342b1b0d47..f0a82b6bec 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -333,7 +333,7 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma { const char_u *to_parse = strargs; to_parse = (char_u *)skipwhite((char *)to_parse); - memset(mapargs, 0, sizeof(*mapargs)); + CLEAR_POINTER(mapargs); // Accept , , , ,