From a5481957c65985fc1f885464ae9b43f92d0dc519 Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 17 Mar 2017 22:48:42 +0800 Subject: vim-patch:7.4.2255 Problem: The script that checks translations can't handle plurals. Solution: Check for plural msgid and msgstr entries. Leave the cursor on the first error. https://github.com/vim/vim/commit/ec42059b78c1932a44f2bf36ac982109884dc7c7 --- src/nvim/po/check.vim | 76 +++++++++++++++++++++++++++++++++++++-------------- src/nvim/version.c | 2 +- 2 files changed, 56 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim index 1622741da6..b323174550 100644 --- a/src/nvim/po/check.vim +++ b/src/nvim/po/check.vim @@ -33,36 +33,66 @@ func! GetMline() return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g') endfunc -" This only works when 'wrapscan' is set. +" This only works when 'wrapscan' is not set. let s:save_wrapscan = &wrapscan -set wrapscan +set nowrapscan " Start at the first "msgid" line. 1 -/^msgid -let startline = line('.') +/^msgid\> + +" When an error is detected this is set to the line number. +" Note: this is used in the Makefile. let error = 0 while 1 if getline(line('.') - 1) !~ "no-c-format" - let fromline = GetMline() + " go over the "msgid" and "msgid_plural" lines + let prevfromline = 'foobar' + while 1 + let fromline = GetMline() + if prevfromline != 'foobar' && prevfromline != fromline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . prevfromline + echomsg 'msgid ' . fromline + if error == 0 + let error = line('.') + endif + endif + if getline('.') !~ 'msgid_plural' + break + endif + let prevfromline = fromline + endwhile + if getline('.') !~ '^msgstr' - echo 'Missing "msgstr" in line ' . line('.') - let error = 1 - endif - let toline = GetMline() - if fromline != toline - echo 'Mismatching % in line ' . (line('.') - 1) - echo 'msgid: ' . fromline - echo 'msgstr: ' . toline - let error = 1 + echomsg 'Missing "msgstr" in line ' . line('.') + if error == 0 + let error = line('.') + endif endif + + " check all the 'msgstr' lines + while getline('.') =~ '^msgstr' + let toline = GetMline() + if fromline != toline + echomsg 'Mismatching % in line ' . (line('.') - 1) + echomsg 'msgid: ' . fromline + echomsg 'msgstr: ' . toline + if error == 0 + let error = line('.') + endif + endif + if line('.') == line('$') + break + endif + endwhile endif - " Find next msgid. - " Wrap around at the end of the file, quit when back at the first one. - /^msgid - if line('.') == startline + " Find next msgid. Quit when there is no more. + let lnum = line('.') + silent! /^msgid\> + if line('.') == lnum break endif endwhile @@ -77,12 +107,16 @@ endwhile " 1 if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0 - echo 'Mismatching error/warning code in line ' . line('.') - let error = 1 + echomsg 'Mismatching error/warning code in line ' . line('.') + if error == 0 + let error = line('.') + endif endif if error == 0 - echo "OK" + echomsg "OK" +else + exe error endif redir END diff --git a/src/nvim/version.c b/src/nvim/version.c index 5b3a7398d5..b9286c2a4e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -186,7 +186,7 @@ static int included_patches[] = { // 2258 NA // 2257 NA // 2256, - // 2255, + 2255, // 2254 NA // 2253 NA // 2252 NA -- cgit From b4cb5fa610903492f89a70d0ae2d4565ddcd1228 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 19:20:07 +0100 Subject: vim-patch:7.4.2266 Problem: printf() test fails on Windows. "-inf" is not used. Solution: Check for Windows-specific values for "nan". Add sign to "inf" when appropriate. https://github.com/vim/vim/commit/9992237a3e791fbc0c1ebf743ece1b75e1488410 --- src/nvim/strings.c | 22 ++++++++++++++++++++-- src/nvim/testdir/test_expr.vim | 8 +++----- src/nvim/version.c | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index d03970108b..47ee311bd4 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1209,9 +1209,10 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } if (fmt_spec == 'f' && abs_f > 1.0e307) { + const char inf_str[] = f < 0 ? "-inf" : "inf"; // avoid a buffer overflow - memmove(tmp, "inf", sizeof("inf")); - str_arg_l = sizeof("inf") - 1; + memmove(tmp, inf_str, sizeof(inf_str) - 1); + str_arg_l = sizeof(inf_str) - 1; } else { format[0] = '%'; int l = 1; @@ -1234,6 +1235,23 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); assert(str_arg_l < sizeof(tmp)); + // Be consistent: Change "1.#IND" to "nan" and + // "1.#INF" to "inf". + char *s = *tmp == '-' ? tmp + 1 : tmp; + if (STRNCMP(s, "1.#INF", 6) == 0) { + STRCPY(s, "inf"); + } else if (STRNCMP(s, "1.#IND", 6) == 0) { + STRCPY(s, "nan"); + } + // Remove sign before "nan" + if (STRNCMP(tmp, "-nan", 4) == 0) { + STRCPY(tmp, "nan"); + } + // Add sign before "inf" if needed. + if (isinf(f) == -1 && STRNCMP(tmp, "inf", 3) == 0) { + STRCPY(tmp, "-inf"); + } + if (remove_trailing_zeroes) { int i; char *tp; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7f7811dc7a..fc804f3a15 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -201,12 +201,10 @@ function Test_printf_float() call assert_equal('inf', printf('%f', 1.0/0.0)) - " This prints inf but shouldn't it print -inf instead? - call assert_match('^-\?inf$', printf('%f', -1.0/0.0)) + call assert_match('^-inf$', printf('%f', -1.0/0.0)) - " This prints -nan but shouldn't it print nan instead? - call assert_match('^-\?nan$', printf('%f', sqrt(-1.0))) - call assert_match('^-\?nan$', printf('%f', 0.0/0.0)) + call assert_match('^nan$', printf('%f', sqrt(-1.0))) + call assert_match('^nan$', printf('%f', 0.0/0.0)) call assert_fails('echo printf("%f", "a")', 'E807:') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 8fd0fce329..5479e9f91f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -175,7 +175,7 @@ static int included_patches[] = { // 2269, // 2268, // 2267 NA - // 2266, + 2266, 2265, 2264, // 2263, -- cgit From 6ca580be9bffcccc513bfce383a6e1f9f026614c Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 08:47:31 +0100 Subject: vim-patch:7.4.2280 Problem: printf() doesn't handle infinity float values correctly. Solution: Add a table with possible infinity values. (Dominique Pelle) https://github.com/vim/vim/commit/e999782e369999539a1783a7ebe4eadcc6da28a8 --- src/nvim/strings.c | 57 +++++++++++++++---------- src/nvim/testdir/test_expr.vim | 96 ++++++++++++++++++++++++++++++++++++------ src/nvim/version.c | 2 +- 3 files changed, 119 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 47ee311bd4..e8d70f2676 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -753,6 +753,22 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) return str_l; } +// Return the representation of infinity for printf() function: +// "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF". +static const char *infinity_str(bool positive, char fmt_spec, + int force_sign, int space_for_positive) +{ + static const char *table[] = { + "-inf", "inf", "+inf", " inf", + "-INF", "INF", "+INF", " INF" + }; + int idx = positive * (1 + force_sign + force_sign * space_for_positive); + if (ASCII_ISUPPER(fmt_spec)) { + idx += 4; + } + return table[idx]; +} + /// Write formatted value to the string /// @@ -1209,10 +1225,10 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } if (fmt_spec == 'f' && abs_f > 1.0e307) { - const char inf_str[] = f < 0 ? "-inf" : "inf"; - // avoid a buffer overflow - memmove(tmp, inf_str, sizeof(inf_str) - 1); - str_arg_l = sizeof(inf_str) - 1; + STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive)); + str_arg_l = STRLEN(tmp); + zero_padding = 0; } else { format[0] = '%'; int l = 1; @@ -1231,25 +1247,22 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); format[l + 1] = NUL; - assert(l + 1 < (int)sizeof(format)); - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); - // Be consistent: Change "1.#IND" to "nan" and - // "1.#INF" to "inf". - char *s = *tmp == '-' ? tmp + 1 : tmp; - if (STRNCMP(s, "1.#INF", 6) == 0) { - STRCPY(s, "inf"); - } else if (STRNCMP(s, "1.#IND", 6) == 0) { - STRCPY(s, "nan"); - } - // Remove sign before "nan" - if (STRNCMP(tmp, "-nan", 4) == 0) { - STRCPY(tmp, "nan"); - } - // Add sign before "inf" if needed. - if (isinf(f) == -1 && STRNCMP(tmp, "inf", 3) == 0) { - STRCPY(tmp, "-inf"); + if (isnan(f)) { + // Not a number: nan or NAN + STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); + str_arg_l = 3; + zero_padding = 0; + } else if (isinf(f)) { + STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive)); + str_arg_l = STRLEN(tmp); + zero_padding = 0; + } else { + // Regular float number + assert(l + 1 < (int)sizeof(format)); + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); } if (remove_trailing_zeroes) { diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index fc804f3a15..b7eeb6d48c 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -141,7 +141,7 @@ function Test_printf_misc() call assert_equal('173', printf('%O', 123)) call assert_equal('7b', printf('%x', 123)) call assert_equal('7B', printf('%X', 123)) - call assert_equal('{', printf('%c', 123)) + call assert_equal('#', printf('%c', 123)) call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%S', 'abc')) @@ -152,13 +152,22 @@ function Test_printf_misc() call assert_equal(' 123', printf('% d', 123)) call assert_equal('-123', printf('% d', -123)) + call assert_equal('123', printf('%2d', 123)) + call assert_equal(' 123', printf('%6d', 123)) + call assert_equal('000123', printf('%06d', 123)) + call assert_equal('+00123', printf('%+06d', 123)) + call assert_equal(' 00123', printf('% 06d', 123)) + call assert_equal(' +123', printf('%+6d', 123)) + call assert_equal(' 123', printf('% 6d', 123)) + call assert_equal(' -123', printf('% 6d', -123)) + call assert_equal('+123 ', printf('%-+6d', 123)) + call assert_equal(' 123 ', printf('%- 6d', 123)) + call assert_equal('-123 ', printf('%- 6d', -123)) + call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123', printf('%2d', 123)) - call assert_equal(' 123', printf('%5d', 123)) - call assert_equal('00123', printf('%05d', 123)) call assert_equal('123 ', printf('%-5d', 123)) call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) @@ -191,20 +200,81 @@ endfunc function Test_printf_float() call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) - call assert_equal('1.23', printf('%g', 1.23)) - call assert_equal('1.23', printf('%G', 1.23)) + call assert_equal('9999999.9', printf('%g', 9999999.9)) + call assert_equal('9999999.9', printf('%G', 9999999.9)) + call assert_equal('1.00000001e7', printf('%.8g', 10000000.1)) + call assert_equal('1.00000001E7', printf('%.8G', 10000000.1)) call assert_equal('1.230000e+00', printf('%e', 1.23)) call assert_equal('1.230000E+00', printf('%E', 1.23)) call assert_equal('1.200000e-02', printf('%e', 0.012)) call assert_equal('-1.200000e-02', printf('%e', -0.012)) - call assert_equal('1.2', printf('%.1f', 1.23)) - + call assert_equal('0.33', printf('%.2f', 1.0/3.0)) + call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) + call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) + call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) + " FIXME: call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + " FIXME: call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + " FIXME: call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + " FIXME: call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + + " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) - - call assert_match('^-inf$', printf('%f', -1.0/0.0)) - - call assert_match('^nan$', printf('%f', sqrt(-1.0))) - call assert_match('^nan$', printf('%f', 0.0/0.0)) + call assert_equal('-inf', printf('%f', -1.0/0.0)) + call assert_equal('inf', printf('%g', 1.0/0.0)) + call assert_equal('-inf', printf('%g', -1.0/0.0)) + call assert_equal('inf', printf('%e', 1.0/0.0)) + call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%E', -1.0/0.0)) + call assert_equal('INF', printf('%E', 1.0/0.0)) + call assert_equal('-INF', printf('%G', -1.0/0.0)) + call assert_equal('+inf', printf('%+f', 1.0/0.0)) + call assert_equal('-inf', printf('%+f', -1.0/0.0)) + call assert_equal(' inf', printf('% f', 1.0/0.0)) + call assert_equal(' inf', printf('%6f', 1.0/0.0)) + call assert_equal(' -inf', printf('%6f', -1.0/0.0)) + call assert_equal(' inf', printf('%6g', 1.0/0.0)) + call assert_equal(' -inf', printf('%6g', -1.0/0.0)) + call assert_equal(' +inf', printf('%+6f', 1.0/0.0)) + call assert_equal(' inf', printf('% 6f', 1.0/0.0)) + call assert_equal(' +inf', printf('%+06f', 1.0/0.0)) + call assert_equal('inf ', printf('%-6f', 1.0/0.0)) + call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) + call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) + call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('INF ', printf('%-6G', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) + call assert_equal('INF ', printf('%-6E', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6E', -1.0/0.0)) + call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) + call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) + + " Float zero can be signed. + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) + + " Float nan (not a number) has no sign. + call assert_equal('nan', printf('%f', sqrt(-1.0))) + call assert_equal('nan', printf('%f', 0.0/0.0)) + call assert_equal('nan', printf('%f', -0.0/0.0)) + call assert_equal('nan', printf('%g', 0.0/0.0)) + call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%G', 0.0/0.0)) + call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%G', -0.0/0.0)) + call assert_equal('NAN', printf('%E', -0.0/0.0)) + call assert_equal(' nan', printf('%6f', 0.0/0.0)) + call assert_equal(' nan', printf('%06f', 0.0/0.0)) + call assert_equal('nan ', printf('%-6f', 0.0/0.0)) + call assert_equal('nan ', printf('%- 6f', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%s', -0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', 0.0/0.0)) + call assert_equal("str2float('nan')", printf('%S', -0.0/0.0)) call assert_fails('echo printf("%f", "a")', 'E807:') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 5479e9f91f..09e27d210b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -161,7 +161,7 @@ static int included_patches[] = { // 2283, // 2282 NA // 2281 NA - // 2280, + 2280, 2279, // 2278 NA 2277, -- cgit From cad9a76be28f2ee3bd2c48ae959243aa7cfdb02a Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 7 Mar 2017 21:05:02 +0100 Subject: vim-patch:7.4.2291 Problem: printf() handles floats wrong when there is a sign. Solution: Fix placing the sign. Add tests. (Dominique Pelle) https://github.com/vim/vim/commit/04186095346daa60e82e981dad114de2b641d672 --- src/nvim/strings.c | 53 ++++++++++++++++++------------- src/nvim/testdir/test_expr.vim | 72 ++++++++++++++++++++++++++++++++++-------- src/nvim/version.c | 2 +- 3 files changed, 90 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e8d70f2676..a2052423e2 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -925,7 +925,6 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; - case 'F': fmt_spec = 'f'; break; default: break; } @@ -1202,6 +1201,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } case 'f': + case 'F': case 'e': case 'E': case 'g': @@ -1217,37 +1217,19 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, if (fmt_spec == 'g' || fmt_spec == 'G') { // can't use %g directly, cause it prints "1.0" as "1" if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { - fmt_spec = 'f'; + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; } else { fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; } remove_trailing_zeroes = true; } - if (fmt_spec == 'f' && abs_f > 1.0e307) { + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0e307) { STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, force_sign, space_for_positive)); str_arg_l = STRLEN(tmp); zero_padding = 0; } else { - format[0] = '%'; - int l = 1; - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; - - // make sure we don't get more digits than we have room for - if (fmt_spec == 'f' && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += snprintf(format + 1, sizeof(format) - 1, ".%d", - (int)precision); - } - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; - if (isnan(f)) { // Not a number: nan or NAN STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); @@ -1259,6 +1241,27 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, str_arg_l = STRLEN(tmp); zero_padding = 0; } else { + format[0] = '%'; + int l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; + + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); + } + if (precision > max_prec) { + precision = max_prec; + } + l += snprintf(format + l, sizeof(format) - 1, ".%d", + (int)precision); + } + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); + format[l + 1] = NUL; + // Regular float number assert(l + 1 < (int)sizeof(format)); str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); @@ -1270,7 +1273,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, char *tp; // using %g or %G: remove superfluous zeroes - if (fmt_spec == 'f') { + if (fmt_spec == 'f' || fmt_spec == 'F') { tp = tmp + str_arg_l - 1; } else { tp = (char *)vim_strchr((char_u *)tmp, @@ -1312,6 +1315,12 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } } } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) { + // Padding 0's should be inserted after the sign. + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } str_arg = tmp; break; } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index b7eeb6d48c..ccbdde8244 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -141,7 +141,7 @@ function Test_printf_misc() call assert_equal('173', printf('%O', 123)) call assert_equal('7b', printf('%x', 123)) call assert_equal('7B', printf('%X', 123)) - call assert_equal('#', printf('%c', 123)) + call assert_equal('{', printf('%c', 123)) call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%S', 'abc')) @@ -160,21 +160,44 @@ function Test_printf_misc() call assert_equal(' +123', printf('%+6d', 123)) call assert_equal(' 123', printf('% 6d', 123)) call assert_equal(' -123', printf('% 6d', -123)) + + " Test left adjusted. + call assert_equal('123 ', printf('%-6d', 123)) call assert_equal('+123 ', printf('%-+6d', 123)) call assert_equal(' 123 ', printf('%- 6d', 123)) call assert_equal('-123 ', printf('%- 6d', -123)) + call assert_equal(' 00123', printf('%7.5d', 123)) + call assert_equal(' -00123', printf('%7.5d', -123)) + call assert_equal(' +00123', printf('%+7.5d', 123)) + " Precision field should not be used when combined with %0 + call assert_equal(' 00123', printf('%07.5d', 123)) + call assert_equal(' -00123', printf('%07.5d', -123)) + + call assert_equal(' 123', printf('%*d', 5, 123)) + call assert_equal('123 ', printf('%*d', -5, 123)) call assert_equal('00123', printf('%.*d', 5, 123)) call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) - call assert_equal('123 ', printf('%-5d', 123)) + " Simple quote (thousand grouping char) is ignored. + call assert_equal('+00123456', printf("%+'09d", 123456)) + + " Unrecognized format specifier kept as-is. + call assert_equal('_123', printf("%_%d", 123)) + + " Test alternate forms. call assert_equal('0x7b', printf('%#x', 123)) call assert_equal('0X7B', printf('%#X', 123)) call assert_equal('0173', printf('%#o', 123)) call assert_equal('0173', printf('%#O', 123)) call assert_equal('abc', printf('%#s', 'abc')) call assert_equal('abc', printf('%#S', 'abc')) + call assert_equal(' 0173', printf('%#6o', 123)) + call assert_equal(' 00173', printf('%#6.5o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal(' 0173', printf('%#6.2o', 123)) + call assert_equal('0173', printf('%#2.2o', 123)) call assert_equal(' 00123', printf('%6.5d', 123)) call assert_equal(' 0007b', printf('%6.5x', 123)) @@ -198,6 +221,7 @@ function Test_printf_misc() endfunc function Test_printf_float() + call assert_equal('1.000000', printf('%f', 1)) call assert_equal('1.230000', printf('%f', 1.23)) call assert_equal('1.230000', printf('%F', 1.23)) call assert_equal('9999999.9', printf('%g', 9999999.9)) @@ -212,10 +236,31 @@ function Test_printf_float() call assert_equal(' 0.33', printf('%6.2f', 1.0/3.0)) call assert_equal(' -0.33', printf('%6.2f', -1.0/3.0)) call assert_equal('000.33', printf('%06.2f', 1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) - " FIXME: call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) - " FIXME: call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) - " FIXME: call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2f', -1.0/3.0)) + call assert_equal('-00.33', printf('%+06.2f', -1.0/3.0)) + call assert_equal('+00.33', printf('%+06.2f', 1.0/3.0)) + call assert_equal(' 00.33', printf('% 06.2f', 1.0/3.0)) + call assert_equal('000.33', printf('%06.2g', 1.0/3.0)) + call assert_equal('-00.33', printf('%06.2g', -1.0/3.0)) + call assert_equal('0.33', printf('%3.2f', 1.0/3.0)) + call assert_equal('003.33e-01', printf('%010.2e', 1.0/3.0)) + call assert_equal(' 03.33e-01', printf('% 010.2e', 1.0/3.0)) + call assert_equal('+03.33e-01', printf('%+010.2e', 1.0/3.0)) + call assert_equal('-03.33e-01', printf('%010.2e', -1.0/3.0)) + + " When precision is 0, the dot should be omitted. + call assert_equal(' 2', printf('%3.f', 7.0/3.0)) + call assert_equal(' 2', printf('%3.g', 7.0/3.0)) + call assert_equal(' 2e+00', printf('%7.e', 7.0/3.0)) + + " Float zero can be signed. + call assert_equal('+0.000000', printf('%+f', 0.0)) + call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) + call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) + call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) + call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) " Float infinity can be signed. call assert_equal('inf', printf('%f', 1.0/0.0)) @@ -224,6 +269,8 @@ function Test_printf_float() call assert_equal('-inf', printf('%g', -1.0/0.0)) call assert_equal('inf', printf('%e', 1.0/0.0)) call assert_equal('-inf', printf('%e', -1.0/0.0)) + call assert_equal('INF', printf('%F', 1.0/0.0)) + call assert_equal('-INF', printf('%F', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) call assert_equal('-INF', printf('%E', -1.0/0.0)) call assert_equal('INF', printf('%E', 1.0/0.0)) @@ -242,6 +289,9 @@ function Test_printf_float() call assert_equal('-inf ', printf('%-6f', -1.0/0.0)) call assert_equal('+inf ', printf('%-+6f', 1.0/0.0)) call assert_equal(' inf ', printf('%- 6f', 1.0/0.0)) + call assert_equal('-INF ', printf('%-6F', -1.0/0.0)) + call assert_equal('+INF ', printf('%-+6F', 1.0/0.0)) + call assert_equal(' INF ', printf('%- 6F', 1.0/0.0)) call assert_equal('INF ', printf('%-6G', 1.0/0.0)) call assert_equal('-INF ', printf('%-6G', -1.0/0.0)) call assert_equal('INF ', printf('%-6E', 1.0/0.0)) @@ -249,22 +299,16 @@ function Test_printf_float() call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) - " Float zero can be signed. - call assert_equal('0.000000', printf('%f', 1.0/(1.0/0.0))) - call assert_equal('-0.000000', printf('%f', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%s', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%s', 1.0/(-1.0/0.0))) - call assert_equal('0.0', printf('%S', 1.0/(1.0/0.0))) - call assert_equal('-0.0', printf('%S', 1.0/(-1.0/0.0))) - " Float nan (not a number) has no sign. call assert_equal('nan', printf('%f', sqrt(-1.0))) call assert_equal('nan', printf('%f', 0.0/0.0)) call assert_equal('nan', printf('%f', -0.0/0.0)) call assert_equal('nan', printf('%g', 0.0/0.0)) call assert_equal('nan', printf('%e', 0.0/0.0)) + call assert_equal('NAN', printf('%F', 0.0/0.0)) call assert_equal('NAN', printf('%G', 0.0/0.0)) call assert_equal('NAN', printf('%E', 0.0/0.0)) + call assert_equal('NAN', printf('%F', -0.0/0.0)) call assert_equal('NAN', printf('%G', -0.0/0.0)) call assert_equal('NAN', printf('%E', -0.0/0.0)) call assert_equal(' nan', printf('%6f', 0.0/0.0)) diff --git a/src/nvim/version.c b/src/nvim/version.c index 09e27d210b..dd4adae213 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -150,7 +150,7 @@ static int included_patches[] = { 2294, // 2293, 2292, - // 2291, + 2291, // 2290 NA // 2289 NA // 2288 NA -- cgit From c6c8e1e8cc4ea6fc98187c0bce8875cf82278ba5 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 21 Mar 2017 07:34:08 +0100 Subject: version: Mark patch 2221 as applied. It was implemented as a part of #3916. --- src/nvim/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/version.c b/src/nvim/version.c index dd4adae213..4bc108dd2d 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -220,7 +220,7 @@ static int included_patches[] = { // 2224, 2223, 2222, - // 2221, + 2221, 2220, 2219, // 2218 NA -- cgit From 66259e4c498dd3b5278aeea16cc3a71ed83f1b76 Mon Sep 17 00:00:00 2001 From: Jack Bracewell Date: Tue, 21 Mar 2017 10:11:32 +0000 Subject: vim-patch:7.4.2293 (#6307) The original patch makes all the modeline comments consistent, but these have been removed in the neovim source. However there as a correction of a comment included in the patch that we can use. https://github.com/vim/vim/commit/edf3f97ae2af024708ebb4ac614227327033ca47 --- src/nvim/eval.c | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d4daffb469..7eb149476f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4196,7 +4196,7 @@ static int eval7( // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; - // Skip '!' and '-' characters. They are handled later. + // Skip '!', '-' and '+' characters. They are handled later. start_leader = *arg; while (**arg == '!' || **arg == '-' || **arg == '+') { *arg = skipwhite(*arg + 1); diff --git a/src/nvim/version.c b/src/nvim/version.c index 8fd0fce329..bf13e52be4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -148,7 +148,7 @@ static int included_patches[] = { // 2296, 2295, 2294, - // 2293, + 2293, 2292, // 2291, // 2290 NA -- cgit From 26d7757ccbc57125a3dfe3b2cad0f07a2e567df1 Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 17 Mar 2017 23:09:15 +0800 Subject: vim-patch:7.4.2256 Problem: Coverity complains about null pointer check. Solution: Remove wrong and superfluous error check. https://github.com/vim/vim/commit/db249f26edf7a5f88d1f4468d08ec5b84f5ab7ad --- src/nvim/eval.c | 5 ++++- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d4daffb469..5de999f175 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4860,7 +4860,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) } *name = NUL; - *arg = p + 1; + if (*p != NUL) { // just in case + p++; + } + *arg = p; return OK; } diff --git a/src/nvim/version.c b/src/nvim/version.c index b9286c2a4e..5cdd0d96fe 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -185,7 +185,7 @@ static int included_patches[] = { // 2259, // 2258 NA // 2257 NA - // 2256, + 2256, 2255, // 2254 NA // 2253 NA -- cgit From 7418adc14375161b504e78c77c5c4e532016902c Mon Sep 17 00:00:00 2001 From: lonerover Date: Tue, 21 Mar 2017 21:36:31 +0800 Subject: move.c: add cursor adjustment for scrolloff (#6319) --- src/nvim/move.c | 1 + src/nvim/testdir/test_normal.vim | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/move.c b/src/nvim/move.c index 4c3f82bc16..4feabf624b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,6 +1877,7 @@ int onepage(int dir, long count) } } foldAdjustCursor(); + cursor_correct(); check_cursor_col(); if (retval == OK) { beginline(BL_SOL | BL_FIX); diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 29a7c1edd8..a22dca35cc 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -433,7 +433,6 @@ func! Test_normal13_help() endfunc func! Test_normal14_page() - throw "skipped: Nvim regression: CTRL-F with 'scrolloff'" " basic test for Ctrl-F and Ctrl-B call Setup_NewWindow() exe "norm! \" -- cgit From 6baa669c10518b8751904a31899feb897fb7c995 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:07:00 +0100 Subject: vim-patch:7.4.2164 (#6326) Problem: It is not possible to use plugins in an "after" directory to tune the behavior of a package. Solution: First load plugins from non-after directories, then packages and finally plugins in after directories. Reset 'loadplugins' before executing --cmd arguments. https://github.com/vim/vim/commit/66459b7c98c67f8a9d39de8f08e8e8f1fca0e359 vim-patch:7.4.2172 vim-patch:7.4.2169 vim-patch:7.4.2177 vim-patch:7.4.2178 vim-patch:7.4.2184 vim-patch:8.0.0050 vim-patch:8.0.0105 vim-patch:8.0.0400 vim-patch:8.0.0405 Closes #6034 --- src/nvim/ex_cmds2.c | 16 ++- src/nvim/main.c | 13 ++- src/nvim/testdir/setup.vim | 3 +- src/nvim/testdir/shared.vim | 214 ++++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_startup.vim | 200 +++++++++++++++++++++++++++++++++++ src/nvim/version.c | 12 +-- src/nvim/vim.h | 2 + 7 files changed, 449 insertions(+), 11 deletions(-) create mode 100644 src/nvim/testdir/shared.vim create mode 100644 src/nvim/testdir/test_startup.vim (limited to 'src') diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 36f50ae7a2..9fc4ef2a02 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2361,12 +2361,25 @@ int do_in_path(char_u *path, char_u *name, int flags, while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); + size_t buflen = STRLEN(buf); + + // Skip after or non-after directories. + if (flags & (DIP_NOAFTER | DIP_AFTER)) { + bool is_after = buflen >= 5 + && STRCMP(buf + buflen - 5, "after") == 0; + + if ((is_after && (flags & DIP_NOAFTER)) + || (!is_after && (flags & DIP_AFTER))) { + continue; + } + } + if (name == NULL) { (*callback)(buf, (void *)&cookie); if (!did_one) { did_one = (cookie == NULL); } - } else if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL) { + } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { add_pathsep((char *)buf); tail = buf + STRLEN(buf); @@ -2597,6 +2610,7 @@ static bool did_source_packages = false; // ":packloadall" // Find plugins in the package directories and source them. +// "eap" is NULL when invoked during startup. void ex_packloadall(exarg_T *eap) { if (!did_source_packages || (eap != NULL && eap->forceit)) { diff --git a/src/nvim/main.c b/src/nvim/main.c index 4416bd067c..7b1c912f4b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -326,6 +326,12 @@ int main(int argc, char **argv) do_cmdline_cmd("augroup END"); #undef PROTO + // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. + // Allows for setting 'loadplugins' there. + if (params.use_vimrc != NULL && strcmp(params.use_vimrc, "NONE") == 0) { + p_lpl = false; + } + /* Execute --cmd arguments. */ exe_pre_commands(¶ms); @@ -1268,11 +1274,14 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL); // NOLINT + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT TIME_MSG("loading plugins"); ex_packloadall(NULL); TIME_MSG("loading packages"); + + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); } } @@ -1708,8 +1717,6 @@ static void source_startup_scripts(const mparm_T *const parmp) if (parmp->use_vimrc != NULL) { if (strcmp(parmp->use_vimrc, "NONE") == 0 || strcmp(parmp->use_vimrc, "NORC") == 0) { - if (parmp->use_vimrc[2] == 'N') - p_lpl = false; // don't load plugins either } else { if (do_source((char_u *)parmp->use_vimrc, FALSE, DOSO_NONE) != OK) EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 05257d566d..06f2199214 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -4,8 +4,9 @@ set noruler set noshowcmd set belloff= -" Make sure 'runtimepath' does not include $HOME. +" Make sure 'runtimepath' and 'packpath' does not include $HOME. set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after +let &packpath = &rtp " Make sure $HOME does not get read or written. let $HOME = '/does/not/exist' diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim new file mode 100644 index 0000000000..784e4a0a02 --- /dev/null +++ b/src/nvim/testdir/shared.vim @@ -0,0 +1,214 @@ +" Functions shared by several tests. + +" Get the name of the Python executable. +" Also keeps it in s:python. +func PythonProg() + " This test requires the Python command to run the test server. + " This most likely only works on Unix and Windows. + if has('unix') + " We also need the job feature or the pkill command to make sure the server + " can be stopped. + if !(executable('python') && (has('job') || executable('pkill'))) + return '' + endif + let s:python = 'python' + elseif has('win32') + " Use Python Launcher for Windows (py.exe) if available. + if executable('py.exe') + let s:python = 'py.exe' + elseif executable('python.exe') + let s:python = 'python.exe' + else + return '' + endif + else + return '' + endif + return s:python +endfunc + +" Run "cmd". Returns the job if using a job. +func RunCommand(cmd) + let job = 0 + if has('job') + let job = job_start(a:cmd, {"stoponexit": "hup"}) + call job_setoptions(job, {"stoponexit": "kill"}) + elseif has('win32') + exe 'silent !start cmd /c start "test_channel" ' . a:cmd + else + exe 'silent !' . a:cmd . '&' + endif + return job +endfunc + +" Read the port number from the Xportnr file. +func GetPort() + let l = [] + for i in range(200) + try + let l = readfile("Xportnr") + catch + endtry + if len(l) >= 1 + break + endif + sleep 10m + endfor + call delete("Xportnr") + + if len(l) == 0 + " Can't make the connection, give up. + return 0 + endif + return l[0] +endfunc + +" Run a Python server for "cmd" and call "testfunc". +" Always kills the server before returning. +func RunServer(cmd, testfunc, args) + " The Python program writes the port number in Xportnr. + call delete("Xportnr") + + if len(a:args) == 1 + let arg = ' ' . a:args[0] + else + let arg = '' + endif + let pycmd = s:python . " " . a:cmd . arg + + try + let g:currentJob = RunCommand(pycmd) + + " Wait for up to 2 seconds for the port number to be there. + let port = GetPort() + if port == 0 + call assert_false(1, "Can't start " . a:cmd) + return + endif + + call call(function(a:testfunc), [port]) + catch + call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint) + finally + call s:kill_server(a:cmd) + endtry +endfunc + +func s:kill_server(cmd) + if has('job') + if exists('g:currentJob') + call job_stop(g:currentJob) + unlet g:currentJob + endif + elseif has('win32') + let cmd = substitute(a:cmd, ".py", '', '') + call system('taskkill /IM ' . s:python . ' /T /F /FI "WINDOWTITLE eq ' . cmd . '"') + else + call system("pkill -f " . a:cmd) + endif +endfunc + +" Wait for up to a second for "expr" to become true. +" Return time slept in milliseconds. With the +reltime feature this can be +" more than the actual waiting time. Without +reltime it can also be less. +func WaitFor(expr) + " using reltime() is more accurate, but not always available + if has('reltime') + let start = reltime() + else + let slept = 0 + endif + for i in range(100) + try + if eval(a:expr) + if has('reltime') + return float2nr(reltimefloat(reltime(start)) * 1000) + endif + return slept + endif + catch + endtry + if !has('reltime') + let slept += 10 + endif + sleep 10m + endfor + return 1000 +endfunc + +" Wait for up to a given milliseconds. +" With the +timers feature this waits for key-input by getchar(), Resume() +" feeds key-input and resumes process. Return time waited in milliseconds. +" Without +timers it uses simply :sleep. +func Standby(msec) + if has('timers') + let start = reltime() + let g:_standby_timer = timer_start(a:msec, function('s:feedkeys')) + call getchar() + return float2nr(reltimefloat(reltime(start)) * 1000) + else + execute 'sleep ' a:msec . 'm' + return a:msec + endif +endfunc + +func Resume() + if exists('g:_standby_timer') + call timer_stop(g:_standby_timer) + call s:feedkeys(0) + unlet g:_standby_timer + endif +endfunc + +func s:feedkeys(timer) + call feedkeys('x', 'nt') +endfunc + +" Get the command to run Vim, with -u NONE and --headless arguments. +" Returns an empty string on error. +func GetVimCommand() + let cmd = v:progpath + let cmd = substitute(cmd, '-u \f\+', '-u NONE', '') + if cmd !~ '-u NONE' + let cmd = cmd . ' -u NONE' + endif + let cmd .= ' --headless -i NONE' + let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '') + return cmd +endfunc + +" Run Vim, using the "vimcmd" file and "-u NORC". +" "before" is a list of Vim commands to be executed before loading plugins. +" "after" is a list of Vim commands to be executed after loading plugins. +" Plugins are not loaded, unless 'loadplugins' is set in "before". +" Return 1 if Vim could be executed. +func RunVim(before, after, arguments) + return RunVimPiped(a:before, a:after, a:arguments, '') +endfunc + +func RunVimPiped(before, after, arguments, pipecmd) + let $NVIM_LOG_FILE='Xnvim.log' + let cmd = GetVimCommand() + if cmd == '' + return 0 + endif + let args = '' + if len(a:before) > 0 + call writefile(a:before, 'Xbefore.vim') + let args .= ' --cmd "so Xbefore.vim"' + endif + if len(a:after) > 0 + call writefile(a:after, 'Xafter.vim') + let args .= ' -S Xafter.vim' + endif + + exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments + + if len(a:before) > 0 + call delete('Xbefore.vim') + endif + if len(a:after) > 0 + call delete('Xafter.vim') + endif + return 1 +endfunc diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim new file mode 100644 index 0000000000..f78d92f3ff --- /dev/null +++ b/src/nvim/testdir/test_startup.vim @@ -0,0 +1,200 @@ +" Tests for startup. + +source shared.vim + +" Check that loading startup.vim works. +func Test_startup_script() + throw 'skipped: Nvim does not need defaults.vim' + set compatible + source $VIMRUNTIME/defaults.vim + + call assert_equal(0, &compatible) +endfunc + +" Verify the order in which plugins are loaded: +" 1. plugins in non-after directories +" 2. packages +" 3. plugins in after directories +func Test_after_comes_later() + if !has('packages') + return + endif + let before = [ + \ 'set nocp viminfo+=nviminfo', + \ 'set guioptions+=M', + \ 'let $HOME = "/does/not/exist"', + \ 'set loadplugins', + \ 'set rtp=Xhere,Xafter', + \ 'set packpath=Xhere,Xafter', + \ 'set nomore', + \ ] + let after = [ + \ 'redir! > Xtestout', + \ 'scriptnames', + \ 'redir END', + \ 'quit', + \ ] + call mkdir('Xhere/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/plugin/here.vim') + call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') + call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') + + call mkdir('Xafter/plugin', 'p') + call writefile(['let done = 1'], 'Xafter/plugin/later.vim') + + if RunVim(before, after, '') + + let lines = readfile('Xtestout') + let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] + let found = [] + for line in lines + for one in expected + if line =~ one + call add(found, one) + endif + endfor + endfor + call assert_equal(expected, found) + endif + + call delete('Xtestout') + call delete('Xhere', 'rf') + call delete('Xafter', 'rf') +endfunc + +func Test_help_arg() + if !has('unix') && has('gui') + " this doesn't work with gvim on MS-Windows + return + endif + if RunVim([], [], '--help >Xtestout') + let lines = readfile('Xtestout') + call assert_true(len(lines) > 20) + call assert_match('Usage:', lines[0]) + + " check if couple of lines are there + let found = [] + for line in lines + if line =~ '-R.*Readonly mode' + call add(found, 'Readonly mode') + endif + " Watch out for a second --version line in the Gnome version. + if line =~ '--version.*Print version information and exit' + call add(found, "--version") + endif + endfor + call assert_equal(['--version'], found) + endif + call delete('Xtestout') +endfunc + +func Test_compatible_args() + throw "skipped: Nvim is always 'nocompatible'" + let after = [ + \ 'call writefile([string(&compatible)], "Xtestout")', + \ 'set viminfo+=nviminfo', + \ 'quit', + \ ] + if RunVim([], after, '-C') + let lines = readfile('Xtestout') + call assert_equal('1', lines[0]) + endif + + if RunVim([], after, '-N') + let lines = readfile('Xtestout') + call assert_equal('0', lines[0]) + endif + + call delete('Xtestout') +endfunc + +func Test_file_args() + let after = [ + \ 'call writefile(argv(), "Xtestout")', + \ 'qall', + \ ] + if RunVim([], after, '') + let lines = readfile('Xtestout') + call assert_equal(0, len(lines)) + endif + + if RunVim([], after, 'one') + let lines = readfile('Xtestout') + call assert_equal(1, len(lines)) + call assert_equal('one', lines[0]) + endif + + if RunVim([], after, 'one two three') + let lines = readfile('Xtestout') + call assert_equal(3, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + call assert_equal('three', lines[2]) + endif + + if RunVim([], after, 'one -c echo two') + let lines = readfile('Xtestout') + call assert_equal(2, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('two', lines[1]) + endif + + if RunVim([], after, 'one -- -c echo two') + let lines = readfile('Xtestout') + call assert_equal(4, len(lines)) + call assert_equal('one', lines[0]) + call assert_equal('-c', lines[1]) + call assert_equal('echo', lines[2]) + call assert_equal('two', lines[3]) + endif + + call delete('Xtestout') +endfunc + +func Test_startuptime() + if !has('startuptime') + return + endif + let after = ['qall'] + if RunVim([], after, '--startuptime Xtestout one') + let lines = readfile('Xtestout') + let expected = ['parsing arguments', 'inits 3', 'opening buffers'] + let found = [] + for line in lines + for exp in expected + if line =~ exp + call add(found, exp) + endif + endfor + endfor + call assert_equal(expected, found) + endif + call delete('Xtestout') +endfunc + +func Test_read_stdin() + let after = [ + \ 'write Xtestout', + \ 'quit!', + \ ] + if RunVimPiped([], after, '-', 'echo something | ') + let lines = readfile('Xtestout') + " MS-Windows adds a space after the word + call assert_equal(['something'], split(lines[0])) + endif + call delete('Xtestout') +endfunc + +func Test_progpath() + " Tests normally run with "./vim" or "../vim", these must have been expanded + " to a full path. + if has('unix') + call assert_equal('/', v:progpath[0]) + elseif has('win32') + call assert_equal(':', v:progpath[1]) + call assert_match('[/\\]', v:progpath[2]) + endif + + " Only expect "vim" to appear in v:progname. + call assert_match('vim\c', v:progname) +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index bf13e52be4..1d4239b215 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -257,27 +257,27 @@ static int included_patches[] = { 2187, // 2186 NA 2185, - // 2184, + 2184, 2183, // 2182 NA // 2181, // 2180, // 2179, - // 2178, - // 2177, + 2178, + 2177, // 2176 NA 2175, 2174, // 2173, - // 2172, + 2172, // 2171, // 2170, - // 2169, + 2169, // 2168 NA // 2167 NA // 2166 NA // 2165, - // 2164, + 2164, 2163, 2162, // 2161, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 458d23fcad..87e9889465 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -325,6 +325,8 @@ enum { #define DIP_START 0x08 // also use "start" directory in 'packpath' #define DIP_OPT 0x10 // also use "opt" directory in 'packpath' #define DIP_NORTP 0x20 // do not use 'runtimepath' +#define DIP_NOAFTER 0x40 // skip "after" directories +#define DIP_AFTER 0x80 // only use "after" directories // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 -- cgit From 3e33025133e1e77c7ad3b8670e06175b9757cf30 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Tue, 21 Mar 2017 22:48:01 +0100 Subject: strings: Fix problems found during code review --- src/nvim/strings.c | 71 +++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index a2052423e2..7ec2ea13f7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1224,49 +1224,44 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, remove_trailing_zeroes = true; } - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0e307) { - STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive)); - str_arg_l = STRLEN(tmp); + if (isinf(f) + || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { + xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive), + sizeof(tmp)); + str_arg_l = strlen(tmp); + zero_padding = 0; + } else if (isnan(f)) { + // Not a number: nan or NAN + memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); + str_arg_l = 3; zero_padding = 0; } else { - if (isnan(f)) { - // Not a number: nan or NAN - STRCPY(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan"); - str_arg_l = 3; - zero_padding = 0; - } else if (isinf(f)) { - STRCPY(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive)); - str_arg_l = STRLEN(tmp); - zero_padding = 0; - } else { - format[0] = '%'; - int l = 1; - if (force_sign) { - format[l++] = space_for_positive ? ' ' : '+'; - } - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; + format[0] = '%'; + size_t l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; - // make sure we don't get more digits than we have room for - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += snprintf(format + l, sizeof(format) - 1, ".%d", - (int)precision); + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); } - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; - - // Regular float number - assert(l + 1 < (int)sizeof(format)); - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); + if (precision > max_prec) { + precision = max_prec; + } + l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", + (int)precision); } + format[l] = fmt_spec == 'F' ? 'f' : fmt_spec; + format[l + 1] = NUL; + + // Regular float number + assert(l + 1 < sizeof(format)); + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); if (remove_trailing_zeroes) { int i; -- cgit From ab16c07584ab4a5bc48784dde3b9b46d30e5e4a0 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:28:03 +0800 Subject: vim-patch:7.4.2283 Problem: Part of ":oldfiles" command isn't cleared. (Lifepillar) Solution: Clear the rest of the line. (closes 1018) https://github.com/vim/vim/commit/885c00eabe6d1fd757d4f0eb531ad3a15a35ec04 --- src/nvim/eval.c | 1 + src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7eb149476f..e11b460edf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -23628,6 +23628,7 @@ void ex_oldfiles(exarg_T *eap) msg_outnum(++nr); MSG_PUTS(": "); msg_outtrans(get_tv_string(&li->li_tv)); + msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ os_breakcheck(); diff --git a/src/nvim/version.c b/src/nvim/version.c index 39886aa9d9..e060724d81 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -158,7 +158,7 @@ static int included_patches[] = { // 2286 NA // 2285 NA 2284, - // 2283, + 2283, // 2282 NA // 2281 NA 2280, -- cgit From 56e400d800b9eb6c89ea8336c50d2a61cc8fd18b Mon Sep 17 00:00:00 2001 From: John Szakmeister Date: Wed, 22 Mar 2017 03:49:45 -0400 Subject: vim_vsnprintf: fix conversion warning #6333 Re-apply fix from #6311 which was accidentally regressed in #6231. --- src/nvim/strings.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 7ec2ea13f7..267832ed2d 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1255,7 +1255,9 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", (int)precision); } - format[l] = fmt_spec == 'F' ? 'f' : fmt_spec; + + // Cast to char to avoid a conversion warning on Ubuntu 12.04. + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); format[l + 1] = NUL; // Regular float number -- cgit From c554b53040e9fea2dce343eb11178a3352d5e8da Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:34:04 +0800 Subject: vim-patch:7.4.2296 Problem: No tests for :undolist and "U" command. Solution: Add tests. (Dominique Pelle) https://github.com/vim/vim/commit/c628fdcd46e93c308f742efdf54248695960e290 --- src/nvim/testdir/test_undo.vim | 33 +++++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index fc61d1f223..9ff73fd870 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -131,6 +131,39 @@ func Test_undo_del_chars() close! endfunc +func Test_undolist() + new + set ul=100 + + let a=execute('undolist') + call assert_equal("\nNothing to undo", a) + + " 1 leaf (2 changes). + call feedkeys('achange1', 'xt') + call feedkeys('achange2', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 .*$", a) + + " 2 leaves. + call feedkeys('u', 'xt') + call feedkeys('achange3\', 'xt') + let a=execute('undolist') + call assert_match("^\nnumber changes when *saved\n *2 *2 *.*\n *3 *2 .*$", a) + close! +endfunc + +func Test_U_command() + new + set ul=100 + call feedkeys("achange1\", 'xt') + call feedkeys("achange2\", 'xt') + norm! U + call assert_equal('', getline(1)) + norm! U + call assert_equal('change1change2', getline(1)) + close! +endfunc + func Test_undojoin() new call feedkeys("Goaaaa\", 'xt') diff --git a/src/nvim/version.c b/src/nvim/version.c index e060724d81..1f2c8d12f5 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -145,7 +145,7 @@ static int included_patches[] = { 2299, // 2298 NA // 2297 NA - // 2296, + 2296, 2295, 2294, 2293, -- cgit From 114a18b93588c7d5238fe66a77dc0f94a2ba5500 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 08:37:02 +0800 Subject: vim-patch:7.4.2303 Problem: When using "is" the mode isn't always updated. Solution: Redraw the command line. (Christian Brabandt) https://github.com/vim/vim/commit/779f2fc3a7468e273897d2fd0672315812a2e3da --- src/nvim/search.c | 3 ++- src/nvim/version.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index ba6c4e6548..75862e1136 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3028,7 +3028,8 @@ extend: ++curwin->w_cursor.col; VIsual = start_pos; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_cmdline = true; // show mode later + redraw_curbuf_later(INVERTED); // update the inversion } else { /* include a newline after the sentence, if there is one */ if (incl(&curwin->w_cursor) == -1) diff --git a/src/nvim/version.c b/src/nvim/version.c index 1f2c8d12f5..ccd7fd21d9 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -138,7 +138,7 @@ static int included_patches[] = { // 2306, 2305, // 2304 NA - // 2303, + 2303, // 2302 NA // 2301 NA 2300, -- cgit From a1732b46abe48541f380d6a605fa7529c9a05da1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 14:49:37 +0100 Subject: terminal: Avoid invalid cursor col (#6265) Patch-by: oni-link Closes #6203 https://s3.amazonaws.com/archive.travis-ci.org/jobs/206794197/log.txt References #3161 [ RUN ] ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua @ 199: terminal buffer term_close() use-after-free #4393 ./test/functional/helpers.lua:187: attempt to perform arithmetic on local 'written' (a nil value) stack traceback: ./test/functional/helpers.lua:187: in function 'nvim_feed' ./test/functional/helpers.lua:329: in function 'execute' ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua:206: in function <...d/neovim/neovim/test/functional/terminal/buffer_spec.lua:199> [ ERROR ] ...d/neovim/neovim/test/functional/terminal/buffer_spec.lua @ 199: terminal buffer term_close() use-after-free #4393 (199.47 ms) ==================== File /home/travis/build/neovim/neovim/build/log/ubsan.15466 ==================== = ================================================================= = ==15466==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000029101 at pc 0x000000ea7ba0 bp 0x7ffd5bb628c0 sp 0x7ffd5bb628b8 = READ of size 1 at 0x621000029101 thread T0 = #0 0xea7b9f in utf_head_off /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1637:7 = #1 0xeaaf53 in mb_adjustpos /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1840:16 = #2 0xeaab48 in mb_adjust_cursor /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1825:3 = #3 0x11000d0 in normal_finish_command /home/travis/build/neovim/neovim/src/nvim/normal.c:928:5 = #4 0x1077df1 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1147:3 = #5 0x16ff943 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #6 0x102d8db in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:463:3 = #7 0xdf3398 in main /home/travis/build/neovim/neovim/src/nvim/main.c:540:3 = #8 0x2b973e8b4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-2.19/csu/libc-start.c:287 = #9 0x447445 in _start (/home/travis/build/neovim/neovim/build/bin/nvim+0x447445) = = 0x621000029101 is located 1 bytes to the right of 4096-byte region [0x621000028100,0x621000029100) = allocated by thread T0 here: = #0 0x4f17b8 in malloc (/home/travis/build/neovim/neovim/build/bin/nvim+0x4f17b8) = #1 0xf1f374 in try_malloc /home/travis/build/neovim/neovim/src/nvim/memory.c:84:15 = #2 0xf1f534 in xmalloc /home/travis/build/neovim/neovim/src/nvim/memory.c:118:15 = #3 0xebe6a8 in mf_alloc_bhdr /home/travis/build/neovim/neovim/src/nvim/memfile.c:646:17 = #4 0xebc394 in mf_new /home/travis/build/neovim/neovim/src/nvim/memfile.c:297:12 = #5 0xed1368 in ml_new_data /home/travis/build/neovim/neovim/src/nvim/memline.c:2704:16 = #6 0xece6ab in ml_open /home/travis/build/neovim/neovim/src/nvim/memline.c:349:8 = #7 0x6438ad in open_buffer /home/travis/build/neovim/neovim/src/nvim/buffer.c:109:7 = #8 0xa6ec8d in do_ecmd /home/travis/build/neovim/neovim/src/nvim/ex_cmds.c:2489:24 = #9 0xb5a0f9 in do_exedit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6723:9 = #10 0xb791f8 in ex_edit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6651:3 = #11 0xb28b43 in do_one_cmd /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:2198:5 = #12 0xb077a7 in do_cmdline /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:601:20 = #13 0x10905db in nv_colon /home/travis/build/neovim/neovim/src/nvim/normal.c:4495:18 = #14 0x1077de8 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1144:3 = #15 0x16ff943 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #16 0x102d8db in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:463:3 = #17 0xdf3398 in main /home/travis/build/neovim/neovim/src/nvim/main.c:540:3 = #18 0x2b973e8b4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-2.19/csu/libc-start.c:287 = = SUMMARY: AddressSanitizer: heap-buffer-overflow /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1637:7 in utf_head_off stack traceback: ./test/helpers.lua:80: in function 'check_logs' ./test/functional/helpers.lua:639: in function <./test/functional/helpers.lua:638> [----------] 9 tests from /home/travis/build/neovim/neovim/test/functional/terminal/buffer_spec.lua (2263.12 ms total) --- src/nvim/cursor.c | 5 ++--- src/nvim/mbyte.c | 35 +++++++++++++++++++++++++++++++++++ src/nvim/normal.c | 4 +--- src/nvim/terminal.c | 1 + 4 files changed, 39 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 544bcf6ede..82f1bf0a16 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -338,9 +338,8 @@ void check_cursor_col(void) check_cursor_col_win(curwin); } -/* - * Make sure win->w_cursor.col is valid. - */ +/// Make sure win->w_cursor.col is valid. Special handling of insert-mode. +/// @see mb_check_adjust_col void check_cursor_col_win(win_T *win) { colnr_T len; diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 6a87a63b8c..7a54b0c64f 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1848,6 +1848,41 @@ void mb_adjustpos(buf_T *buf, pos_T *lp) } } +/// Checks and adjusts cursor column. Not mode-dependent. +/// @see check_cursor_col_win +/// +/// @param win Places cursor on a valid column for this window. +void mb_check_adjust_col(win_T *win) +{ + colnr_T oldcol = win->w_cursor.col; + + // Column 0 is always valid. + if (oldcol != 0) { + char_u *p = ml_get_buf(win->w_buffer, win->w_cursor.lnum, false); + colnr_T len = (colnr_T)STRLEN(p); + + // Empty line or invalid column? + if (len == 0 || oldcol < 0) { + win->w_cursor.col = 0; + } else { + // Cursor column too big for line? + if (oldcol > len) { + win->w_cursor.col = len - 1; + } + // Move the cursor to the head byte. + win->w_cursor.col -= (*mb_head_off)(p, p + win->w_cursor.col); + } + + // Reset `coladd` when the cursor would be on the right half of a + // double-wide character. + if (win->w_cursor.coladd == 1 && p[win->w_cursor.col] != TAB + && vim_isprintc((*mb_ptr2char)(p + win->w_cursor.col)) + && ptr2cells(p + win->w_cursor.col) > 1) { + win->w_cursor.coladd = 0; + } + } +} + /* * Return a pointer to the character before "*p", if there is one. */ diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7188e13436..85dc509ee6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -925,9 +925,7 @@ normal_end: checkpcmark(); // check if we moved since setting pcmark xfree(s->ca.searchbuf); - if (has_mbyte) { - mb_adjust_cursor(); - } + mb_check_adjust_col(curwin); // #6203 if (curwin->w_p_scb && s->toplevel) { validate_cursor(); // may need to update w_leftcol diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index bd925a8106..87ee8f410f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1197,6 +1197,7 @@ static void adjust_topline(Terminal *term, buf_T *buf, long added) // Ensure valid cursor for each window displaying this terminal. wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end); } + mb_check_adjust_col(wp); } } } -- cgit From 1d8356a807e306eb82f2c1a7e2d2802407c7fa6e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 22 Mar 2017 17:16:49 +0100 Subject: mbyte: remove dead code --- src/nvim/mbyte.c | 249 +++++++----------------------------------------------- src/nvim/search.c | 30 ++----- src/nvim/spell.c | 9 +- 3 files changed, 41 insertions(+), 247 deletions(-) (limited to 'src') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7a54b0c64f..0ab133a545 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -355,14 +355,13 @@ int bomb_size(void) */ void remove_bom(char_u *s) { - if (enc_utf8) { - char_u *p = s; - - while ((p = vim_strbyte(p, 0xef)) != NULL) { - if (p[1] == 0xbb && p[2] == 0xbf) - STRMOVE(p, p + 3); - else - ++p; + char_u *p = s; + + while ((p = vim_strbyte(p, 0xef)) != NULL) { + if (p[1] == 0xbb && p[2] == 0xbf) { + STRMOVE(p, p + 3); + } else { + p++; } } } @@ -388,154 +387,7 @@ int mb_get_class_buf(const char_u *p, buf_T *buf) return 2; return 1; } - if (enc_dbcs != 0 && p[0] != NUL && p[1] != NUL) - return dbcs_class(p[0], p[1]); - if (enc_utf8) - return utf_class(utf_ptr2char(p)); - return 0; -} - -/* - * Get class of a double-byte character. This always returns 3 or bigger. - * TODO: Should return 1 for punctuation. - */ -int dbcs_class(unsigned lead, unsigned trail) -{ - switch (enc_dbcs) { - /* please add classify routine for your language in here */ - - case DBCS_JPNU: /* ? */ - case DBCS_JPN: - { - /* JIS code classification */ - unsigned char lb = lead; - unsigned char tb = trail; - - /* convert process code to JIS */ - /* - * XXX: Code page identification can not use with all - * system! So, some other encoding information - * will be needed. - * In japanese: SJIS,EUC,UNICODE,(JIS) - * Note that JIS-code system don't use as - * process code in most system because it uses - * escape sequences(JIS is context depend encoding). - */ - /* assume process code is JAPANESE-EUC */ - lb &= 0x7f; - tb &= 0x7f; - /* exceptions */ - switch (lb << 8 | tb) { - case 0x2121: /* ZENKAKU space */ - return 0; - case 0x2122: /* TOU-TEN (Japanese comma) */ - case 0x2123: /* KU-TEN (Japanese period) */ - case 0x2124: /* ZENKAKU comma */ - case 0x2125: /* ZENKAKU period */ - return 1; - case 0x213c: /* prolongedsound handled as KATAKANA */ - return 13; - } - /* sieved by KU code */ - switch (lb) { - case 0x21: - case 0x22: - /* special symbols */ - return 10; - case 0x23: - /* alpha-numeric */ - return 11; - case 0x24: - /* hiragana */ - return 12; - case 0x25: - /* katakana */ - return 13; - case 0x26: - /* greek */ - return 14; - case 0x27: - /* russian */ - return 15; - case 0x28: - /* lines */ - return 16; - default: - /* kanji */ - return 17; - } - } - - case DBCS_KORU: /* ? */ - case DBCS_KOR: - { - /* KS code classification */ - unsigned char c1 = lead; - unsigned char c2 = trail; - - /* - * 20 : Hangul - * 21 : Hanja - * 22 : Symbols - * 23 : Alpha-numeric/Roman Letter (Full width) - * 24 : Hangul Letter(Alphabet) - * 25 : Roman Numeral/Greek Letter - * 26 : Box Drawings - * 27 : Unit Symbols - * 28 : Circled/Parenthesized Letter - * 29 : Hiragana/Katakana - * 30 : Cyrillic Letter - */ - - if (c1 >= 0xB0 && c1 <= 0xC8) - /* Hangul */ - return 20; - - else if (c1 >= 0xCA && c1 <= 0xFD) - /* Hanja */ - return 21; - else switch (c1) { - case 0xA1: - case 0xA2: - /* Symbols */ - return 22; - case 0xA3: - /* Alpha-numeric */ - return 23; - case 0xA4: - /* Hangul Letter(Alphabet) */ - return 24; - case 0xA5: - /* Roman Numeral/Greek Letter */ - return 25; - case 0xA6: - /* Box Drawings */ - return 26; - case 0xA7: - /* Unit Symbols */ - return 27; - case 0xA8: - case 0xA9: - if (c2 <= 0xAF) - return 25; /* Roman Letter */ - else if (c2 >= 0xF6) - return 22; /* Symbols */ - else - /* Circled/Parenthesized Letter */ - return 28; - case 0xAA: - case 0xAB: - /* Hiragana/Katakana */ - return 29; - case 0xAC: - /* Cyrillic Letter */ - return 30; - } - } - default: - break; - } - return 3; + return utf_class(utf_ptr2char(p)); } /* @@ -788,10 +640,7 @@ int mb_cptr2char_adv(char_u **pp) int c; c = (*mb_ptr2char)(*pp); - if (enc_utf8) - *pp += utf_ptr2len(*pp); - else - *pp += (*mb_ptr2len)(*pp); + *pp += utf_ptr2len(*pp); return c; } @@ -1542,36 +1391,7 @@ int utf16_to_utf8(const WCHAR *strw, char **str) */ int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) { - int i, l; - int cdiff; - int n = (int)nn; - - if (enc_utf8) { - return utf_strnicmp(s1, s2, nn, nn); - } else { - for (i = 0; i < n; i += l) { - if (s1[i] == NUL && s2[i] == NUL) /* both strings end */ - return 0; - - l = (*mb_ptr2len)(s1 + i); - if (l <= 1) { - /* Single byte: first check normally, then with ignore case. */ - if (s1[i] != s2[i]) { - cdiff = vim_tolower(s1[i]) - vim_tolower(s2[i]); - if (cdiff != 0) - return cdiff; - } - } else { - /* For non-Unicode multi-byte don't ignore case. */ - if (l > n - i) - l = n - i; - cdiff = STRNCMP(s1 + i, s2 + i, l); - if (cdiff != 0) - return cdiff; - } - } - } - return 0; + return utf_strnicmp(s1, s2, nn, nn); } /* We need to call mb_stricmp() even when we aren't dealing with a multi-byte @@ -1699,27 +1519,24 @@ int mb_off_next(char_u *base, char_u *p) int i; int j; - if (enc_utf8) { - if (*p < 0x80) /* be quick for ASCII */ - return 0; + if (*p < 0x80) { // be quick for ASCII + return 0; + } - /* Find the next character that isn't 10xx.xxxx */ - for (i = 0; (p[i] & 0xc0) == 0x80; ++i) - ; - if (i > 0) { - /* Check for illegal sequence. */ - for (j = 0; p - j > base; ++j) - if ((p[-j] & 0xc0) != 0x80) - break; - if (utf8len_tab[p[-j]] != i + j) - return 0; + // Find the next character that isn't 10xx.xxxx + for (i = 0; (p[i] & 0xc0) == 0x80; i++) {} + if (i > 0) { + // Check for illegal sequence. + for (j = 0; p - j > base; j++) { + if ((p[-j] & 0xc0) != 0x80) { + break; + } + } + if (utf8len_tab[p[-j]] != i + j) { + return 0; } - return i; } - - /* Only need to check if we're on a trail byte, it doesn't matter if we - * want the offset to the next or current character. */ - return (*mb_head_off)(base, p); + return i; } /* @@ -1762,10 +1579,10 @@ void utf_find_illegal(void) char_u *tofree = NULL; vimconv.vc_type = CONV_NONE; - if (enc_utf8 && (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT)) { - /* 'encoding' is "utf-8" but we are editing a 8-bit encoded file, - * possibly a utf-8 file with illegal bytes. Setup for conversion - * from utf-8 to 'fileencoding'. */ + if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) { + // 'encoding' is "utf-8" but we are editing a 8-bit encoded file, + // possibly a utf-8 file with illegal bytes. Setup for conversion + // from utf-8 to 'fileencoding'. convert_setup(&vimconv, p_enc, curbuf->b_p_fenc); } @@ -2279,13 +2096,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, *to++ = '?'; if ((*mb_ptr2cells)((char_u *)from) > 1) *to++ = '?'; - if (enc_utf8) - l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); - else { - l = (*mb_ptr2len)((char_u *)from); - if (l > (int)fromlen) - l = (int)fromlen; - } + l = utfc_ptr2len_len((const char_u *)from, (int)fromlen); from += l; fromlen -= l; } else if (ICONV_ERRNO != ICONV_E2BIG) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 75862e1136..8c56eda7cf 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2417,32 +2417,20 @@ static int cls(void) int c; c = gchar_cursor(); - if (p_altkeymap && c == F_BLANK) + if (p_altkeymap && c == F_BLANK) { return 0; - if (c == ' ' || c == '\t' || c == NUL) - return 0; - if (enc_dbcs != 0 && c > 0xFF) { - /* If cls_bigword, report multi-byte chars as class 1. */ - if (enc_dbcs == DBCS_KOR && cls_bigword) - return 1; - - /* process code leading/trailing bytes */ - return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); } - if (enc_utf8) { - c = utf_class(c); - if (c != 0 && cls_bigword) - return 1; - return c; + if (c == ' ' || c == '\t' || c == NUL) { + return 0; } - /* If cls_bigword is TRUE, report all non-blanks as class 1. */ - if (cls_bigword) - return 1; + c = utf_class(c); - if (vim_iswordc(c)) - return 2; - return 1; + // If cls_bigword is TRUE, report all non-blanks as class 1. + if (c != 0 && cls_bigword) { + return 1; + } + return c; } /* diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 12f982106a..3b891d998f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2623,7 +2623,7 @@ bool spell_iswordp_nmw(char_u *p, win_T *wp) // Returns true if word class indicates a word character. // Only for characters above 255. // Unicode subscript and superscript are not considered word characters. -// See also dbcs_class() and utf_class() in mbyte.c. +// See also utf_class() in mbyte.c. static bool spell_mb_isword_class(int cl, win_T *wp) { if (wp->w_s->b_cjk) @@ -2646,12 +2646,7 @@ static bool spell_iswordp_w(int *p, win_T *wp) s = p; if (*s > 255) { - if (enc_utf8) - return spell_mb_isword_class(utf_class(*s), wp); - if (enc_dbcs) - return spell_mb_isword_class( - dbcs_class((unsigned)*s >> 8, *s & 0xff), wp); - return false; + return spell_mb_isword_class(utf_class(*s), wp); } return spelltab.st_isw[*s]; } -- cgit From b9e1289819183f94afb43330a7e0d953869e2af7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 02:48:28 +0100 Subject: vim-patch:8.0.0486 Problem: Crash and endless loop when closing windows in a SessionLoadPost autocommand. Solution: Check for valid tabpage. (partly neovim/neovim#6308) https://github.com/vim/vim/commit/8c752bd6c4af54c0b7bac35a39acc2bf16015f85 Closes #6308 --- src/nvim/fileio.c | 6 ++++ src/nvim/testdir/test_autocmd.vim | 68 +++++++++++++++++++++++++++++++++++++++ src/nvim/window.c | 58 ++++++++++++++++++++++++++------- 3 files changed, 121 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 873c15ff4a..d948e20b32 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6464,6 +6464,12 @@ win_found: win_remove(curwin, NULL); aucmd_win_used = false; last_status(false); // may need to remove last status line + + if (!valid_tabpage_win(curtab)) { + // no valid window in current tabpage + close_tabpage(curtab); + } + restore_snapshot(SNAP_AUCMD_IDX, false); (void)win_comp_pos(); // recompute window positions unblock_autocmds(); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index f05a55f1aa..fe9a3dd451 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -196,3 +196,71 @@ func Test_augroup_deleted() au! VimEnter endfunc +" Closing a window might cause an endless loop +" E814 for older Vims +function Test_autocmd_bufwipe_in_SessLoadPost() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content=['set nocp noswapfile', + \ 'let v:swapchoice="e"', + \ 'augroup test_autocmd_sessionload', + \ 'autocmd!', + \ 'autocmd SessionLoadPost * 4bw!', + \ 'augroup END' + \ ] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + call assert_match('E814', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc + +" SEGV occurs in older versions. +function Test_autocmd_bufwipe_in_SessLoadPost2() + if has('win32') + throw 'Skipped: test hangs on MS-Windows' + endif + tabnew + set noswapfile + let g:bufnr=bufnr('%') + mksession! + + let content = ['set nocp noswapfile', + \ 'function! DeleteInactiveBufs()', + \ ' tabfirst', + \ ' let tabblist = []', + \ ' for i in range(1, tabpagenr(''$''))', + \ ' call extend(tabblist, tabpagebuflist(i))', + \ ' endfor', + \ ' for b in range(1, bufnr(''$''))', + \ ' if bufexists(b) && buflisted(b) && (index(tabblist, b) == -1 || bufname(b) =~# ''^$'')', + \ ' exec ''bwipeout '' . b', + \ ' endif', + \ ' endfor', + \ 'call append("1", "SessionLoadPost DONE")', + \ 'endfunction', + \ 'au SessionLoadPost * call DeleteInactiveBufs()'] + call writefile(content, 'Xvimrc') + let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + " this probably only matches on unix + if has("unix") + call assert_notmatch('Caught deadly signal SEGV', a) + endif + call assert_match('SessionLoadPost DONE', a) + + unlet! g:bufnr + set swapfile + for file in ['Session.vim', 'Xvimrc'] + call delete(file) + endfor +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 6c9d3554f1..eda3cd7810 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1714,14 +1714,10 @@ static void win_equal_rec( } } -/* - * close all windows for buffer 'buf' - */ -void -close_windows ( - buf_T *buf, - int keep_curwin /* don't close "curwin" */ -) +/// Closes all windows for buffer `buf`. +/// +/// @param keep_curwin don't close `curwin` +void close_windows(buf_T *buf, int keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); @@ -1731,9 +1727,11 @@ close_windows ( for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close(wp, FALSE); + && !(wp->w_closing || wp->w_buffer->b_closing)) { + if (win_close(wp, false) == FAIL) { + // If closing the window fails give up, to avoid looping forever. + break; + } /* Start all over, autocommands may change the window layout. */ wp = firstwin; @@ -3134,6 +3132,44 @@ bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Returns true when `tpc` is valid and at least one window is valid. +int valid_tabpage_win(tabpage_T *tpc) +{ + FOR_ALL_TABS(tp) { + if (tp == tpc) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (win_valid_any_tab(wp)) { + return true; + } + } + return false; + } + } + // shouldn't happen + return false; +} + +/// Close tabpage `tab`, assuming it has no windows in it. +/// There must be another tabpage or this will crash. +void close_tabpage(tabpage_T *tab) +{ + tabpage_T *ptp; + + if (tab == first_tabpage) { + first_tabpage = tab->tp_next; + ptp = first_tabpage; + } else { + for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab; + ptp = ptp->tp_next) { + // do nothing + } + ptp->tp_next = tab->tp_next; + } + + goto_tabpage_tp(ptp, false, false); + free_tabpage(tab); +} + /* * Find tab page "n" (first one is 1). Returns NULL when not found. */ -- cgit From ca853edb6f9ffe1d2e5d4a63bf88e4c3a059f5fb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 13:59:11 +0100 Subject: vim-patch:8.0.0177 Problem: When opening a buffer on a directory and inside a try/catch then the BufEnter event is not triggered. Solution: Return NOTDONE from readfile() for a directory and deal with the three possible return values. (Justin M. Keyes, closes vim/vim#1375, closes vim/vim#1353) https://github.com/vim/vim/commit/e13b9afe1283f5ae43232b5992372a0eb570666c --- src/nvim/testdir/test_autocmd.vim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index fe9a3dd451..395c4a8596 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -196,6 +196,25 @@ func Test_augroup_deleted() au! VimEnter endfunc +func Test_BufEnter() + au! BufEnter + au Bufenter * let val = val . '+' + let g:val = '' + split NewFile + call assert_equal('+', g:val) + bwipe! + call assert_equal('++', g:val) + + " Also get BufEnter when editing a directory + call mkdir('Xdir') + split Xdir + call assert_equal('+++', g:val) + bwipe! + + call delete('Xdir', 'd') + au! BufEnter +endfunc + " Closing a window might cause an endless loop " E814 for older Vims function Test_autocmd_bufwipe_in_SessLoadPost() -- cgit From 165ba3e636769c38d67285e1b8ea2966ccb00b30 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Mar 2017 14:01:22 +0100 Subject: vim-patch:7.4.2324 Problem: Crash when editing a new buffer and BufUnload autocommand wipes out the new buffer. (Norio Takagi) Solution: Don't allow wiping out this buffer. (partly by Hirohito Higashi) Move old style test13 into test_autocmd. Avoid ml_get error when editing a file. https://github.com/vim/vim/commit/e0ab94e7123ca7855f45919114d948ef2bc1e8c3 --- src/nvim/buffer.c | 23 +++++---- src/nvim/buffer_defs.h | 6 +-- src/nvim/ex_cmds.c | 11 ++--- src/nvim/ex_docmd.c | 6 +-- src/nvim/testdir/test13.in | 63 ------------------------- src/nvim/testdir/test13.ok | 31 ------------ src/nvim/testdir/test_autocmd.vim | 99 +++++++++++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- src/nvim/window.c | 13 ++--- 9 files changed, 133 insertions(+), 121 deletions(-) delete mode 100644 src/nvim/testdir/test13.in delete mode 100644 src/nvim/testdir/test13.ok (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9e781f5dff..5edc87eab1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -360,6 +360,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) wipe_buf = true; } + // Disallow deleting the buffer when it is locked (already being closed or + // halfway a command that relies on it). Unloading is allowed. + if (buf->b_locked > 0 && (del_buf || wipe_buf)) { + EMSG(_("E937: Attempt to delete a buffer that is in use")); + return; + } + if (win_valid_any_tab(win)) { // Set b_last_cursor when closing the last window for the buffer. // Remember the last cursor position and window options of the buffer. @@ -378,14 +385,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -395,14 +402,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* When the buffer becomes hidden, but is not unloaded, trigger * BufHidden */ if (!unload_buf) { - buf->b_closing = true; + buf->b_locked++; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } - buf->b_closing = false; + buf->b_locked--; if (abort_if_last && one_window()) { /* Autocommands made this the only window. */ EMSG(_(e_auabort)); @@ -559,7 +566,7 @@ void buf_freeall(buf_T *buf, int flags) tabpage_T *the_curtab = curtab; // Make sure the buffer isn't closed by autocommands. - buf->b_closing = true; + buf->b_locked++; bufref_T bufref; set_bufref(&bufref, buf); @@ -584,7 +591,7 @@ void buf_freeall(buf_T *buf, int flags) // Autocommands may delete the buffer. return; } - buf->b_closing = false; + buf->b_locked--; // If the buffer was in curwin and the window has changed, go back to that // window, if it still exists. This avoids that ":edit x" triggering a @@ -1111,7 +1118,7 @@ do_buffer ( * a window with this buffer. */ while (buf == curbuf - && !(curwin->w_closing || curwin->w_buffer->b_closing) + && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { if (win_close(curwin, FALSE) == FAIL) break; @@ -4499,7 +4506,7 @@ void ex_buffer_all(exarg_T *eap) : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin) ) && firstwin != lastwin - && !(wp->w_closing || wp->w_buffer->b_closing) + && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); wpnext = firstwin; /* just in case an autocommand does diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index fdd7d945c9..9d350c763e 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -470,9 +470,9 @@ struct file_buffer { int b_nwindows; /* nr of windows open on this buffer */ - int b_flags; /* various BF_ flags */ - bool b_closing; /* buffer is being closed, don't let - autocommands close it too. */ + int b_flags; // various BF_ flags + int b_locked; // Buffer is being closed or referenced, don't + // let autocommands wipe it out. /* * b_ffname has the full path of the file (NULL for no name). diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b83677807..41f97b0cef 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2284,8 +2284,11 @@ int do_ecmd( } else { win_T *the_curwin = curwin; - // Set the w_closing flag to avoid that autocommands close the window. + // Set w_closing to avoid that autocommands close the window. + // Set b_locked for the same reason. the_curwin->w_closing = true; + buf->b_locked++; + if (curbuf == old_curbuf.br_buf) { buf_copy_options(buf, BCO_ENTER); } @@ -2298,6 +2301,7 @@ int do_ecmd( false); the_curwin->w_closing = false; + buf->b_locked--; // autocmds may abort script processing if (aborting() && curwin->w_buffer != NULL) { @@ -2444,11 +2448,6 @@ int do_ecmd( /* Assume success now */ retval = OK; - /* - * Reset cursor position, could be used by autocommands. - */ - check_cursor(); - /* * Check if we are editing the w_arg_idx file in the argument list. */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bd3b8c204a..ebfbf2bf09 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5775,7 +5775,7 @@ static void ex_quit(exarg_T *eap) // Refuse to quit when locked or when the buffer in the last window is // being closed (can only happen in autocommands). if (curbuf_locked() - || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) { + || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) { return; } @@ -5836,7 +5836,7 @@ static void ex_quit_all(exarg_T *eap) apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); /* Refuse to quit when locked or when the buffer in the last window is * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) return; exiting = true; @@ -6131,7 +6131,7 @@ static void ex_exit(exarg_T *eap) apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); /* Refuse to quit when locked or when the buffer in the last window is * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) return; // if more files or windows we won't exit diff --git a/src/nvim/testdir/test13.in b/src/nvim/testdir/test13.in deleted file mode 100644 index fa9ba312b7..0000000000 --- a/src/nvim/testdir/test13.in +++ /dev/null @@ -1,63 +0,0 @@ -Tests for autocommands on :close command - -Write three files and open them, each in a window. -Then go to next window, with autocommand that deletes the previous one. -Do this twice, writing the file. - -Also test deleting the buffer on a Unload event. If this goes wrong there -will be the ATTENTION prompt. - -Also test changing buffers in a BufDel autocommand. If this goes wrong there -are ml_line errors and/or a Crash. - -STARTTEST -:/^start of testfile/,/^end of testfile/w! Xtestje1 -:/^start of testfile/,/^end of testfile/w! Xtestje2 -:/^start of testfile/,/^end of testfile/w! Xtestje3 -:e Xtestje1 -otestje1 -:w -:sp Xtestje2 -otestje2 -:w -:sp Xtestje3 -otestje3 -:w - -:au WinLeave Xtestje2 bwipe - -:w! test.out -:au WinLeave Xtestje1 bwipe Xtestje3 -:close -:w >>test.out -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out -:au! -:au! BufUnload Xtestje1 bwipe -:e Xtestje3 -:w >>test.out -:e Xtestje2 -:sp Xtestje1 -:e -:w >>test.out -:au! -:only -:e Xtestje1 -:bwipe Xtestje2 Xtestje3 test.out test13.in -:au BufWipeout Xtestje1 buf Xtestje1 -:bwipe -:w >>test.out -:only -:new|set buftype=help -:wincmd w -:1quit -:$put ='Final line' -:$w >>test.out -:qa! -ENDTEST - -start of testfile - contents - contents - contents -end of testfile diff --git a/src/nvim/testdir/test13.ok b/src/nvim/testdir/test13.ok deleted file mode 100644 index 66ebce63f7..0000000000 --- a/src/nvim/testdir/test13.ok +++ /dev/null @@ -1,31 +0,0 @@ -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -start of testfile -testje3 - contents - contents - contents -end of testfile -start of testfile -testje2 - contents - contents - contents -end of testfile -start of testfile -testje1 - contents - contents - contents -end of testfile -Final line diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 395c4a8596..39cd92440e 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -77,11 +77,49 @@ function Test_autocmd_bufunload_with_tabnext() quit call assert_equal(2, tabpagenr('$')) + autocmd! test_autocmd_bufunload_with_tabnext_group augroup! test_autocmd_bufunload_with_tabnext_group tablast quit endfunc +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_01() + split aa.txt + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + call assert_fails('edit bb.txt', 'E937:') + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! aa.txt + bwipe! bb.txt +endfunc + +" SEGV occurs in older versions. (At least 7.4.2321 or older) +function Test_autocmd_bufunload_avoiding_SEGV_02() + setlocal buftype=nowrite + let lastbuf = bufnr('$') + + augroup test_autocmd_bufunload + autocmd! + exe 'autocmd BufUnload ' . (lastbuf + 1) . 'bwipeout!' + augroup END + + normal! i1 + call assert_fails('edit a.txt', 'E517:') + call feedkeys("\") + + autocmd! test_autocmd_bufunload + augroup! test_autocmd_bufunload + bwipe! a.txt +endfunc + func Test_win_tab_autocmd() let g:record = [] @@ -196,6 +234,67 @@ func Test_augroup_deleted() au! VimEnter endfunc +" Tests for autocommands on :close command. +" This used to be in test13. +func Test_three_windows() + " Write three files and open them, each in a window. + " Then go to next window, with autocommand that deletes the previous one. + " Do this twice, writing the file. + e! Xtestje1 + call setline(1, 'testje1') + w + sp Xtestje2 + call setline(1, 'testje2') + w + sp Xtestje3 + call setline(1, 'testje3') + w + wincmd w + au WinLeave Xtestje2 bwipe + wincmd w + call assert_equal('Xtestje1', expand('%')) + + au WinLeave Xtestje1 bwipe Xtestje3 + close + call assert_equal('Xtestje1', expand('%')) + + " Test deleting the buffer on a Unload event. If this goes wrong there + " will be the ATTENTION prompt. + e Xtestje1 + au! + au! BufUnload Xtestje1 bwipe + call assert_fails('e Xtestje3', 'E937:') + call assert_equal('Xtestje3', expand('%')) + + e Xtestje2 + sp Xtestje1 + call assert_fails('e', 'E937:') + call assert_equal('Xtestje2', expand('%')) + + " Test changing buffers in a BufWipeout autocommand. If this goes wrong + " there are ml_line errors and/or a Crash. + au! + only + e Xanother + e Xtestje1 + bwipe Xtestje2 + bwipe Xtestje3 + au BufWipeout Xtestje1 buf Xtestje1 + bwipe + call assert_equal('Xanother', expand('%')) + + only + help + wincmd w + 1quit + call assert_equal('Xanother', expand('%')) + + au! + call delete('Xtestje1') + call delete('Xtestje2') + call delete('Xtestje3') +endfunc + func Test_BufEnter() au! BufEnter au Bufenter * let val = val . '+' diff --git a/src/nvim/version.c b/src/nvim/version.c index 32ef2c2fdb..8ae5e4057e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -117,7 +117,7 @@ static int included_patches[] = { // 2327 NA 2326, // 2325 NA - // 2324, + 2324, 2323, 2322, 2321, diff --git a/src/nvim/window.c b/src/nvim/window.c index eda3cd7810..a737ffb33c 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1727,7 +1727,7 @@ void close_windows(buf_T *buf, int keep_curwin) for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing)) { + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { if (win_close(wp, false) == FAIL) { // If closing the window fails give up, to avoid looping forever. break; @@ -1745,8 +1745,7 @@ void close_windows(buf_T *buf, int keep_curwin) if (tp != curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { win_close_othertab(wp, FALSE, tp); /* Start all over, the tab page may be closed and @@ -1882,8 +1881,9 @@ int win_close(win_T *win, int free_buf) return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) - return FAIL; /* window is already being closed */ + if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + return FAIL; // window is already being closed + } if (win == aucmd_win) { EMSG(_("E813: Cannot close autocmd window")); return FAIL; @@ -2064,7 +2064,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) { + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { return; // window is already being closed } -- cgit From 6a8bad03084c58d531e48b3d7f7f87f305f150d8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:15:22 +0100 Subject: vim-patch:8.0.0019 Problem: Test_command_count is old style. Solution: Turn it into a new style test. (Naruhiko Nishino) Use more assert functions. --- src/nvim/testdir/Makefile | 3 +- src/nvim/testdir/test_alot.vim | 1 + src/nvim/testdir/test_autocmd.vim | 13 +++ src/nvim/testdir/test_command_count.vim | 191 ++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_command_count.vim (limited to 'src') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index d090ace432..0337ab13dd 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -1,4 +1,4 @@ -# +# vim: noet ts=8 # Makefile to run all tests for Vim # @@ -30,6 +30,7 @@ SCRIPTS ?= \ NEW_TESTS ?= \ test_bufwintabinfo.res \ test_cmdline.res \ + test_command_count.res \ test_cscope.res \ test_digraph.res \ test_diffmode.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 8aa0f417d1..241300f688 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,6 +3,7 @@ source test_assign.vim source test_autocmd.vim +source test_command_count.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 39cd92440e..ace9e457bb 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1,5 +1,15 @@ " Tests for autocommands +set belloff=all + +function! s:cleanup_buffers() abort + for bnr in range(1, bufnr('$')) + if bufloaded(bnr) && bufnr('%') != bnr + execute 'bd! ' . bnr + endif + endfor +endfunction + func Test_vim_did_enter() call assert_false(v:vim_did_enter) @@ -237,6 +247,9 @@ endfunc " Tests for autocommands on :close command. " This used to be in test13. func Test_three_windows() + " Clean up buffers, because in some cases this function fails. + call s:cleanup_buffers() + " Write three files and open them, each in a window. " Then go to next window, with autocommand that deletes the previous one. " Do this twice, writing the file. diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim new file mode 100644 index 0000000000..e438a8b077 --- /dev/null +++ b/src/nvim/testdir/test_command_count.vim @@ -0,0 +1,191 @@ +" Test for user command counts. + +func Test_command_count_0() + set hidden + set noswapfile + + split DoesNotExistEver + let lastbuf = bufnr('$') + call setline(1, 'asdf') + quit! + + command! -range -addr=loaded_buffers RangeLoadedBuffers :let lines = [, ] + command! -range=% -addr=loaded_buffers RangeLoadedBuffersAll :let lines = [, ] + command! -range -addr=buffers RangeBuffers :let lines = [, ] + command! -range=% -addr=buffers RangeBuffersAll :let lines = [, ] + + .,$RangeLoadedBuffers + call assert_equal([1, 1], lines) + %RangeLoadedBuffers + call assert_equal([1, 1], lines) + RangeLoadedBuffersAll + call assert_equal([1, 1], lines) + .,$RangeBuffers + call assert_equal([1, lastbuf], lines) + %RangeBuffers + call assert_equal([1, lastbuf], lines) + RangeBuffersAll + call assert_equal([1, lastbuf], lines) + + delcommand RangeLoadedBuffers + delcommand RangeLoadedBuffersAll + delcommand RangeBuffers + delcommand RangeBuffersAll + + set hidden& + set swapfile& +endfunc + +func Test_command_count_1() + silent! %argd + arga a b c d e + argdo echo "loading buffers" + argu 3 + command! -range -addr=arguments RangeArguments :let lines = [, ] + command! -range=% -addr=arguments RangeArgumentsAll :let lines = [, ] + .-,$-RangeArguments + call assert_equal([2, 4], lines) + %RangeArguments + call assert_equal([1, 5], lines) + RangeArgumentsAll + call assert_equal([1, 5], lines) + N + .RangeArguments + call assert_equal([2, 2], lines) + delcommand RangeArguments + delcommand RangeArgumentsAll + + split|split|split|split + 3wincmd w + command! -range -addr=windows RangeWindows :let lines = [, ] + .,$RangeWindows + call assert_equal([3, 5], lines) + %RangeWindows + call assert_equal([1, 5], lines) + delcommand RangeWindows + + command! -range=% -addr=windows RangeWindowsAll :let lines = [, ] + RangeWindowsAll + call assert_equal([1, 5], lines) + delcommand RangeWindowsAll + only + blast|bd + + tabe|tabe|tabe|tabe + normal 2gt + command! -range -addr=tabs RangeTabs :let lines = [, ] + .,$RangeTabs + call assert_equal([2, 5], lines) + %RangeTabs + call assert_equal([1, 5], lines) + delcommand RangeTabs + + command! -range=% -addr=tabs RangeTabsAll :let lines = [, ] + RangeTabsAll + call assert_equal([1, 5], lines) + delcommand RangeTabsAll + 1tabonly + + s/\n/\r\r\r\r\r/ + 2ma< + $-ma> + command! -range=% RangeLines :let lines = [, ] + '<,'>RangeLines + call assert_equal([2, 5], lines) + delcommand RangeLines + + command! -range=% -buffer LocalRangeLines :let lines = [, ] + '<,'>LocalRangeLines + call assert_equal([2, 5], lines) + delcommand LocalRangeLines +endfunc + +func Test_command_count_2() + silent! %argd + arga a b c d + call assert_fails('5argu', 'E16:') + + $argu + call assert_equal('d', expand('%:t')) + + 1argu + call assert_equal('a', expand('%:t')) + + call assert_fails('300b', 'E16:') + + split|split|split|split + 0close + + $wincmd w + $close + call assert_equal(3, winnr()) + + call assert_fails('$+close', 'E16:') + + $tabe + call assert_equal(2, tabpagenr()) + + call assert_fails('$+tabe', 'E16:') + + only! + e x + 0tabm + normal 1gt + call assert_equal('x', expand('%:t')) + + tabonly! + only! +endfunc + +func Test_command_count_3() + se nohidden + e aaa + let buf_aaa = bufnr('%') + e bbb + let buf_bbb = bufnr('%') + e ccc + let buf_ccc = bufnr('%') + buf 1 + call assert_equal([1, 1, 1], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_bbb . "," . buf_ccc . "bdelete" + call assert_equal([1, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) + exe buf_aaa . "bdelete" + call assert_equal([0, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) +endfunc + +func Test_command_count_4() + %argd + let bufnr = bufnr('$') + 1 + arga aa bb cc dd ee ff + 3argu + let args = [] + .,$-argdo call add(args, expand('%')) + call assert_equal(['cc', 'dd', 'ee'], args) + + " create windows to get 5 + split|split|split|split + 2wincmd w + let windows = [] + .,$-windo call add(windows, winnr()) + call assert_equal([2, 3, 4], windows) + only! + + exe bufnr . 'buf' + let buffers = [] + .,$-bufdo call add(buffers, bufnr('%')) + call assert_equal([bufnr, bufnr + 1, bufnr + 2, bufnr + 3, bufnr + 4], buffers) + + exe (bufnr + 3) . 'bdel' + let buffers = [] + exe (bufnr + 2) . ',' . (bufnr + 5) . "bufdo call add(buffers, bufnr('%'))" + call assert_equal([bufnr + 2, bufnr + 4, bufnr + 5], buffers) + + " create tabpages to get 5 + tabe|tabe|tabe|tabe + normal! 2gt + let tabpages = [] + .,$-tabdo call add(tabpages, tabpagenr()) + call assert_equal([2, 3, 4], tabpages) + tabonly! + bwipe! +endfunc -- cgit From 4c18670e91e22ab4fe566aa181aa6f28166e8aad Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:19:01 +0100 Subject: vim-patch:7.4.2346 Problem: Autocommand test fails when run directly, passes when run as part of test_alot. Solution: Add command to make the cursor move. Close a tab page. --- src/nvim/testdir/test_autocmd.vim | 3 +++ src/nvim/version.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index ace9e457bb..34cca5f612 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -23,6 +23,9 @@ if has('timers') endfunc func Test_cursorhold_insert() + " Need to move the cursor. + call feedkeys("ggG", "xt") + let g:triggered = 0 au CursorHoldI * let g:triggered += 1 set updatetime=20 diff --git a/src/nvim/version.c b/src/nvim/version.c index 8ae5e4057e..21f3a493c3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -95,7 +95,7 @@ static int included_patches[] = { // 2349, 2348, 2347, - // 2346, + 2346, // 2345 NA // 2344 NA // 2343, -- cgit From 4a2e6f460d02762d211c0f92af305ef5290a667b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:24:40 +0100 Subject: vim-patch:7.4.2341 Problem: Tiny things. Test doesn't clean up properly. Solution: Adjust comment and white space. Restore option value. --- src/nvim/ex_cmds.c | 2 +- src/nvim/testdir/test_autocmd.vim | 3 +++ src/nvim/version.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 41f97b0cef..e368838df7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2294,7 +2294,7 @@ int do_ecmd( } // Close the link to the current buffer. This will set - // curwin->w_buffer to NULL. + // oldwin->w_buffer to NULL. u_sync(false); close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 34cca5f612..b5d78c183f 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -33,6 +33,7 @@ if has('timers') call feedkeys('a', 'x!') call assert_equal(1, g:triggered) au! CursorHoldI + set updatetime& endfunc func Test_cursorhold_insert_ctrl_x() @@ -44,6 +45,7 @@ if has('timers') call feedkeys("a\", 'x!') call assert_equal(0, g:triggered) au! CursorHoldI + set updatetime& endfunc endif @@ -223,6 +225,7 @@ func Test_augroup_warning() augroup Another augroup END call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0) + augroup! Another " no warning for postpone aucmd delete augroup StartOK diff --git a/src/nvim/version.c b/src/nvim/version.c index 21f3a493c3..84cb8fea9b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -100,7 +100,7 @@ static int included_patches[] = { // 2344 NA // 2343, // 2342 NA - // 2341, + 2341, // 2340 NA // 2339, // 2338 NA -- cgit From 10f6624f65edbc8af84c9775e6712484be9c81a5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:39:24 +0100 Subject: vim-patch:7.4.2328 Problem: Crash when BufWinLeave autocmd goes to another tab page. (Hirohito Higashi) Solution: Make close_buffer() go back to the right window. --- src/nvim/buffer.c | 14 ++++++++++++++ src/nvim/testdir/test_autocmd.vim | 11 +++++++++++ src/nvim/version.c | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5edc87eab1..7429e8628e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -337,6 +337,10 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); bool wipe_buf = (action == DOBUF_WIPE); + bool is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + // Force unloading or deleting when 'bufhidden' says so, but not for terminal // buffers. // The caller must take care of NOT deleting/freeing when 'bufhidden' is @@ -419,6 +423,16 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) if (aborting()) /* autocmds may abort script processing */ return; } + + // If the buffer was in curwin and the window has changed, go back to that + // window, if it still exists. This avoids that ":edit x" triggering a + // "tabnext" BufUnload autocmd leaves a window behind without a buffer. + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + int nwindows = buf->b_nwindows; /* decrease the link count from windows (unless not in any window) */ diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index b5d78c183f..a122a62561 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -98,6 +98,17 @@ function Test_autocmd_bufunload_with_tabnext() quit endfunc +function Test_autocmd_bufwinleave_with_tabfirst() + tabedit + augroup sample + autocmd! + autocmd BufWinLeave tabfirst + augroup END + call setline(1, ['a', 'b', 'c']) + edit! a.txt + tabclose +endfunc + " SEGV occurs in older versions. (At least 7.4.2321 or older) function Test_autocmd_bufunload_avoiding_SEGV_01() split aa.txt diff --git a/src/nvim/version.c b/src/nvim/version.c index 84cb8fea9b..d1d3e5a3c9 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -113,7 +113,7 @@ static int included_patches[] = { 2331, // 2330, 2329, - // 2328, + 2328, // 2327 NA 2326, // 2325 NA -- cgit From 2e9c1a9c4a6e2aaaef87c1c37b9cd14965b6d7b8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 12:09:11 +0100 Subject: vim-patch:8.0.0178 Problem: test_command_count may fail when a previous test interferes, seen on MS-Windows. Solution: Run it separately. https://github.com/vim/vim/commit/9b73c4a215cb5f0f7df1e7f0663aea2bce1914ab --- src/nvim/testdir/test_alot.vim | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 241300f688..8aa0f417d1 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,7 +3,6 @@ source test_assign.vim source test_autocmd.vim -source test_command_count.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim -- cgit From b0e34497b357462024c07d506a16426d58475497 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 10:47:46 +0100 Subject: test/legacy: fix test_autocmd.vim vim-patch:8.0.0487 --- src/nvim/testdir/runtest.vim | 2 ++ src/nvim/testdir/test_autocmd.vim | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b4eb9de506..1cf7ab475c 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -72,6 +72,8 @@ let v:testing = 1 set directory^=. set backspace= set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd +" Prevent Nvim log from writing to stderr. +let $NVIM_LOG_FILE='Xnvim.log' function RunTheTest(test) echo 'Executing ' . a:test diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index a122a62561..5b5232f06e 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -359,11 +359,11 @@ function Test_autocmd_bufwipe_in_SessLoadPost() \ 'let v:swapchoice="e"', \ 'augroup test_autocmd_sessionload', \ 'autocmd!', - \ 'autocmd SessionLoadPost * 4bw!', - \ 'augroup END' + \ 'autocmd SessionLoadPost * 4bw!|qall!', + \ 'augroup END', \ ] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') call assert_match('E814', a) unlet! g:bufnr @@ -395,11 +395,13 @@ function Test_autocmd_bufwipe_in_SessLoadPost2() \ ' exec ''bwipeout '' . b', \ ' endif', \ ' endfor', - \ 'call append("1", "SessionLoadPost DONE")', + \ 'redraw!', + \ 'echon "SessionLoadPost DONE"', + \ 'qall!', \ 'endfunction', \ 'au SessionLoadPost * call DeleteInactiveBufs()'] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -u Xvimrc --noplugins -S Session.vim') + let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') " this probably only matches on unix if has("unix") call assert_notmatch('Caught deadly signal SEGV', a) -- cgit From 53ccd07fa1c0bfcb79ca11bd873ea12f4dc12f4a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 11:02:29 +0100 Subject: lint --- src/nvim/buffer.c | 4 ++-- src/nvim/ex_docmd.c | 18 ++++++++++-------- src/nvim/window.c | 5 +++-- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7429e8628e..3c416c157f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4518,8 +4518,8 @@ void ex_buffer_all(exarg_T *eap) ? wp->w_height + wp->w_status_height < Rows - p_ch - tabline_height() : wp->w_width != Columns) - || (had_tab > 0 && wp != firstwin) - ) && firstwin != lastwin + || (had_tab > 0 && wp != firstwin)) + && firstwin != lastwin && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ebfbf2bf09..abf4371455 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5833,11 +5833,12 @@ static void ex_quit_all(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } exiting = true; if (eap->forceit || !check_changed_any(false, false)) { @@ -6128,11 +6129,12 @@ static void ex_exit(exarg_T *eap) text_locked_msg(); return; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked or when the buffer in the last window is + // being closed (can only happen in autocommands). + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) { return; + } // if more files or windows we won't exit if (check_more(false, eap->forceit) == OK && only_one_window()) { diff --git a/src/nvim/window.c b/src/nvim/window.c index a737ffb33c..15482d9061 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1746,7 +1746,7 @@ void close_windows(buf_T *buf, int keep_curwin) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { - win_close_othertab(wp, FALSE, tp); + win_close_othertab(wp, false, tp); /* Start all over, the tab page may be closed and * autocommands may change the window layout. */ @@ -1881,7 +1881,8 @@ int win_close(win_T *win, int free_buf) return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { return FAIL; // window is already being closed } if (win == aucmd_win) { -- cgit From 929859ed8156a45f5acb491d82f8c06ef5f57902 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:11:10 +0100 Subject: vim-patch:7.4.2355 Problem: Regexp fails to match when using "\>\)\?". (Ramel) Solution: When a state is already in the list, but addstate_here() is used and the existing state comes later, add the new state anyway. https://github.com/vim/vim/commit/16b3578f355282846f2600ce77fb344950f0b9ce --- src/nvim/regexp_nfa.c | 61 +++++++++++++++++++++++++---------- src/nvim/testdir/test_regexp_utf8.vim | 9 ++++++ src/nvim/version.c | 2 +- 3 files changed, 54 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 474f3df32a..3f4e12af4a 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3893,21 +3893,25 @@ state_in_list ( return FALSE; } -/* - * Add "state" and possibly what follows to state list ".". - * Returns "subs_arg", possibly copied into temp_subs. - */ +// Offset used for "off" by addstate_here(). +#define ADDSTATE_HERE_OFFSET 10 +// Add "state" and possibly what follows to state list ".". +// Returns "subs_arg", possibly copied into temp_subs. static regsubs_T * addstate ( nfa_list_T *l, /* runtime state list */ nfa_state_T *state, /* state to update */ regsubs_T *subs_arg, /* pointers to subexpressions */ nfa_pim_T *pim, /* postponed look-behind match */ - int off /* byte offset, when -1 go to next line */ -) + int off_arg) /* byte offset, when -1 go to next line */ { int subidx; + int off = off_arg; + int add_here = FALSE; + int listindex = 0; + int k; + int found = FALSE; nfa_thread_T *thread; lpos_T save_lpos; int save_in_use; @@ -3920,6 +3924,12 @@ addstate ( int did_print = FALSE; #endif + if (off_arg <= -ADDSTATE_HERE_OFFSET) { + add_here = true; + off = 0; + listindex = -(off_arg + ADDSTATE_HERE_OFFSET); + } + switch (state->c) { case NFA_NCLOSE: case NFA_MCLOSE: @@ -3996,13 +4006,28 @@ addstate ( * lower position is preferred. */ if (!nfa_has_backref && pim == NULL && !l->has_pim && state->c != NFA_MATCH) { + + /* When called from addstate_here() do insert before + * existing states. */ + if (add_here) { + for (k = 0; k < l->n && k < listindex; ++k) { + if (l->t[k].state->id == state->id) { + found = TRUE; + break; + } + } + } + + if (!add_here || found) { skip_add: #ifdef REGEXP_DEBUG - nfa_set_code(state->c); - fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s\n", - abs(state->id), l->id, state->c, code); + nfa_set_code(state->c); + fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s pim: %s has_pim: %d found: %d\n", + abs(state->id), l->id, state->c, code, + pim == NULL ? "NULL" : "yes", l->has_pim, found); #endif return subs; + } } /* Do not add the state again when it exists with the same @@ -4058,14 +4083,14 @@ skip_add: case NFA_SPLIT: /* order matters here */ - subs = addstate(l, state->out, subs, pim, off); - subs = addstate(l, state->out1, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); + subs = addstate(l, state->out1, subs, pim, off_arg); break; case NFA_EMPTY: case NFA_NOPEN: case NFA_NCLOSE: - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; case NFA_MOPEN: @@ -4145,7 +4170,7 @@ skip_add: sub->list.line[subidx].start = reginput + off; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZOPEN && state->c <= NFA_ZOPEN9) sub = &subs->synt; @@ -4168,7 +4193,7 @@ skip_add: ? subs->norm.list.multi[0].end_lnum >= 0 : subs->norm.list.line[0].end != NULL)) { /* Do not overwrite the position set by \ze. */ - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); break; } case NFA_MCLOSE1: @@ -4228,7 +4253,7 @@ skip_add: save_lpos.col = 0; } - subs = addstate(l, state->out, subs, pim, off); + subs = addstate(l, state->out, subs, pim, off_arg); /* "subs" may have changed, need to set "sub" again */ if (state->c >= NFA_ZCLOSE && state->c <= NFA_ZCLOSE9) sub = &subs->synt; @@ -4266,8 +4291,10 @@ addstate_here ( int count; int listidx = *ip; - /* first add the state(s) at the end, so that we know how many there are */ - addstate(l, state, subs, pim, 0); + /* First add the state(s) at the end, so that we know how many there are. + * Pass the listidx as offset (avoids adding another argument to + * addstate(). */ + addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET); /* when "*ip" was at the end of the list, nothing to do */ if (listidx + 1 == tlen) diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 9e9a3de500..7f3b31575d 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -97,3 +97,12 @@ func Test_recursive_substitute() call setwinvar(1, 'myvar', 1) bwipe! endfunc + +func Test_eow_with_optional() + let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', ''] + for re in range(0, 2) + exe 'set re=' . re + let actual = matchlist('abc def', '\(abc\>\)\?\s*\(def\)') + call assert_equal(expected, actual) + endfor +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index d1d3e5a3c9..1b6e513c49 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -86,7 +86,7 @@ static int included_patches[] = { // 2358 NA // 2357, // 2356, - // 2355, + 2355, // 2354, 2353, // 2352 NA -- cgit From 5ee211770d9c7999a01413f368e82588f3d0426d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:22:14 +0100 Subject: vim-patch:8.0.0033 Problem: Cannot use overlapping positions with matchaddpos(). Solution: Check end of match. (Ozaki Kiichi) Add a test (Hirohito Higashi) https://github.com/vim/vim/commit/a6c27ee6db2c328e0ab0e6d143e2a295a0bb9c9a --- src/nvim/testdir/test_match.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 9398ef2f27..066bb2f6a1 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -198,6 +198,16 @@ func Test_matchaddpos() call assert_equal(screenattr(2,2), screenattr(1,10)) call assert_notequal(screenattr(2,2), screenattr(1,11)) + " Check overlapping pos + call clearmatches() + call setline(1, ['1234567890', 'NH']) + call matchaddpos('Error', [[1,1,5], [1,3,5], [2,2]]) + redraw! + call assert_notequal(screenattr(2,2), 0) + call assert_equal(screenattr(2,2), screenattr(1,5)) + call assert_equal(screenattr(2,2), screenattr(1,7)) + call assert_notequal(screenattr(2,2), screenattr(1,8)) + nohl call clearmatches() syntax off -- cgit From cd9fc4627ecf4d675e2c221b614cc2eb40aabf9c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:26:02 +0100 Subject: vim-patch:8.0.0049 Problem: When a match ends in part of concealed text highlighting, it might mess up concealing by resetting prev_syntax_id. Solution: Do not reset prev_syntax_id and add a test to verify. (Christian Brabandt, closes vim/vim#1092) https://github.com/vim/vim/commit/2f97912800e86a296c001832bbbf2fc425f1e533 --- src/nvim/screen.c | 1 - src/nvim/testdir/test_matchadd_conceal.vim | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b98e59ed06..280b8c65fe 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2922,7 +2922,6 @@ win_line ( } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; - prev_syntax_id = 0; next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index bc1c28d6e9..c788689e33 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -260,3 +260,25 @@ function! Test_matchadd_repeat_conceal_with_syntax_off() quit! endfunction + +function! Test_matchadd_and_syn_conceal() + new + let cnt='Inductive bool : Type := | true : bool | false : bool.' + let expect = 'Inductive - : Type := | true : - | false : -.' + 0put =cnt + " set filetype and :syntax on to change screenattr() + set cole=1 cocu=nv + hi link CheckedByCoq WarningMsg + syntax on + syntax keyword coqKwd bool conceal cchar=- + redraw! + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) + call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c') + call assert_equal(expect, s:screenline(1)) + call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) + call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) + call assert_equal(screenattr(1, 11) , screenattr(1, 32)) +endfunction -- cgit From 89abed7d852518528051c1c0fdf84d34d3d8c2d5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:56:10 +0100 Subject: vim-patch:8.0.0032 Problem: Tests may change the input file when something goes wrong. Solution: Avoid writing the input file. https://github.com/vim/vim/commit/3e8474dd50f64c998bb665ce852f584a58dede6b --- src/nvim/testdir/test_tabpage.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 7c3039ba24..c2aadcea6e 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -11,6 +11,7 @@ function Test_tabpage() 0tabnew 1tabnew $tabnew + %del tabdo call append(line('$'), tabpagenr()) tabclose! 2 tabrewind -- cgit From 78a4c73cf06aaf8cd496d39bbbeaef821906fc25 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 14:11:27 +0100 Subject: vim-patch:7.4.2339 Problem: Tab page test fails when run as fake root. Solution: Check 'buftype' instead of 'filetype'. (James McCoy, closes vim/vim#1042) https://github.com/vim/vim/commit/100f5c90f4d4fb40bc3aeabc35192db371f5988f --- src/nvim/testdir/test_tabpage.vim | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index c2aadcea6e..870cb4da13 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -205,7 +205,7 @@ function Test_tabpage_with_tab_modifier() exec 'tabnext ' . a:pre_nr exec a:cmd call assert_equal(a:post_nr, tabpagenr()) - call assert_equal('help', &filetype) + call assert_equal('help', &buftype) helpclose endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 1b6e513c49..db3b9b51b2 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -102,7 +102,7 @@ static int included_patches[] = { // 2342 NA 2341, // 2340 NA - // 2339, + 2339, // 2338 NA 2337, 2336, -- cgit From c99514c2a4ebfc35413daa57d017be16bd179e90 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 13:42:33 +0100 Subject: test/legacy: Run test_match, test_tabpage isolated These are failing when run as a batch. Most likely some Vim runtime patch fixed something, but we don't have it yet. Just isolate them for now. Also test_matchadd_conceal_utf8 (it's not there in Vim tree, either). --- src/nvim/testdir/test_alot.vim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 8aa0f417d1..9d1f07fc0b 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -13,8 +13,6 @@ source test_filter_map.vim source test_goto.vim source test_jumps.vim source test_lambda.vim -source test_match.vim -source test_matchadd_conceal_utf8.vim source test_menu.vim source test_mapping.vim source test_messages.vim @@ -26,7 +24,7 @@ source test_source_utf8.vim source test_statusline.vim source test_syn_attr.vim source test_tabline.vim -source test_tabpage.vim +" source test_tabpage.vim source test_tagcase.vim source test_tagjump.vim source test_true_false.vim -- cgit From 386a5f379b059060808d9772cd1f150f507a5577 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 14:35:21 +0100 Subject: test/legacy: Makefile Add missing tests These tests aren't in test_alot.vim, so they need to be added to the Makefile or they won't be run. --- src/nvim/testdir/Makefile | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 0337ab13dd..d948cd0f93 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -36,6 +36,8 @@ NEW_TESTS ?= \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_fold.res \ + test_glob2regpat.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ @@ -47,13 +49,16 @@ NEW_TESTS ?= \ test_marks.res \ test_match.res \ test_matchadd_conceal.res \ + test_matchadd_conceal_utf8.res \ test_nested_function.res \ test_normal.res \ test_quickfix.res \ test_signs.res \ test_syntax.res \ + test_tabpage.res \ test_textobjects.res \ test_timers.res \ + test_undo.res \ test_usercommands.res \ test_viml.res \ test_visual.res \ -- cgit From 0f3afdaa1b5a75a5ed54a69dc43e32ddbb6a32ea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 16:22:04 +0100 Subject: vim-patch:8.0.0259 Problem: Tab commands do not handle count correctly. (Ken Hamada) Solution: Add ADDR_TABS_RELATIVE. (Hirohito Higashi) https://github.com/vim/vim/commit/2f72c70657129c16e6b0e413752a775c804f02f8 --- src/nvim/ex_cmds.lua | 29 ++-- src/nvim/ex_cmds_defs.h | 3 +- src/nvim/ex_docmd.c | 282 ++++++++++++++++++++++++++------------ src/nvim/testdir/test_tabpage.vim | 242 ++++++++++++++++++++++++++++++-- 4 files changed, 444 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 5f81306fc1..92f0669422 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -34,7 +34,8 @@ local ADDR_ARGUMENTS = 2 local ADDR_LOADED_BUFFERS = 3 local ADDR_BUFFERS = 4 local ADDR_TABS = 5 -local ADDR_QUICKFIX = 6 +local ADDR_TABS_RELATIVE = 6 +local ADDR_QUICKFIX = 7 local ADDR_OTHER = 99 -- The following table is described in ex_cmds_defs.h file. @@ -2650,12 +2651,12 @@ return { { command='tab', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_wrongmodifier', }, { command='tabclose', - flags=bit.bor(RANGE, NOTADR, COUNT, BANG, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabclose', }, @@ -2680,7 +2681,7 @@ return { { command='tabfirst', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2692,13 +2693,13 @@ return { { command='tablast', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS, func='ex_tabnext', }, { @@ -2709,32 +2710,32 @@ return { }, { command='tabonly', - flags=bit.bor(BANG, RANGE, NOTADR, TRLBAR, CMDWIN), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), addr_type=ADDR_TABS, func='ex_tabonly', }, { command='tabprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type=ADDR_TABS_RELATIVE, func='ex_tabnext', }, { command='tabrewind', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabnext', }, { command='tabs', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type=ADDR_TABS, func='ex_tabs', }, { diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index c6389a0c8b..133c37cef4 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -72,7 +72,8 @@ #define ADDR_LOADED_BUFFERS 3 #define ADDR_BUFFERS 4 #define ADDR_TABS 5 -#define ADDR_QUICKFIX 6 +#define ADDR_TABS_RELATIVE 6 // Tab page that only relative +#define ADDR_QUICKFIX 7 #define ADDR_OTHER 99 typedef struct exarg exarg_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index abf4371455..4f34312562 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1522,8 +1522,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: - lnum = CURRENT_WIN_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_WIN_NR; break; case ADDR_ARGUMENTS: ea.line2 = curwin->w_arg_idx + 1; @@ -1536,8 +1535,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curbuf->b_fnum; break; case ADDR_TABS: - lnum = CURRENT_TAB_NR; - ea.line2 = lnum; + ea.line2 = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; break; case ADDR_QUICKFIX: ea.line2 = qf_get_cur_valid_idx(&ea); @@ -1587,6 +1588,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } break; + case ADDR_TABS_RELATIVE: + errormsg = (char_u *)_(e_invrange); + goto doend; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -1973,6 +1978,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, case ADDR_TABS: ea.line2 = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; + break; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { ea.line1 = ea.line2 = 0; @@ -2028,11 +2036,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = ea.line2; ea.line2 += n - 1; ++ea.addr_count; - /* - * Be vi compatible: no error message for out of range. - */ - if (ea.line2 > curbuf->b_ml.ml_line_count) + // Be vi compatible: no error message for out of range. + if (ea.addr_type == ADDR_LINES + && ea.line2 > curbuf->b_ml.ml_line_count) { ea.line2 = curbuf->b_ml.ml_line_count; + } } } @@ -3459,6 +3467,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3493,6 +3506,11 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_TABS_RELATIVE: + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + break; case ADDR_QUICKFIX: lnum = qf_get_size(eap); if (lnum == 0) { @@ -3641,6 +3659,9 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_TABS_RELATIVE: + lnum = 1; + break; case ADDR_QUICKFIX: lnum = qf_get_cur_valid_idx(eap); break; @@ -3655,7 +3676,12 @@ static linenr_T get_address(exarg_T *eap, n = 1; else n = getdigits(&cmd); - if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { + + if (addr_type == ADDR_TABS_RELATIVE) { + EMSG(_(e_invrange)); + cmd = NULL; + goto error; + } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { lnum = compute_buffer_local_count( addr_type, lnum, (i == '-') ? -1 * n : n); } else { @@ -3777,6 +3803,9 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } break; + case ADDR_TABS_RELATIVE: + // Do nothing + break; case ADDR_QUICKFIX: assert(eap->line2 >= 0); if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { @@ -4285,6 +4314,104 @@ static int getargopt(exarg_T *eap) return OK; } +/* + * Handle the argument for a tabpage related ex command. + * Returns a tabpage number. + * When an error is encountered then eap->errmsg is set. + */ + static int +get_tabpage_arg(exarg_T *eap) +{ + int tab_number; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + + if (eap->arg && *eap->arg != NUL) + { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; /* argument +N/-N means: go to N places to the + * right/left relative to the current position. */ + + if (*p == '-') + { + relative = -1; + p++; + } + else if (*p == '+') + { + relative = 1; + p++; + } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) + { + if (STRCMP(p, "$") == 0) + tab_number = LAST_TAB_NR; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + } + else + { + if (*p_save == NUL) + tab_number = 1; + else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number == 0) + { + /* No numbers as argument. */ + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) + --tab_number; + } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) + eap->errmsg = e_invarg; + } + else if (eap->addr_count > 0) + { + if (unaccept_arg0 && eap->line2 == 0) + eap->errmsg = e_invrange; + else + { + tab_number = eap->line2; + if (!unaccept_arg0 && **eap->cmdlinep == '-') + { + --tab_number; + if (tab_number < unaccept_arg0) + eap->errmsg = e_invarg; + } + } + } + else + { + switch (eap->cmdidx) + { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } + +theend: + return tab_number; +} + /* * ":abbreviate" and friends. */ @@ -5938,8 +6065,9 @@ static void ex_tabclose(exarg_T *eap) else if (first_tabpage->tp_next == NULL) EMSG(_("E784: Cannot close last tab page")); else { - if (eap->addr_count > 0) { - tp = find_tabpage((int)eap->line2); + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tp = find_tabpage(tab_number); if (tp == NULL) { beep_flush(); return; @@ -5947,44 +6075,43 @@ static void ex_tabclose(exarg_T *eap) if (tp != curtab) { tabpage_close_other(tp, eap->forceit); return; + } else if (!text_locked() && !curbuf_locked()) { + tabpage_close(eap->forceit); } } - if (!text_locked() - && !curbuf_locked() - ) - tabpage_close(eap->forceit); } } -/* - * ":tabonly": close all tab pages except the current one - */ +/// ":tabonly": close all tab pages except the current one static void ex_tabonly(exarg_T *eap) { - if (cmdwin_type != 0) + if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) - MSG(_("Already only one tab page")); - else { - if (eap->addr_count > 0) - goto_tabpage(eap->line2); - /* Repeat this up to a 1000 times, because autocommands may mess - * up the lists. */ - for (int done = 0; done < 1000; ++done) { - FOR_ALL_TABS(tp) { - if (tp->tp_topframe != topframe) { - tabpage_close_other(tp, eap->forceit); - /* if we failed to close it quit */ - if (valid_tabpage(tp)) - done = 1000; - /* start over, "tp" is now invalid */ + } else if (first_tabpage->tp_next == NULL) { + MSG(_("Already only one tab page")); + } else { + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); + // Repeat this up to a 1000 times, because autocommands may + // mess up the lists. + for (int done = 0; done < 1000; done++) { + FOR_ALL_TABS(tp) { + if (tp->tp_topframe != topframe) { + tabpage_close_other(tp, eap->forceit); + // if we failed to close it quit + if (valid_tabpage(tp)) { + done = 1000; + } + // start over, "tp" is now invalid + break; + } + } + assert(first_tabpage); + if (first_tabpage->tp_next == NULL) { break; } } - assert(first_tabpage); - if (first_tabpage->tp_next == NULL) { - break; - } } } } @@ -6473,6 +6600,8 @@ void tabpage_new(void) */ static void ex_tabnext(exarg_T *eap) { + int tab_number; + switch (eap->cmdidx) { case CMD_tabfirst: case CMD_tabrewind: @@ -6483,66 +6612,47 @@ static void ex_tabnext(exarg_T *eap) break; case CMD_tabprevious: case CMD_tabNext: - goto_tabpage(eap->addr_count == 0 ? -1 : -(int)eap->line2); - break; - default: /* CMD_tabnext */ - goto_tabpage(eap->addr_count == 0 ? 0 : (int)eap->line2); - break; - } -} - -/* - * :tabmove command - */ -static void ex_tabmove(exarg_T *eap) -{ - int tab_number; + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save = p; - if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - int relative = 0; /* argument +N/-N means: move N places to the - * right/left relative to the current position. */ - - if (*eap->arg == '-') { - relative = -1; - p = eap->arg + 1; - } else if (*eap->arg == '+') { - relative = 1; - p = eap->arg + 1; - } else - p = eap->arg; - - if (relative == 0) { - if (STRCMP(p, "$") == 0) { - tab_number = LAST_TAB_NR; - } else if (p == skipdigits(p)) { + tab_number = getdigits(&p); + if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { // No numbers as argument. eap->errmsg = e_invarg; return; - } else { - tab_number = getdigits(&p); } } else { - if (*p != NUL) { - tab_number = getdigits(&p); - } else { + if (eap->addr_count == 0) { tab_number = 1; - } - tab_number = tab_number * relative + tabpage_index(curtab); - if (relative == -1) { - --tab_number; + } else { + tab_number = eap->line2; + if (tab_number < 1) { + eap->errmsg = e_invrange; + return; + } } } - } else if (eap->addr_count != 0) { - tab_number = eap->line2; - if (**eap->cmdlinep == '-') { - --tab_number; + goto_tabpage(-tab_number); + break; + default: // CMD_tabnext + tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + goto_tabpage(tab_number); } - } else { - tab_number = LAST_TAB_NR; + break; } +} - tabpage_move(tab_number); +/* + * :tabmove command + */ +static void ex_tabmove(exarg_T *eap) +{ + int tab_number = get_tabpage_arg(eap); + if (eap->errmsg == NULL) { + tabpage_move(tab_number); + } } /* diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 870cb4da13..33139fcda0 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -94,10 +94,6 @@ function Test_tabpage() call assert_equal(7, tabpagenr()) tabmove call assert_equal(10, tabpagenr()) - tabmove -20 - call assert_equal(1, tabpagenr()) - tabmove +20 - call assert_equal(10, tabpagenr()) 0tabmove call assert_equal(1, tabpagenr()) $tabmove @@ -110,7 +106,16 @@ function Test_tabpage() call assert_equal(4, tabpagenr()) 7tabmove 5 call assert_equal(5, tabpagenr()) + call assert_fails("99tabmove", 'E16:') + call assert_fails("+99tabmove", 'E16:') + call assert_fails("-99tabmove", 'E16:') call assert_fails("tabmove foo", 'E474:') + call assert_fails("tabmove 99", 'E474:') + call assert_fails("tabmove +99", 'E474:') + call assert_fails("tabmove -99", 'E474:') + call assert_fails("tabmove -3+", 'E474:') + call assert_fails("tabmove $3", 'E474:') + 1tabonly! endfunc " Test autocommands @@ -118,7 +123,6 @@ function Test_tabpage_with_autocmd() if !has('autocmd') return endif - tabonly! command -nargs=1 -bar C :call add(s:li, '=== ' . . ' ===')| augroup TestTabpageGroup au! @@ -183,8 +187,10 @@ function Test_tabpage_with_autocmd() autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3 let s:li = [] - C tabnext 3 - call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ===', 'BufEnter', '=== tabclose 3 ==='], s:li) + call assert_equal(3, tabpagenr('$')) + C tabnext 2 + call assert_equal(2, tabpagenr('$')) + call assert_equal(['=== tabnext 2 ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ==='], s:li) call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')]) delcommand C @@ -192,8 +198,7 @@ function Test_tabpage_with_autocmd() augroup! TabDestructive autocmd! TestTabpageGroup augroup! TestTabpageGroup - tabonly! - bw! + 1tabonly! endfunction function Test_tabpage_with_tab_modifier() @@ -224,8 +229,223 @@ function Test_tabpage_with_tab_modifier() call assert_fails('-99tab help', 'E16:') delfunction s:check_tab - tabonly! - bw! + 1tabonly! +endfunction + +function Check_tab_count(pre_nr, cmd, post_nr) + exec 'tabnext' a:pre_nr + normal! G + exec a:cmd + call assert_equal(a:post_nr, tabpagenr(), a:cmd) +endfunc + +" Test for [count] of tabnext +function Test_tabpage_with_tabnext() + for n in range(4) + tabedit + call setline(1, ['', '', '3']) + endfor + + call Check_tab_count(1, 'tabnext', 2) + call Check_tab_count(1, '3tabnext', 3) + call Check_tab_count(1, '.tabnext', 1) + call Check_tab_count(1, '.+1tabnext', 2) + call Check_tab_count(2, '+tabnext', 3) + call Check_tab_count(2, '+2tabnext', 4) + call Check_tab_count(4, '-tabnext', 3) + call Check_tab_count(4, '-2tabnext', 2) + call Check_tab_count(3, '$tabnext', 5) + call assert_fails('0tabnext', 'E16:') + call assert_fails('99tabnext', 'E16:') + call assert_fails('+99tabnext', 'E16:') + call assert_fails('-99tabnext', 'E16:') + call Check_tab_count(1, 'tabnext 3', 3) + call Check_tab_count(2, 'tabnext +', 3) + call Check_tab_count(2, 'tabnext +2', 4) + call Check_tab_count(4, 'tabnext -', 3) + call Check_tab_count(4, 'tabnext -2', 2) + call Check_tab_count(3, 'tabnext $', 5) + call assert_fails('tabnext 0', 'E474:') + call assert_fails('tabnext .', 'E474:') + call assert_fails('tabnext -+', 'E474:') + call assert_fails('tabnext +2-', 'E474:') + call assert_fails('tabnext $3', 'E474:') + call assert_fails('tabnext 99', 'E474:') + call assert_fails('tabnext +99', 'E474:') + call assert_fails('tabnext -99', 'E474:') + + 1tabonly! +endfunction + +" Test for [count] of tabprevious +function Test_tabpage_with_tabprevious() + for n in range(5) + tabedit + call setline(1, ['', '', '3']) + endfor + + for cmd in ['tabNext', 'tabprevious'] + call Check_tab_count(6, cmd, 5) + call Check_tab_count(6, '3' . cmd, 3) + call Check_tab_count(6, '8' . cmd, 4) + call Check_tab_count(6, cmd . ' 3', 3) + call Check_tab_count(6, cmd . ' 8', 4) + for n in range(2) + for c in ['0', '.+3', '+', '+2' , '-', '-2' , '$', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . cmd + let err_code = 'E16:' + else + let entire_cmd = cmd . ' ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + endfor + endfor + endfor + + 1tabonly! +endfunction + +function s:reconstruct_tabpage_for_test(nr) + let n = (a:nr > 2) ? a:nr - 2 : 1 + 1tabonly! + 0tabedit n0 + for n in range(1, n) + exec '$tabedit n' . n + if n == 1 + call setline(1, ['', '', '3']) + endif + endfor +endfunc + +" Test for [count] of tabclose +function Test_tabpage_with_tabclose() + + " pre count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, '3tabclose', 1) + call Check_tab_count(4, '4tabclose', 3) + call Check_tab_count(3, '1tabclose', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, '$tabclose', 2) + call Check_tab_count(4, '.tabclose', 4) + call Check_tab_count(3, '.+tabclose', 3) + call Check_tab_count(3, '.-2tabclose', 2) + call Check_tab_count(1, '.+1tabclose!', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + " post count + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(3, 'tabclose!', 3) + call Check_tab_count(1, 'tabclose 3', 1) + call Check_tab_count(4, 'tabclose 4', 3) + call Check_tab_count(3, 'tabclose 1', 2) + call Check_tab_count(2, 'tabclose', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + call Check_tab_count(2, 'tabclose $', 2) + call Check_tab_count(4, 'tabclose', 4) + call Check_tab_count(3, 'tabclose +', 3) + call Check_tab_count(3, 'tabclose -2', 2) + call Check_tab_count(1, 'tabclose! +1', 1) + call assert_equal(1, tabpagenr('$')) + call assert_equal('', bufname('')) + + call s:reconstruct_tabpage_for_test(6) + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + if n == 0 " pre count + let entire_cmd = c . 'tabclose' + let err_code = 'E16:' + else + let entire_cmd = 'tabclose ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + call assert_fails('3tabclose', 'E37:') + call assert_fails('tabclose 3', 'E37:') + call assert_fails('tabclose -+', 'E474:') + call assert_fails('tabclose +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! +endfunction + +" Test for [count] of tabonly +function Test_tabpage_with_tabonly() + + " Test for the normal behavior (pre count only) + let tc = [ [4, '.', '!'], [2, '.+', ''], [3, '.-2', '!'], [1, '.+1', '!'] ] + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = c[1] . 'tabonly' . c[2] + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + + " Test for the normal behavior + let tc2 = [ [3, '', ''], [1, '3', ''], [4, '4', '!'], [3, '1', '!'], + \ [2, '', '!'], + \ [2, '$', '!'], [3, '+', '!'], [3, '-2', '!'], [3, '+1', '!'] + \ ] + for n in range(2) + for c in tc2 + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c[1] . 'tabonly' . c[2] + else + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + endif + call Check_tab_count(c[0], entire_cmd, 1) + call assert_equal(1, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior + for n in range(2) + for c in ['0', '$3', '99', '+99', '-99'] + call s:reconstruct_tabpage_for_test(6) + if n == 0 " pre count + let entire_cmd = c . 'tabonly' + let err_code = 'E16:' + else + let entire_cmd = 'tabonly ' . c + let err_code = 'E474:' + endif + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + endfor + + " Test for the error behavior (post count only) + for c in tc + call s:reconstruct_tabpage_for_test(6) + let entire_cmd = 'tabonly' . c[2] . ' ' . c[1] + let err_code = 'E474:' + call assert_fails(entire_cmd, err_code) + call assert_equal(6, tabpagenr('$')) + endfor + + call assert_fails('tabonly -+', 'E474:') + call assert_fails('tabonly +2-', 'E474:') + call assert_equal(6, tabpagenr('$')) + + 1tabonly! + new + only! endfunction func Test_tabnext_on_buf_unload1() -- cgit From 7e23ce6b4fa843c12e44464a20154d92e95355c6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:34:25 +0100 Subject: lint --- src/nvim/ex_docmd.c | 168 ++++++++++++++++++++++++---------------------------- 1 file changed, 76 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4f34312562..2bd5158b33 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4314,102 +4314,86 @@ static int getargopt(exarg_T *eap) return OK; } -/* - * Handle the argument for a tabpage related ex command. - * Returns a tabpage number. - * When an error is encountered then eap->errmsg is set. - */ - static int -get_tabpage_arg(exarg_T *eap) -{ - int tab_number; - int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; - - if (eap->arg && *eap->arg != NUL) - { - char_u *p = eap->arg; - char_u *p_save; - int relative = 0; /* argument +N/-N means: go to N places to the - * right/left relative to the current position. */ - - if (*p == '-') - { - relative = -1; - p++; - } - else if (*p == '+') - { - relative = 1; - p++; - } - - p_save = p; - tab_number = getdigits(&p); - - if (relative == 0) - { - if (STRCMP(p, "$") == 0) - tab_number = LAST_TAB_NR; - else if (p == p_save || *p_save == '-' || *p != NUL - || tab_number > LAST_TAB_NR) - { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - goto theend; - } - } - else - { - if (*p_save == NUL) - tab_number = 1; - else if (p == p_save || *p_save == '-' || *p != NUL - || tab_number == 0) - { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - goto theend; - } - tab_number = tab_number * relative + tabpage_index(curtab); - if (!unaccept_arg0 && relative == -1) - --tab_number; - } - if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) - eap->errmsg = e_invarg; - } - else if (eap->addr_count > 0) - { - if (unaccept_arg0 && eap->line2 == 0) - eap->errmsg = e_invrange; - else - { - tab_number = eap->line2; - if (!unaccept_arg0 && **eap->cmdlinep == '-') - { - --tab_number; - if (tab_number < unaccept_arg0) - eap->errmsg = e_invarg; - } - } +/// Handle the argument for a tabpage related ex command. +/// Returns a tabpage number. +/// When an error is encountered then eap->errmsg is set. +static int get_tabpage_arg(exarg_T *eap) +{ + int tab_number; + int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; + + if (eap->arg && *eap->arg != NUL) { + char_u *p = eap->arg; + char_u *p_save; + int relative = 0; // argument +N/-N means: go to N places to the + // right/left relative to the current position. + + if (*p == '-') { + relative = -1; + p++; + } else if (*p == '+') { + relative = 1; + p++; } - else - { - switch (eap->cmdidx) - { - case CMD_tabnext: - tab_number = tabpage_index(curtab) + 1; - if (tab_number > LAST_TAB_NR) - tab_number = 1; - break; - case CMD_tabmove: - tab_number = LAST_TAB_NR; - break; - default: - tab_number = tabpage_index(curtab); - } + + p_save = p; + tab_number = getdigits(&p); + + if (relative == 0) { + if (STRCMP(p, "$") == 0) { + tab_number = LAST_TAB_NR; + } else if (p == p_save || *p_save == '-' || *p != NUL + || tab_number > LAST_TAB_NR) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + } else { + if (*p_save == NUL) { + tab_number = 1; + } + else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + // No numbers as argument. + eap->errmsg = e_invarg; + goto theend; + } + tab_number = tab_number * relative + tabpage_index(curtab); + if (!unaccept_arg0 && relative == -1) { + --tab_number; + } + } + if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) { + eap->errmsg = e_invarg; + } + } else if (eap->addr_count > 0) { + if (unaccept_arg0 && eap->line2 == 0) { + eap->errmsg = e_invrange; + } else { + tab_number = eap->line2; + if (!unaccept_arg0 && **eap->cmdlinep == '-') { + --tab_number; + if (tab_number < unaccept_arg0) { + eap->errmsg = e_invarg; + } + } } + } else { + switch (eap->cmdidx) { + case CMD_tabnext: + tab_number = tabpage_index(curtab) + 1; + if (tab_number > LAST_TAB_NR) + tab_number = 1; + break; + case CMD_tabmove: + tab_number = LAST_TAB_NR; + break; + default: + tab_number = tabpage_index(curtab); + } + } theend: - return tab_number; + return tab_number; } /* -- cgit From a24c6cc6a8e180d201c5887935d6dfdc95ac1abf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 17:38:30 +0100 Subject: get_tabpage_arg: satisfy clang warning vim-patch:8.0.0266 --- src/nvim/ex_docmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2bd5158b33..a06c6a89c8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4319,7 +4319,7 @@ static int getargopt(exarg_T *eap) /// When an error is encountered then eap->errmsg is set. static int get_tabpage_arg(exarg_T *eap) { - int tab_number; + int tab_number = 0; int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; if (eap->arg && *eap->arg != NUL) { @@ -4368,6 +4368,7 @@ static int get_tabpage_arg(exarg_T *eap) } else if (eap->addr_count > 0) { if (unaccept_arg0 && eap->line2 == 0) { eap->errmsg = e_invrange; + tab_number = 0; } else { tab_number = eap->line2; if (!unaccept_arg0 && **eap->cmdlinep == '-') { -- cgit From f06a6913260a320ec6b8ede5860edb1829c4f8ba Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:14:18 +0100 Subject: vim-patch:8.0.0037 Problem: Get E924 when switching tabs. () Solution: Use win_valid_any_tab() instead of win_valid(). (Martin Vuille, closes vim/vim#1167, closes vim/vim#1171) https://github.com/vim/vim/commit/0a9046fbcb33770517ab0220b8100c4494bddab2 --- src/nvim/quickfix.c | 2 +- src/nvim/testdir/test_quickfix.vim | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3f7975051f..8d8c20c1d0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1975,7 +1975,7 @@ win_found: ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); - if (qi != &ql_info && !win_valid(oldwin)) { + if (qi != &ql_info && !win_valid_any_tab(oldwin)) { EMSG(_("E924: Current window was closed")); is_abort = true; opened_window = false; diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 640918b343..75ab01f013 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -599,6 +599,22 @@ function Test_locationlist_curwin_was_closed() augroup! testgroup endfunction +function Test_locationlist_cross_tab_jump() + call writefile(['loclistfoo'], 'loclistfoo') + call writefile(['loclistbar'], 'loclistbar') + set switchbuf=usetab + + edit loclistfoo + tabedit loclistbar + silent lgrep loclistfoo loclist* + call assert_equal(1, tabpagenr()) + + enew | only | tabonly + set switchbuf&vim + call delete('loclistfoo') + call delete('loclistbar') +endfunction + " More tests for 'errorformat' function! Test_efm1() if !has('unix') -- cgit From 9c8540edfd52c77678eac84e48beb56171487b3f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:16:26 +0100 Subject: vim-patch:8.0.0159 References #5406 Problem: Using a NULL pointer when using feedkeys() to trigger drawing a tabline. Solution: Skip drawing a tabline if TabPageIdxs is NULL. (Dominique Pelle) Also fix recursing into getcmdline() from the cmd window. https://github.com/vim/vim/commit/c695cec4698b41d7b9555efdd47dda9b1945d3ae --- src/nvim/ex_getln.c | 2 ++ src/nvim/screen.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d99c8d02f7..d6e003a82f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -698,7 +698,9 @@ static int command_line_execute(VimState *state, int key) if (s->c == cedit_key || s->c == K_CMDWIN) { if (ex_normal_busy == 0 && got_int == false) { // Open a window to edit the command line (and history). + save_cmdline(&s->save_ccline); s->c = ex_window(); + restore_cmdline(&s->save_ccline); s->some_key_typed = true; } } else { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 280b8c65fe..baec18dd6f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6884,7 +6884,10 @@ static void draw_tabline(void) int use_sep_chars = (t_colors < 8 ); - redraw_tabline = FALSE; + if (ScreenLines == NULL) { + return; + } + redraw_tabline = false; if (tabline_height() < 1) -- cgit From b82e3358e006264187f104bb0321104621bcc811 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 19:23:12 +0100 Subject: vim-patch:8.0.0083 Problem: Using freed memory with win_getid(). (Domenique Pelle) Solution: For the current tab use curwin. https://github.com/vim/vim/commit/8e639052638a9bb8c7dd6e3e10776b1218cec1a3 --- src/nvim/testdir/test_window_id.vim | 9 +++++++++ src/nvim/window.c | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index 66656e1d0a..b3b506d04d 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -92,3 +92,12 @@ func Test_win_getid() only! endfunc + +func Test_win_getid_curtab() + tabedit X + tabfirst + copen + only + call assert_equal(win_getid(1), win_getid(1, 1)) + tabclose! +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 15482d9061..ce322e1185 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5821,7 +5821,11 @@ int win_getid(typval_T *argvars) if (tp == NULL) { return -1; } - wp = tp->tp_firstwin; + if (tp == curtab) { + wp = firstwin; + } else { + wp = tp->tp_firstwin; + } } for ( ; wp != NULL; wp = wp->w_next) { if (--winnr == 0) { -- cgit From 01bf78971cea43938e01439ae6d4687bc97196ea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 23:17:10 +0100 Subject: vim-patch:8.0.0172 Problem: The command selected in the command line window is not executed. (Andrey Starodubtsev) Solution: Save and restore the command line at a lower level. (closes vim/vim#1370) https://github.com/vim/vim/commit/1d669c233c97486555a34f7d3f069068d9ebdb63 --- src/nvim/ex_getln.c | 12 ++++-------- src/nvim/testdir/test_history.vim | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index d6e003a82f..2600f484dc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -698,9 +698,7 @@ static int command_line_execute(VimState *state, int key) if (s->c == cedit_key || s->c == K_CMDWIN) { if (ex_normal_busy == 0 && got_int == false) { // Open a window to edit the command line (and history). - save_cmdline(&s->save_ccline); s->c = ex_window(); - restore_cmdline(&s->save_ccline); s->some_key_typed = true; } } else { @@ -5229,10 +5227,8 @@ static int ex_window(void) invalidate_botline(); redraw_later(SOME_VALID); - /* Save the command line info, can be used recursively. */ - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; + // Save the command line info, can be used recursively. + save_cmdline(&save_ccline); /* No Ex mode here! */ exmode_active = 0; @@ -5266,8 +5262,8 @@ static int ex_window(void) /* Restore KeyTyped in case it is modified by autocommands */ KeyTyped = save_KeyTyped; - /* Restore the command line info. */ - ccline = save_ccline; + // Restore the command line info. + restore_cmdline(&save_ccline); cmdwin_type = 0; exmode_active = save_exmode; diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index ee6acfffc3..3163b344d3 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -63,3 +63,20 @@ function Test_History() call assert_equal(-1, histnr('abc')) call assert_fails('call histnr([])', 'E730:') endfunction + +function Test_Search_history_window() + new + call setline(1, ['a', 'b', 'a', 'b']) + 1 + call feedkeys("/a\", 'xt') + call assert_equal('a', getline('.')) + 1 + call feedkeys("/b\", 'xt') + call assert_equal('b', getline('.')) + 1 + " select the previous /a command + call feedkeys("q/kk\", 'x!') + call assert_equal('a', getline('.')) + call assert_equal('a', @/) + bwipe! +endfunc -- cgit From 33858ccb9b53d79f3dafbffef8ca7273be7de119 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 21 Mar 2017 23:36:27 +0100 Subject: vim-patch:8.0.0485 Problem: Not all windows commands are tested. Solution: Add more tests for windows commands. (Dominique Pelle, closes vim/vim#1575) Run test_autocmd separately, it interferes with other tests. Fix tests that depended on side effects. https://github.com/vim/vim/commit/4520d440c59034452d1450b27fcd56825c090687 --- src/nvim/testdir/Makefile | 1 + src/nvim/testdir/test_alot.vim | 1 - src/nvim/testdir/test_autocmd.vim | 2 + src/nvim/testdir/test_window_cmd.vim | 300 +++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index d948cd0f93..531a07912f 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -28,6 +28,7 @@ SCRIPTS ?= \ # Tests using runtest.vim. # Keep test_alot*.res as the last one, sort the others. NEW_TESTS ?= \ + test_autocmd.res \ test_bufwintabinfo.res \ test_cmdline.res \ test_command_count.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 9d1f07fc0b..9c7db9e397 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -2,7 +2,6 @@ " This makes testing go faster, since Vim doesn't need to restart. source test_assign.vim -source test_autocmd.vim source test_cursor_func.vim source test_execute_func.vim source test_ex_undo.vim diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 5b5232f06e..e8e563d8ad 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -320,6 +320,8 @@ func Test_three_windows() call assert_equal('Xanother', expand('%')) au! + enew + bwipe! Xtestje1 call delete('Xtestje1') call delete('Xtestje2') call delete('Xtestje3') diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 569a78a0ed..a5f27fba8a 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -67,4 +67,304 @@ function Test_window_cmd_wincmd_gf() augroup! test_window_cmd_wincmd_gf endfunc +func Test_window_quit() + e Xa + split Xb + call assert_equal(2, winnr('$')) + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + + wincmd q + call assert_equal(1, winnr('$')) + call assert_equal('Xa', bufname(winbufnr(1))) + + bw Xa Xb +endfunc + +func Test_window_horizontal_split() + call assert_equal(1, winnr('$')) + 3wincmd s + call assert_equal(2, winnr('$')) + call assert_equal(3, winheight(0)) + call assert_equal(winwidth(1), winwidth(2)) + + call assert_fails('botright topleft wincmd s', 'E442:') + bw +endfunc + +func Test_window_vertical_split() + call assert_equal(1, winnr('$')) + 3wincmd v + call assert_equal(2, winnr('$')) + call assert_equal(3, winwidth(0)) + call assert_equal(winheight(1), winheight(2)) + + call assert_fails('botright topleft wincmd v', 'E442:') + bw +endfunc + +func Test_window_split_edit_alternate() + e Xa + e Xb + + wincmd ^ + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + + bw Xa Xb +endfunc + +func Test_window_preview() + " Open a preview window + pedit Xa + call assert_equal(2, winnr('$')) + call assert_equal(0, &previewwindow) + + " Go to the preview window + wincmd P + call assert_equal(1, &previewwindow) + + " Close preview window + wincmd z + call assert_equal(1, winnr('$')) + call assert_equal(0, &previewwindow) + + call assert_fails('wincmd P', 'E441:') +endfunc + +func Test_window_exchange() + e Xa + + " Nothing happens with window exchange when there is 1 window + wincmd x + call assert_equal(1, winnr('$')) + + split Xb + split Xc + + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange current window 1 with window 3 + 3wincmd x + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the top window + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + " Exchange window with next when at the middle window + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Exchange window with next when at the bottom window. + " When there is no next window, it exchanges with the previous window. + wincmd j + wincmd x + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + bw Xa Xb Xc +endfunc + +func Test_window_rotate() + e Xa + split Xb + split Xc + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate downwards + wincmd r + call assert_equal('Xa', bufname(winbufnr(1))) + call assert_equal('Xc', bufname(winbufnr(2))) + call assert_equal('Xb', bufname(winbufnr(3))) + + 2wincmd r + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + " Rotate upwards + wincmd R + call assert_equal('Xb', bufname(winbufnr(1))) + call assert_equal('Xa', bufname(winbufnr(2))) + call assert_equal('Xc', bufname(winbufnr(3))) + + 2wincmd R + call assert_equal('Xc', bufname(winbufnr(1))) + call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xa', bufname(winbufnr(3))) + + bot vsplit + call assert_fails('wincmd R', 'E443:') + + bw Xa Xb Xc +endfunc + +func Test_window_height() + e Xa + split Xb + + let [wh1, wh2] = [winheight(1), winheight(2)] + " Active window (1) should have the same height or 1 more + " than the other window. + call assert_inrange(wh2, wh2 + 1, wh1) + + wincmd - + call assert_equal(wh1 - 1, winheight(1)) + call assert_equal(wh2 + 1, winheight(2)) + + wincmd + + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + call assert_equal(2, winheight(1)) + call assert_equal(wh1 + wh2 - 2, winheight(2)) + + wincmd = + call assert_equal(wh1, winheight(1)) + call assert_equal(wh2, winheight(2)) + + 2wincmd _ + set winfixheight + split Xc + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + call assert_equal(2, winheight(2)) + call assert_inrange(wh3, wh3 + 1, wh1) + 3wincmd + + call assert_equal(2, winheight(2)) + call assert_equal(wh1 + 3, winheight(1)) + call assert_equal(wh3 - 3, winheight(3)) + wincmd = + call assert_equal(2, winheight(2)) + call assert_equal(wh1, winheight(1)) + call assert_equal(wh3, winheight(3)) + + wincmd j + set winfixheight& + + wincmd = + let [wh1, wh2, wh3] = [winheight(1), winheight(2), winheight(3)] + " Current window (2) should have the same height or 1 more + " than the other windows. + call assert_inrange(wh1, wh1 + 1, wh2) + call assert_inrange(wh3, wh3 + 1, wh2) + + bw Xa Xb Xc +endfunc + +func Test_window_width() + e Xa + vsplit Xb + + let [ww1, ww2] = [winwidth(1), winwidth(2)] + " Active window (1) should have the same width or 1 more + " than the other window. + call assert_inrange(ww2, ww2 + 1, ww1) + + wincmd < + call assert_equal(ww1 - 1, winwidth(1)) + call assert_equal(ww2 + 1, winwidth(2)) + + wincmd > + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + call assert_equal(2, winwidth(1)) + call assert_equal(ww1 + ww2 - 2, winwidth(2)) + + wincmd = + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww2, winwidth(2)) + + 2wincmd | + set winfixwidth + vsplit Xc + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_inrange(ww3, ww3 + 1, ww1) + 3wincmd > + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1 + 3, winwidth(1)) + call assert_equal(ww3 - 3, winwidth(3)) + wincmd = + " FIXME: commented out: I would expect the width of 2nd window to + " remain 2 but it's actually 1?! + "call assert_equal(2, winwidth(2)) + call assert_equal(ww1, winwidth(1)) + call assert_equal(ww3, winwidth(3)) + + wincmd l + set winfixwidth& + + wincmd = + let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] + " Current window (2) should have the same width or 1 more + " than the other windows. + call assert_inrange(ww1, ww1 + 1, ww2) + call assert_inrange(ww3, ww3 + 1, ww2) + + bw Xa Xb Xc +endfunc + +func Test_window_jump_tag() + help + /iccf + call assert_match('^|iccf|', getline('.')) + call assert_equal(2, winnr('$')) + 2wincmd } + call assert_equal(3, winnr('$')) + call assert_match('^|iccf|', getline('.')) + wincmd k + call assert_match('\*iccf\*', getline('.')) + call assert_equal(2, winheight(0)) + + wincmd z + set previewheight=4 + help + /bugs + wincmd } + wincmd k + call assert_match('\*bugs\*', getline('.')) + call assert_equal(4, winheight(0)) + set previewheight& + + %bw! +endfunc + +func Test_window_newtab() + e Xa + + call assert_equal(1, tabpagenr('$')) + call assert_equal("\nAlready only one window", execute('wincmd T')) + + split Xb + split Xc + + wincmd T + call assert_equal(2, tabpagenr('$')) + call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(tabpagebuflist(2), 'bufname(v:val)')) + + %bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 51bc9f243a0b60205985b42bb47f174379a463c6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 00:22:45 +0100 Subject: ex_tabnext: Disallow "+NN" arg. Need to do this explicitly because our implementation of getdigits() is slightly different. --- src/nvim/ex_docmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a06c6a89c8..d815d57a60 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6602,7 +6602,8 @@ static void ex_tabnext(exarg_T *eap) char_u *p_save = p; tab_number = getdigits(&p); - if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL + || tab_number == 0) { // No numbers as argument. eap->errmsg = e_invarg; return; -- cgit From 2b32053559669580f51da3cad2258f65c3adf9c4 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 01:19:13 +0100 Subject: test/legacy: Test_three_windows: rtp for :help --- src/nvim/testdir/test_autocmd.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index e8e563d8ad..c4110eba94 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -314,6 +314,8 @@ func Test_three_windows() call assert_equal('Xanother', expand('%')) only + + helptags ALL help wincmd w 1quit -- cgit From f58a593cea3872c7b8b862375b6982436ee88ef2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 01:26:32 +0100 Subject: vim-patch:8.0.0483 Problem: Illegal memory access when using :all. (Dominique Pelle) Solution: Adjust the cursor position right after setting "curwin". https://github.com/vim/vim/commit/f79225ed4f81bc579bb3360ad2eb06adc8058153 --- src/nvim/testdir/test_window_cmd.vim | 12 ++++++++++++ src/nvim/window.c | 8 +++++--- 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index a5f27fba8a..188a7ed0f3 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -67,6 +67,18 @@ function Test_window_cmd_wincmd_gf() augroup! test_window_cmd_wincmd_gf endfunc +func Test_next_split_all() + " This was causing an illegal memory access. + n x + norm axxx + split + split + s/x + s/x + all + bwipe! +endfunc + func Test_window_quit() e Xa split Xb diff --git a/src/nvim/window.c b/src/nvim/window.c index ce322e1185..82e97ea00d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2018,10 +2018,12 @@ int win_close(win_T *win, int free_buf) } curbuf = curwin->w_buffer; close_curwin = TRUE; + + // The cursor position may be invalid if the buffer changed after last + // using the window. + check_cursor(); } - if (p_ea - && (*p_ead == 'b' || *p_ead == dir) - ) { + if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { win_equal(curwin, true, dir); } else { win_comp_pos(); -- cgit From 54f31187ba006049ba455a1eeea4781750a3958d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 02:13:54 +0100 Subject: vim-patch:8.0.0482 Problem: The setbufvar() function may mess up the window layout. (Kay Z.) Solution: Do not check the window to be valid if it is NULL. https://github.com/vim/vim/commit/2c90d51123fba44a90e09aa4a4f2b7d972dadb94 --- src/nvim/testdir/test_functions.vim | 31 +++++++++++++++++++++++++++++++ src/nvim/window.c | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_functions.vim (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim new file mode 100644 index 0000000000..81cb6314ce --- /dev/null +++ b/src/nvim/testdir/test_functions.vim @@ -0,0 +1,31 @@ +func Test_setbufvar_options() + " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the + " window layout. + call assert_equal(1, winnr('$')) + split dummy_preview + resize 2 + set winfixheight winfixwidth + let prev_id = win_getid() + + wincmd j + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf1', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + let dum1_id = win_getid() + + wincmd h + let wh = winheight('.') + let dummy_buf = bufnr('dummy_buf2', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight('.')) + + bwipe! + call win_gotoid(prev_id) + bwipe! + call win_gotoid(dum1_id) + bwipe! +endfunc + diff --git a/src/nvim/window.c b/src/nvim/window.c index 82e97ea00d..ebb998e3af 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5377,7 +5377,8 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) || (sn->fr_next != NULL && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) || (sn->fr_child != NULL - && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) + && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) + || (sn->fr_win != NULL && !win_valid(sn->fr_win))) return FAIL; return OK; } -- cgit From 41bffeacff885b4904a1f456c6d56e016ad6f88c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 02:20:06 +0100 Subject: vim-patch:8.0.0331 Problem: Restoring help snapshot accesses freed memory. (Dominique Pelle) Solution: Don't restore a snapshot when the window closes. https://github.com/vim/vim/commit/343b8c042967da82f2f022afa31f2c97a264c1c8 --- src/nvim/testdir/test_help.vim | 16 ++++++++++++++++ src/nvim/window.c | 6 ++---- 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/nvim/testdir/test_help.vim (limited to 'src') diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim new file mode 100644 index 0000000000..26edc16107 --- /dev/null +++ b/src/nvim/testdir/test_help.vim @@ -0,0 +1,16 @@ + +" Tests for :help + +func Test_help_restore_snapshot() + help + set buftype= + help + edit x + help + helpclose +endfunc + +func Test_help_errors() + call assert_fails('help doesnotexist', 'E149:') + call assert_fails('help!', 'E478:') +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index ebb998e3af..a34daccb89 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5365,10 +5365,8 @@ restore_snapshot ( clear_snapshot(curtab, idx); } -/* - * Check if frames "sn" and "fr" have the same layout, same following frames - * and same children. - */ +/// Check if frames "sn" and "fr" have the same layout, same following frames +/// and same children. And the window pointer is valid. static int check_snapshot_rec(frame_T *sn, frame_T *fr) { if (sn->fr_layout != fr->fr_layout -- cgit From 830b31683e9958130a64b9dec990f42b58efc0a7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 13:06:29 +0100 Subject: vim-patch:8.0.0502 Problem: Coverity complains about possible NULL pointer. Solution: Add an assert(), let's see if this works on all systems. https://github.com/vim/vim/commit/a37ffaa5e0a47e2db27bc0cc23f49e7094f47f3b --- src/nvim/window.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index a34daccb89..db6c0f9048 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3167,6 +3167,7 @@ void close_tabpage(tabpage_T *tab) ptp = ptp->tp_next) { // do nothing } + assert(ptp != NULL); ptp->tp_next = tab->tp_next; } -- cgit From c398402f1225e8050856260cbc6ae929ff2f5c31 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 22 Mar 2017 15:02:19 +0100 Subject: vim-patch:8.0.0307 Problem: Asan detects a memory error when EXITFREE is defined. (Dominique Pelle) Solution: In getvcol() check for ml_get_buf() returning an empty string. Also skip adjusting the scroll position. Set "exiting" in mch_exit() for all systems. https://github.com/vim/vim/commit/955f198fc546cc30a34361932d3f454a61df0efa --- src/nvim/charset.c | 5 +++++ src/nvim/window.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9e240fd38b..f31cef5886 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1160,6 +1160,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, // continue until the NUL posptr = NULL; } else { + // Special check for an empty line, which can happen on exit, when + // ml_get_buf() always returns an empty string. + if (*ptr == NUL) { + pos->col = 0; + } posptr = ptr + pos->col; } diff --git a/src/nvim/window.c b/src/nvim/window.c index db6c0f9048..dc0ce03794 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4803,7 +4803,11 @@ void win_new_height(win_T *wp, int height) wp->w_height = height; wp->w_skipcol = 0; - scroll_to_fraction(wp, prev_height); + // There is no point in adjusting the scroll position when exiting. Some + // values might be invalid. + if (!exiting) { + scroll_to_fraction(wp, prev_height); + } } void scroll_to_fraction(win_T *wp, int prev_height) -- cgit From 44e75eba30682007dd17f9a80805a9925a921976 Mon Sep 17 00:00:00 2001 From: raichoo Date: Wed, 22 Mar 2017 21:57:20 +0100 Subject: vim-patch:7.4.2161 (#6340) Problem: Expression test fails without conceal feature. Solution: Only check "conceal" with the conceal feature. https://github.com/vim/vim/commit/7ab6defcafe017a3ad58580a3e56dab705b1ed8b --- src/nvim/testdir/test_expr.vim | 8 ++++++-- src/nvim/version.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index ccbdde8244..2541d38d12 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -101,8 +101,12 @@ endfunc func Test_setmatches() hi def link 1 Comment hi def link 2 PreProc - let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}] - let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}] + let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4}] + let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4}] + if has('conceal') + let set[0]['conceal'] = 5 + let exp[0]['conceal'] = '5' + endif call setmatches(set) call assert_equal(exp, getmatches()) endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index 398a3e6542..cbb7902985 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -280,7 +280,7 @@ static int included_patches[] = { 2164, 2163, 2162, - // 2161, + 2161, 2160, 2159, 2158, -- cgit From 06ed7a189b2c1dca88f307538b9739b989776068 Mon Sep 17 00:00:00 2001 From: Jurica Bradarić Date: Thu, 23 Mar 2017 08:54:01 +0100 Subject: vim-patch:7.4.2329 (#6341) Problem: Error for min() and max() contains %s. (Nikolay Pavlov) Solution: Pass the function name. (closes vim/vim#1040) https://github.com/vim/vim/commit/26b84339fd8766898bcf6a259cbc2e0c38689726 --- src/nvim/eval.c | 5 +++-- src/nvim/testdir/test_expr.vim | 7 +++++++ src/nvim/version.c | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0872ed37ad..8f52717252 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13804,8 +13804,9 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) } } } - } else - EMSG(_(e_listdictarg)); + } else { + EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + } rettv->vval.v_number = error ? 0 : n; } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 2541d38d12..03a9155512 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -335,6 +335,13 @@ function Test_printf_errors() call assert_fails('echo printf("%d", 1.2)', 'E805:') endfunc +function Test_max_min_errors() + call assert_fails('call max(v:true)', 'E712:') + call assert_fails('call max(v:true)', 'max()') + call assert_fails('call min(v:true)', 'E712:') + call assert_fails('call min(v:true)', 'min()') +endfunc + func Test_substitute_expr() let g:val = 'XXX' call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', '')) diff --git a/src/nvim/version.c b/src/nvim/version.c index cbb7902985..32ef2c2fdb 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -112,7 +112,7 @@ static int included_patches[] = { // 2332 NA 2331, // 2330, - // 2329, + 2329, // 2328, // 2327 NA 2326, -- cgit From b2b88423aa0087e5d4aaa122e63dffb97f85222f Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Mon, 27 Feb 2017 09:46:50 +0000 Subject: Robustly handle folds during a :move command In order to re-order marks according to the :move command, do_move() uses mark_adjust() in a non-standard manner. The non-standard action is that it moves some marks *past* other marks. This doesn't matter for marks, but mark_adjust() calls foldMarkAdjust() which simply changes fold starts and lengths and doesn't have enough information to know that other folds have to be checked and reordered. The array of folds for each window are assumed to be in order of increasing line number, and if this gets broken some folds can get "lost". There has been a previous patch to avoid this problem by deleting and recalculating all folds in the window, but this comes at the cost of closing all folds when executing :move, and doesn't cover the case of manual folds. This patch adds a new function foldMoveRange() specifically for the :move command that handles reordering folds as well as simply moving them. Additionally, we allow calling mark_adjust_nofold() that does the same as mark_adjust() but doesn't affect any fold array. Calling mark_adjust_nofold() should be done in the same manner as calling mark_adjust(), but according changes to the fold arrays must be done seperately by the calling function. vim-patch:8.0.0457 vim-patch:8.0.0459 vim-patch:8.0.0461 vim-patch:8.0.0465 --- src/nvim/ex_cmds.c | 35 +++++----- src/nvim/fold.c | 148 +++++++++++++++++++++++++++++++++++++++++ src/nvim/mark.c | 22 +++++- src/nvim/testdir/test_fold.vim | 141 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b83677807..c560ff9210 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -756,14 +756,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T num_lines; // Num lines moved linenr_T last_line; // Last line in file after adding new text - // Moving lines seems to corrupt the folds, delete folding info now - // and recreate it when finished. Don't do this for manual folding, it - // would delete all folds. - bool isFolded = hasAnyFolding(curwin) && !foldmethodIsManual(curwin); - if (isFolded) { - deleteFoldRecurse(&curwin->w_folds); - } - if (dest >= line1 && dest < line2) { EMSG(_("E134: Move lines into themselves")); return FAIL; @@ -801,21 +793,29 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) * their final destination at the new text position -- webb */ last_line = curbuf->b_ml.ml_line_count; - mark_adjust(line1, line2, last_line - line2, 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); + mark_adjust_nofold(line1, line2, last_line - line2, 0L); if (dest >= line2) { - mark_adjust(line2 + 1, dest, -num_lines, 0L); + mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, line1, line2, dest); + } + } curbuf->b_op_start.lnum = dest - num_lines + 1; curbuf->b_op_end.lnum = dest; } else { - mark_adjust(dest + 1, line1 - 1, num_lines, 0L); + mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L); + FOR_ALL_TAB_WINDOWS(tab, win) { + if (win->w_buffer == curbuf) { + foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); + } + } curbuf->b_op_start.lnum = dest + 1; curbuf->b_op_end.lnum = dest + num_lines; } curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - mark_adjust(last_line - num_lines + 1, last_line, - -(last_line - dest - extra), 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); + mark_adjust_nofold(last_line - num_lines + 1, last_line, + -(last_line - dest - extra), 0L); /* * Now we delete the original text -- webb @@ -851,11 +851,6 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) changed_lines(dest + 1, 0, line1 + num_lines, 0L); } - // recreate folds - if (isFolded) { - foldUpdateAll(curwin); - } - return OK; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7c0283971e..e2d1b017b6 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2597,6 +2597,154 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) } } +// foldMoveRange() {{{2 +static void reverse_fold_order(garray_T *gap, size_t start, size_t end) +{ + for (; start < end; start++, end--) { + fold_T *left = (fold_T *)gap->ga_data + start; + fold_T *right = (fold_T *)gap->ga_data + end; + fold_T tmp = *left; + *left = *right; + *right = tmp; + } +} + +// Move folds within the inclusive range "line1" to "line2" to after "dest" +// require "line1" <= "line2" <= "dest" +// +// There are the following situations for the first fold at or below line1 - 1. +// 1 2 3 4 +// 1 2 3 4 +// line1 2 3 4 +// 2 3 4 5 6 7 +// line2 3 4 5 6 7 +// 3 4 6 7 8 9 +// dest 4 7 8 9 +// 4 7 8 10 +// 4 7 8 10 +// +// In the following descriptions, "moved" means moving in the buffer, *and* in +// the fold array. +// Meanwhile, "shifted" just means moving in the buffer. +// 1. not changed +// 2. truncated above line1 +// 3. length reduced by line2 - line1, folds starting between the end of 3 and +// dest are truncated and shifted up +// 4. internal folds moved (from [line1, line2] to dest) +// 5. moved to dest. +// 6. truncated below line2 and moved. +// 7. length reduced by line2 - dest, folds starting between line2 and dest are +// removed, top is moved down by move_len. +// 8. truncated below dest and shifted up. +// 9. shifted up +// 10. not changed +static void truncate_fold(fold_T *fp, linenr_T end) +{ + // I want to stop *at here*, foldRemove() stops *above* top + end += 1; + foldRemove(&fp->fd_nested, end - fp->fd_top, MAXLNUM); + fp->fd_len = end - fp->fd_top; +} + +#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) +void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, + const linenr_T dest) +{ + fold_T *fp; + const linenr_T range_len = line2 - line1 + 1; + const linenr_T move_len = dest - line2; + const bool at_start = foldFind(gap, line1 - 1, &fp); + + if (at_start) { + if (fold_end(fp) > dest) { + // Case 4 -- don't have to change this fold, but have to move nested + // folds. + foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - + fp->fd_top, dest - fp->fd_top); + return; + } else if (fold_end(fp) > line2) { + // Case 3 -- Remove nested folds between line1 and line2 & reduce the + // length of fold by "range_len". + // Folds after this one must be dealt with. + foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, + line2 - fp->fd_top, MAXLNUM, -range_len); + fp->fd_len -= range_len; + } else { + // Case 2 -- truncate fold *above* line1. + // Folds after this one must be dealt with. + truncate_fold(fp, line1 - 1); + } + // Look at the next fold, and treat that one as if it were the first after + // "line1" (because now it is). + fp = fp + 1; + } + + if (!valid_fold(fp, gap) || fp->fd_top > dest) { + // No folds after "line1" and before "dest" + // Case 10. + return; + } else if (fp->fd_top > line2) { + for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) { + // Case 9. (for all case 9's) -- shift up. + fp->fd_top -= range_len; + } + if (valid_fold(fp, gap) && fp->fd_top <= dest) { + // Case 8. -- ensure truncated at dest, shift up + truncate_fold(fp, dest); + fp->fd_top -= range_len; + } + return; + } else if (fold_end(fp) > dest) { + // Case 7 -- remove nested folds and shrink + foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, + dest - fp->fd_top, MAXLNUM, -move_len); + fp->fd_len -= move_len; + fp->fd_top += move_len; + return; + } + + // Case 5 or 6: changes rely on whether there are folds between the end of + // this fold and "dest". + size_t move_start = fold_index(fp, gap); + size_t move_end = 0, dest_index = 0; + for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) { + if (fp->fd_top <= line2) { + // 5, or 6 + if (fold_end(fp) > line2) { + // 6, truncate before moving + truncate_fold(fp, line2); + } + fp->fd_top += move_len; + continue; + } + + // Record index of the first fold after the moved range. + if (move_end == 0) { + move_end = fold_index(fp, gap); + } + + if (fold_end(fp) > dest) { + truncate_fold(fp, dest); + } + + fp->fd_top -= range_len; + } + dest_index = fold_index(fp, gap); + + // All folds are now correct, but they are not necessarily in the correct + // order. + // We have to swap folds in the range [move_end, dest_index) with those in + // the range [move_start, move_end). + reverse_fold_order(gap, move_start, dest_index - 1); + reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1); + reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1); +} +#undef fold_end +#undef valid_fold +#undef fold_index + /* foldMerge() {{{2 */ /* * Merge two adjacent folds (and the nested ones in them). diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 4e05845eb5..de2fdd7f13 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -886,6 +886,23 @@ void ex_changes(exarg_T *eap) * or: mark_adjust(56, 55, MAXLNUM, 2); */ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) +{ + mark_adjust_internal(line1, line2, amount, amount_after, true); +} + +// mark_adjust_nofold() does the same as mark_adjust() but without adjusting +// folds in any way. Folds must be adjusted manually by the caller. +// This is only useful when folds need to be moved in a way different to +// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after, +// for an example of why this may be necessary, see do_move(). +void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, + long amount_after) +{ + mark_adjust_internal(line1, line2, amount, amount_after, false); +} + +static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, + long amount_after, bool adjust_folds) { int i; int fnum = curbuf->b_fnum; @@ -1011,8 +1028,9 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) } } - /* adjust folds */ - foldMarkAdjust(win, line1, line2, amount, amount_after); + if (adjust_folds) { + foldMarkAdjust(win, line1, line2, amount, amount_after); + } } } diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 7cb9faa75f..de07a05a2c 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -1,5 +1,9 @@ " Test for folding +func! PrepIndent(arg) + return [a:arg] + repeat(["\t".a:arg], 5) +endfu + func! Test_address_fold() new call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', @@ -128,3 +132,140 @@ func Test_manual_fold_with_filter() call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) bwipe! endfunc + +func! Test_move_folds_around_manual() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set foldenable foldlevel=0 fdm=indent + " needs a forced redraw + redraw! + set fdm=manual + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + redraw! + set fdm=manual + call cursor(2, 1) + %foldopen + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + redraw! + set fdm=manual + %foldopen + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(0, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, -1, 3, 3, 3, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(5, foldclosedend(2)) + call assert_equal(0, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + " Ensure moving around the edges still works. + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + set fdm=manual + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 0], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! +endfunc + +func! Test_move_folds_around_indent() + new + let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + let folds=[-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14] + " all folds closed + set fdm=indent + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + call assert_equal(input, getline(1, '$')) + 7,12m0 + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + 10,12m0 + call assert_equal(PrepIndent("a")[1:] + PrepIndent("b") + ["a"] + PrepIndent("c"), getline(1, '$')) + call assert_equal([1, 1, 1, 1, 1, -1, 7, 7, 7, 7, 7, -1, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + " moving should not close the folds + %d + call setline(1, PrepIndent("a") + PrepIndent("b") + PrepIndent("c")) + set fdm=indent + call cursor(2, 1) + %foldopen + 7,12m0 + let folds=repeat([-1], 18) + call assert_equal(PrepIndent("b") + PrepIndent("a") + PrepIndent("c"), getline(1, '$')) + call assert_equal(folds, map(range(1, line('$')), 'foldclosed(v:val)')) + norm! zM + " folds are not corrupted and all have been closed + call assert_equal([-1, 2, 2, 2, 2, 2, -1, 8, 8, 8, 8, 8, -1, 14, 14, 14, 14, 14], map(range(1, line('$')), 'foldclosed(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te"]) + set fdm=indent + %foldopen + 3m4 + %foldclose + call assert_equal(["a", "\tb", "\td", "\tc", "\te"], getline(1, '$')) + call assert_equal([-1, 5, 5, 5, 5], map(range(1, line('$')), 'foldclosedend(v:val)')) + %d + call setline(1, ["a", "\tb", "\tc", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"]) + set fdm=indent foldlevel=0 + %foldopen + 3m1 + %foldclose + call assert_equal(["a", "\tc", "\tb", "\td", "\te", "z", "\ty", "\tx", "\tw", "\tv"], getline(1, '$')) + call assert_equal(1, foldlevel(2)) + call assert_equal(5, foldclosedend(3)) + call assert_equal([-1, 2, 2, 2, 2, -1, 7, 7, 7, 7], map(range(1, line('$')), 'foldclosed(v:val)')) + 2,6m$ + %foldclose + call assert_equal(9, foldclosedend(2)) + call assert_equal(1, foldlevel(6)) + call assert_equal(9, foldclosedend(7)) + call assert_equal([-1, 2, 2, 2, 2, 2, 2, 2, 2, -1], map(range(1, line('$')), 'foldclosed(v:val)')) + " Ensure moving around the edges still works. + %d + call setline(1, PrepIndent("a") + repeat(["a"], 3) + ["\ta"]) + set fdm=indent foldlevel=0 + %foldopen + 6m$ + " The first fold has been truncated to the 5'th line. + " Second fold has been moved up because the moved line is now below it. + call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)')) + bw! +endfunc -- cgit From 308a953e0b892a0d59467e55227e972dbe51983c Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 22 Mar 2017 16:41:00 +0000 Subject: Fix wrap-around in 32 bit --- src/nvim/fold.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/fold.c b/src/nvim/fold.c index e2d1b017b6..5835da2e94 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2507,6 +2507,8 @@ static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot) fp = (fold_T *)gap->ga_data + i; fp[1].fd_top = bot + 1; + // check for wrap around (MAXLNUM, and 32bit) + assert(fp[1].fd_top > bot); fp[1].fd_len = fp->fd_len - (fp[1].fd_top - fp->fd_top); fp[1].fd_flags = fp->fd_flags; fp[1].fd_small = MAYBE; @@ -2555,34 +2557,35 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) { fold_T *fp = NULL; - if (bot < top) - return; /* nothing to do */ + if (bot < top) { + return; // nothing to do + } for (;; ) { - /* Find fold that includes top or a following one. */ + // Find fold that includes top or a following one. if (foldFind(gap, top, &fp) && fp->fd_top < top) { - /* 2: or 3: need to delete nested folds */ + // 2: or 3: need to delete nested folds foldRemove(&fp->fd_nested, top - fp->fd_top, bot - fp->fd_top); - if (fp->fd_top + fp->fd_len > bot + 1) { - /* 3: need to split it. */ + if (fp->fd_top + fp->fd_len - 1 > bot) { + // 3: need to split it. foldSplit(gap, (int)(fp - (fold_T *)gap->ga_data), top, bot); } else { - /* 2: truncate fold at "top". */ + // 2: truncate fold at "top". fp->fd_len = top - fp->fd_top; } - fold_changed = TRUE; + fold_changed = true; continue; } if (fp >= (fold_T *)(gap->ga_data) + gap->ga_len || fp->fd_top > bot) { - /* 6: Found a fold below bot, can stop looking. */ + // 6: Found a fold below bot, can stop looking. break; } if (fp->fd_top >= top) { - /* Found an entry below top. */ - fold_changed = TRUE; + // Found an entry below top. + fold_changed = true; if (fp->fd_top + fp->fd_len - 1 > bot) { - /* 5: Make fold that includes bot start below bot. */ + // 5: Make fold that includes bot start below bot. foldMarkAdjustRecurse(&fp->fd_nested, (linenr_T)0, (long)(bot - fp->fd_top), (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); @@ -2591,8 +2594,8 @@ static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) break; } - /* 4: Delete completely contained fold. */ - deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), TRUE); + // 4: Delete completely contained fold. + deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), true); } } } -- cgit From a6c9c91841a9c3a63196ec5b82c09308c8e5b78d Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Thu, 23 Mar 2017 14:16:21 +0000 Subject: vim-patch:8.0.0407 : filtering folds with marker method not tested Problem: Filtering folds with marker method not tested. Solution: Also set 'foldmethod' to "marker". --- src/nvim/testdir/test_fold.vim | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index de07a05a2c..976c6b5cd1 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -120,17 +120,22 @@ func Test_manual_fold_with_filter() if !executable('cat') return endif - new - call setline(1, range(1, 20)) - 4,$fold - %foldopen - 10,$fold - %foldopen - " This filter command should not have an effect - 1,8! cat - call feedkeys('5ggzdzMGdd', 'xt') - call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) - bwipe! + for type in ['manual', 'marker'] + exe 'set foldmethod=' . type + new + call setline(1, range(1, 20)) + 4,$fold + %foldopen + 10,$fold + %foldopen + " This filter command should not have an effect + 1,8! cat + call feedkeys('5ggzdzMGdd', 'xt') + call assert_equal(['1', '2', '3', '4', '5', '6', '7', '8', '9'], getline(1, '$')) + + bwipe! + set foldmethod& + endfor endfunc func! Test_move_folds_around_manual() -- cgit From 7214d0bc846179a862e8d3061d00270a6caa0d7b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 23 Mar 2017 14:07:51 +0100 Subject: XXX: ex_tabonly(): aucmd_win is not part of the window list. During free_all_mem, somehow ex_tabonly() may free aucmd_win. But it isn't fully destroyed (maybe autocmd_busy?). When win_free_all() tries to free aucmd_win directly, it double-frees the sub-fields. Tried unnsuccessfully to work around this by invoking `:tabonly!` with autocmds disabled: diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a12..91c845e94d22 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -565,9 +565,9 @@ void free_all_mem(void) /* Close all tabs and windows. Reset 'equalalways' to avoid redraws. */ p_ea = false; if (first_tabpage->tp_next != NULL) - do_cmdline_cmd("tabonly!"); + do_cmdline_cmd("noautocmd tabonly!"); if (firstwin != lastwin) - do_cmdline_cmd("only!"); + do_cmdline_cmd("noautocmd only!"); /* Free all spell info. */ spell_free_all(); --- src/nvim/ex_docmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d815d57a60..486baaad47 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6081,6 +6081,9 @@ static void ex_tabonly(exarg_T *eap) // Repeat this up to a 1000 times, because autocommands may // mess up the lists. for (int done = 0; done < 1000; done++) { + FOR_ALL_TAB_WINDOWS(tp, wp) { + assert(wp != aucmd_win); + } FOR_ALL_TABS(tp) { if (tp->tp_topframe != topframe) { tabpage_close_other(tp, eap->forceit); -- cgit From 2a6d44ca5232d5fbfa0401451b3ebce60e93ed4d Mon Sep 17 00:00:00 2001 From: James McCoy Date: Fri, 24 Mar 2017 09:36:09 -0400 Subject: vim-patch:8.0.0250 Problem: When virtcol() gets a column that is not the first byte of a multi-byte character the result is unpredictable. (Christian Ludwig) Solution: Correct the column to the first byte of a multi-byte character. Change the utf-8 test to new style. https://github.com/vim/vim/commit/0c0590d9827cb07a33c1552cb3558b94bddcb4dc Closes #6269 --- src/nvim/charset.c | 1 + src/nvim/testdir/test_alot.vim | 1 + src/nvim/testdir/test_utf8.vim | 65 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/nvim/testdir/test_utf8.vim (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f31cef5886..efe32b915f 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1166,6 +1166,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, pos->col = 0; } posptr = ptr + pos->col; + posptr -= utf_head_off(line, posptr); } // This function is used very often, do some speed optimizations. diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 9c7db9e397..baf49b7ff7 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -28,4 +28,5 @@ source test_tagcase.vim source test_tagjump.vim source test_true_false.vim source test_unlet.vim +source test_utf8.vim source test_window_cmd.vim diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim new file mode 100644 index 0000000000..24e3db86fb --- /dev/null +++ b/src/nvim/testdir/test_utf8.vim @@ -0,0 +1,65 @@ +" Tests for Unicode manipulations +if !has('multi_byte') + finish +endif + + +" Visual block Insert adjusts for multi-byte char +func Test_visual_block_insert() + new + call setline(1, ["aaa", "あああ", "bbb"]) + exe ":norm! gg0l\jjIx\" + call assert_equal(['axaa', 'xあああ', 'bxbb'], getline(1, '$')) + bwipeout! +endfunc + +" Test for built-in function strchars() +func Test_strchars() + let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"] + let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] + for i in range(len(inp)) + call assert_equal(exp[i][0], strchars(inp[i])) + call assert_equal(exp[i][1], strchars(inp[i], 0)) + call assert_equal(exp[i][2], strchars(inp[i], 1)) + endfor +endfunc + +" Test for customlist completion +function! CustomComplete1(lead, line, pos) + return ['あ', 'い'] +endfunction + +function! CustomComplete2(lead, line, pos) + return ['あたし', 'あたま', 'あたりめ'] +endfunction + +function! CustomComplete3(lead, line, pos) + return ['Nこ', 'Nん', 'Nぶ'] +endfunction + +func Test_customlist_completion() + command -nargs=1 -complete=customlist,CustomComplete1 Test1 echo + call feedkeys(":Test1 \\\"\", 'itx') + call assert_equal('"Test1 ', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete2 Test2 echo + call feedkeys(":Test2 \\\"\", 'itx') + call assert_equal('"Test2 あた', getreg(':')) + + command -nargs=1 -complete=customlist,CustomComplete3 Test3 echo + call feedkeys(":Test3 \\\"\", 'itx') + call assert_equal('"Test3 N', getreg(':')) + + call garbagecollect(1) +endfunc + +" Yank one 3 byte character and check the mark columns. +func Test_getvcol() + new + call setline(1, "x\u2500x") + normal 0lvy + call assert_equal(2, col("'[")) + call assert_equal(4, col("']")) + call assert_equal(2, virtcol("'[")) + call assert_equal(2, virtcol("']")) +endfunc -- cgit From 90ac8b09f56bf5a53ca9e1ddba0a5f92e4df7fa9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 25 Mar 2017 00:15:31 +0100 Subject: fold.c: uppercase macros --- src/nvim/fold.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 5835da2e94..36a5b0efd7 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2649,9 +2649,9 @@ static void truncate_fold(fold_T *fp, linenr_T end) fp->fd_len = end - fp->fd_top; } -#define fold_end(fp) ((fp)->fd_top + (fp)->fd_len - 1) -#define valid_fold(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) -#define fold_index(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) +#define FOLD_END(fp) ((fp)->fd_top + (fp)->fd_len - 1) +#define VALID_FOLD(fp, gap) ((fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) +#define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, const linenr_T dest) { @@ -2661,13 +2661,13 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, const bool at_start = foldFind(gap, line1 - 1, &fp); if (at_start) { - if (fold_end(fp) > dest) { + if (FOLD_END(fp) > dest) { // Case 4 -- don't have to change this fold, but have to move nested // folds. foldMoveRange(&fp->fd_nested, line1 - fp->fd_top, line2 - fp->fd_top, dest - fp->fd_top); return; - } else if (fold_end(fp) > line2) { + } else if (FOLD_END(fp) > line2) { // Case 3 -- Remove nested folds between line1 and line2 & reduce the // length of fold by "range_len". // Folds after this one must be dealt with. @@ -2684,22 +2684,22 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, fp = fp + 1; } - if (!valid_fold(fp, gap) || fp->fd_top > dest) { + if (!VALID_FOLD(fp, gap) || fp->fd_top > dest) { // No folds after "line1" and before "dest" // Case 10. return; } else if (fp->fd_top > line2) { - for (; valid_fold(fp, gap) && fold_end(fp) <= dest; fp++) { + for (; VALID_FOLD(fp, gap) && FOLD_END(fp) <= dest; fp++) { // Case 9. (for all case 9's) -- shift up. fp->fd_top -= range_len; } - if (valid_fold(fp, gap) && fp->fd_top <= dest) { + if (VALID_FOLD(fp, gap) && fp->fd_top <= dest) { // Case 8. -- ensure truncated at dest, shift up truncate_fold(fp, dest); fp->fd_top -= range_len; } return; - } else if (fold_end(fp) > dest) { + } else if (FOLD_END(fp) > dest) { // Case 7 -- remove nested folds and shrink foldMarkAdjustRecurse(&fp->fd_nested, line2 + 1 - fp->fd_top, dest - fp->fd_top, MAXLNUM, -move_len); @@ -2710,12 +2710,12 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, // Case 5 or 6: changes rely on whether there are folds between the end of // this fold and "dest". - size_t move_start = fold_index(fp, gap); + size_t move_start = FOLD_INDEX(fp, gap); size_t move_end = 0, dest_index = 0; - for (; valid_fold(fp, gap) && fp->fd_top <= dest; fp++) { + for (; VALID_FOLD(fp, gap) && fp->fd_top <= dest; fp++) { if (fp->fd_top <= line2) { // 5, or 6 - if (fold_end(fp) > line2) { + if (FOLD_END(fp) > line2) { // 6, truncate before moving truncate_fold(fp, line2); } @@ -2725,16 +2725,16 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, // Record index of the first fold after the moved range. if (move_end == 0) { - move_end = fold_index(fp, gap); + move_end = FOLD_INDEX(fp, gap); } - if (fold_end(fp) > dest) { + if (FOLD_END(fp) > dest) { truncate_fold(fp, dest); } fp->fd_top -= range_len; } - dest_index = fold_index(fp, gap); + dest_index = FOLD_INDEX(fp, gap); // All folds are now correct, but they are not necessarily in the correct // order. @@ -2744,9 +2744,9 @@ void foldMoveRange(garray_T *gap, const linenr_T line1, const linenr_T line2, reverse_fold_order(gap, move_start, move_start + dest_index - move_end - 1); reverse_fold_order(gap, move_start + dest_index - move_end, dest_index - 1); } -#undef fold_end -#undef valid_fold -#undef fold_index +#undef FOLD_END +#undef VALID_FOLD +#undef FOLD_INDEX /* foldMerge() {{{2 */ /* -- cgit From 098e91400eb06d29c31264ba973ea8a563703059 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Sat, 25 Mar 2017 14:43:19 +0000 Subject: refactor: Remove allow_keys global (#6346) * The allow_keys global is unused in nvim, remove it * clint --- src/nvim/digraph.c | 4 ---- src/nvim/edit.c | 36 ++++++++++++++++-------------------- src/nvim/eval.c | 6 ++---- src/nvim/ex_cmds.c | 6 ++---- src/nvim/ex_getln.c | 12 ++++-------- src/nvim/getchar.c | 27 ++++++++++----------------- src/nvim/globals.h | 12 +++++------- src/nvim/message.c | 23 +++++++++++------------ src/nvim/misc1.c | 18 +++++++----------- src/nvim/normal.c | 38 +++++++++++++------------------------- src/nvim/window.c | 11 +++++------ 11 files changed, 75 insertions(+), 118 deletions(-) (limited to 'src') diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 6ba6e659a6..560205fe7d 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1448,10 +1448,8 @@ int get_digraph(int cmdline) { int cc; no_mapping++; - allow_keys++; int c = plain_vgetc(); no_mapping--; - allow_keys--; if (c != ESC) { // ESC cancels CTRL-K @@ -1468,10 +1466,8 @@ int get_digraph(int cmdline) add_to_showcmd(c); } no_mapping++; - allow_keys++; cc = plain_vgetc(); no_mapping--; - allow_keys--; if (cc != ESC) { // ESC cancels CTRL-K diff --git a/src/nvim/edit.c b/src/nvim/edit.c index aa029c38a2..5544f0b163 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -689,11 +689,9 @@ static int insert_execute(VimState *state, int key) if (s->c == Ctrl_BSL) { // may need to redraw when no more chars available now ins_redraw(false); - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) { // it's something else vungetc(s->c); @@ -8303,17 +8301,16 @@ static int ins_digraph(void) } - /* don't map the digraph chars. This also prevents the - * mode message to be deleted when ESC is hit */ - ++no_mapping; - ++allow_keys; + // don't map the digraph chars. This also prevents the + // mode message to be deleted when ESC is hit + no_mapping++; c = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the next - * line and will not be removed by the redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the next + // line and will not be removed by the redraw edit_unputchar(); + } if (IS_SPECIAL(c) || mod_mask) { /* special key */ clear_showcmd(); @@ -8333,15 +8330,14 @@ static int ins_digraph(void) } add_to_showcmd_c(c); } - ++no_mapping; - ++allow_keys; + no_mapping++; cc = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the - * next line and will not be removed by a redraw */ + no_mapping--; + if (did_putchar) { + // when the line fits in 'columns' the '?' is at the start of the + // next line and will not be removed by a redraw edit_unputchar(); + } if (cc != ESC) { AppendToRedobuff((char_u *)CTRL_V_STR); c = getdigraph(c, cc, TRUE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8f52717252..d49fcbf17e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10596,8 +10596,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; int error = FALSE; - ++no_mapping; - ++allow_keys; + no_mapping++; for (;; ) { // Position the cursor. Needed after a message that ends in a space, // or if event processing caused a redraw. @@ -10630,8 +10629,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; break; } - --no_mapping; - --allow_keys; + no_mapping--; vimvars[VV_MOUSE_WIN].vv_nr = 0; vimvars[VV_MOUSE_WINID].vv_nr = 0; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 852d930b0a..678102daf6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3577,11 +3577,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) ui_cursor_goto(msg_row, msg_col); RedrawingDisabled = temp; - ++no_mapping; /* don't map this key */ - ++allow_keys; /* allow special keys */ + no_mapping++; // don't map this key typed = plain_vgetc(); - --allow_keys; - --no_mapping; + no_mapping--; /* clear the question */ msg_didout = FALSE; /* don't scroll up */ diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 2600f484dc..58979c0e43 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -621,11 +621,9 @@ static int command_line_execute(VimState *state, int key) // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert // mode when 'insertmode' is set, CTRL-\ e prompts for an expression. if (s->c == Ctrl_BSL) { - ++no_mapping; - ++allow_keys; + no_mapping++; s->c = plain_vgetc(); - --no_mapping; - --allow_keys; + no_mapping--; // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' @@ -1887,8 +1885,7 @@ getexmodeline ( msg_putchar(' '); } } - ++no_mapping; - ++allow_keys; + no_mapping++; /* * Get the line, one character at a time. @@ -2078,8 +2075,7 @@ redraw: } } - --no_mapping; - --allow_keys; + no_mapping--; /* make following messages go to the next line */ msg_didout = FALSE; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index bae8ae6d91..0c131d7b33 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1389,27 +1389,20 @@ int vgetc(void) for (;; ) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read - ++no_mapping; - ++allow_keys; + no_mapping++; did_inc = true; // mod_mask may change value } c = vgetorpeek(true); if (did_inc) { - --no_mapping; - --allow_keys; + no_mapping--; } - /* Get two extra bytes for special keys */ - if (c == K_SPECIAL - ) { - int save_allow_keys = allow_keys; - - ++no_mapping; - allow_keys = 0; /* make sure BS is not found */ - c2 = vgetorpeek(TRUE); /* no mapping for these chars */ - c = vgetorpeek(TRUE); - --no_mapping; - allow_keys = save_allow_keys; + // Get two extra bytes for special keys + if (c == K_SPECIAL) { + no_mapping++; + c2 = vgetorpeek(true); // no mapping for these chars + c = vgetorpeek(true); + no_mapping--; if (c2 == KS_MODIFIER) { mod_mask = c; continue; @@ -1487,7 +1480,7 @@ int vgetc(void) buf[i] = CSI; } } - --no_mapping; + no_mapping--; c = (*mb_ptr2char)(buf); } @@ -1570,7 +1563,7 @@ int char_avail(void) no_mapping++; retval = vpeekc(); - --no_mapping; + no_mapping--; return retval != NUL; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f8c7c9d330..ad14ec4f8c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -832,13 +832,11 @@ EXTERN int ex_no_reprint INIT(= FALSE); /* no need to print after z or p */ EXTERN int Recording INIT(= FALSE); /* TRUE when recording into a reg. */ EXTERN int Exec_reg INIT(= FALSE); /* TRUE when executing a register */ -EXTERN int no_mapping INIT(= FALSE); /* currently no mapping allowed */ -EXTERN int no_zero_mapping INIT(= 0); /* mapping zero not allowed */ -EXTERN int allow_keys INIT(= FALSE); /* allow key codes when no_mapping - * is set */ -EXTERN int no_u_sync INIT(= 0); /* Don't call u_sync() */ -EXTERN int u_sync_once INIT(= 0); /* Call u_sync() once when evaluating - an expression. */ +EXTERN int no_mapping INIT(= false); // currently no mapping allowed +EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed +EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() +EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating + // an expression. EXTERN bool force_restart_edit INIT(= false); // force restart_edit after // ex_normal returns diff --git a/src/nvim/message.c b/src/nvim/message.c index 4cd0db21e8..cc9ef15f13 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -847,23 +847,22 @@ void wait_return(int redraw) * CTRL-C, but we need to loop then. */ had_got_int = got_int; - /* Don't do mappings here, we put the character back in the - * typeahead buffer. */ - ++no_mapping; - ++allow_keys; - - /* Temporarily disable Recording. If Recording is active, the - * character will be recorded later, since it will be added to the - * typebuf after the loop */ + // Don't do mappings here, we put the character back in the + // typeahead buffer. + no_mapping++; + + // Temporarily disable Recording. If Recording is active, the + // character will be recorded later, since it will be added to the + // typebuf after the loop save_Recording = Recording; save_scriptout = scriptout; Recording = FALSE; scriptout = NULL; c = safe_vgetc(); - if (had_got_int && !global_busy) - got_int = FALSE; - --no_mapping; - --allow_keys; + if (had_got_int && !global_busy) { + got_int = false; + } + no_mapping--; Recording = save_Recording; scriptout = save_scriptout; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 0bb5a8468d..d751f13644 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2225,11 +2225,10 @@ int ask_yesno(const char *str, bool direct) int r = ' '; int save_State = State; - ++no_wait_return; - State = CONFIRM; /* mouse behaves like with :confirm */ - setmouse(); /* disables mouse for xterm */ - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_wait_return++; + State = CONFIRM; // mouse behaves like with :confirm + setmouse(); // disables mouse for xterm + no_mapping++; while (r != 'y' && r != 'n') { /* same highlighting as for wait_return */ @@ -2247,8 +2246,7 @@ int ask_yesno(const char *str, bool direct) --no_wait_return; State = save_State; setmouse(); - --no_mapping; - --allow_keys; + no_mapping--; return r; } @@ -2398,8 +2396,7 @@ get_number ( if (msg_silent != 0) return 0; - ++no_mapping; - ++allow_keys; /* no mapping here, but recognize keys */ + no_mapping++; for (;; ) { ui_cursor_goto(msg_row, msg_col); c = safe_vgetc(); @@ -2427,8 +2424,7 @@ get_number ( } else if (c == CAR || c == NL || c == Ctrl_C || c == ESC) break; } - --no_mapping; - --allow_keys; + no_mapping--; return n; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 85dc509ee6..2ade9cb87d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -642,8 +642,7 @@ static void normal_get_additional_char(NormalState *s) bool langmap_active = false; // using :lmap mappings int lang; // getting a text character - ++no_mapping; - ++allow_keys; // no mapping for nchar, but allow key codes + no_mapping++; // Don't generate a CursorHold event here, most commands can't handle // it, e.g., nv_replace(), nv_csearch(). did_cursorhold = true; @@ -681,8 +680,7 @@ static void normal_get_additional_char(NormalState *s) } if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) { // Allow mappings defined with ":lmap". - --no_mapping; - --allow_keys; + no_mapping--; if (repl) { State = LREPLACE; } else { @@ -695,8 +693,7 @@ static void normal_get_additional_char(NormalState *s) if (langmap_active) { // Undo the decrement done above - ++no_mapping; - ++allow_keys; + no_mapping++; State = NORMAL_BUSY; } State = NORMAL_BUSY; @@ -781,8 +778,7 @@ static void normal_get_additional_char(NormalState *s) } no_mapping++; } - --no_mapping; - --allow_keys; + no_mapping--; } static void normal_invert_horizontal(NormalState *s) @@ -832,8 +828,7 @@ static bool normal_get_command_count(NormalState *s) } if (s->ctrl_w) { - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; } ++no_zero_mapping; // don't map zero here @@ -841,8 +836,7 @@ static bool normal_get_command_count(NormalState *s) LANGMAP_ADJUST(s->c, true); --no_zero_mapping; if (s->ctrl_w) { - --no_mapping; - --allow_keys; + no_mapping--; } s->need_flushbuf |= add_to_showcmd(s->c); } @@ -852,12 +846,10 @@ static bool normal_get_command_count(NormalState *s) s->ctrl_w = true; s->ca.opcount = s->ca.count0; // remember first count s->ca.count0 = 0; - ++no_mapping; - ++allow_keys; // no mapping for nchar, but keys + no_mapping++; s->c = plain_vgetc(); // get next character LANGMAP_ADJUST(s->c, true); - --no_mapping; - --allow_keys; + no_mapping--; s->need_flushbuf |= add_to_showcmd(s->c); return true; } @@ -4043,12 +4035,10 @@ static void nv_zet(cmdarg_T *cap) return; n = nchar - '0'; for (;; ) { - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (nchar == K_DEL || nchar == K_KDEL) n /= 10; @@ -4376,13 +4366,11 @@ dozet: break; - case 'u': /* "zug" and "zuw": undo "zg" and "zw" */ - ++no_mapping; - ++allow_keys; /* no mapping for nchar, but allow key codes */ + case 'u': // "zug" and "zuw": undo "zg" and "zw" + no_mapping++; nchar = plain_vgetc(); LANGMAP_ADJUST(nchar, true); - --no_mapping; - --allow_keys; + no_mapping--; (void)add_to_showcmd(nchar); if (vim_strchr((char_u *)"gGwW", nchar) == NULL) { clearopbeep(cap->oap); diff --git a/src/nvim/window.c b/src/nvim/window.c index dc0ce03794..4fac730f02 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -447,13 +447,12 @@ wingotofile: case 'g': case Ctrl_G: CHECK_CMDWIN - ++ no_mapping; - ++allow_keys; /* no mapping for xchar, but allow key codes */ - if (xchar == NUL) + no_mapping++; + if (xchar == NUL) { xchar = plain_vgetc(); - LANGMAP_ADJUST(xchar, TRUE); - --no_mapping; - --allow_keys; + } + LANGMAP_ADJUST(xchar, true); + no_mapping--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': -- cgit From 43a99f77a82e3de980798383ae8a4134953ece06 Mon Sep 17 00:00:00 2001 From: Yichao Zhou Date: Sun, 26 Mar 2017 04:04:20 -0700 Subject: highlight: :match should override 'list' (#6343) Closes #4946 --- src/nvim/screen.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index baec18dd6f..505d04ed56 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2193,7 +2193,8 @@ win_line ( int prev_c1 = 0; /* first composing char for prev_c */ int did_line_attr = 0; - bool has_bufhl = false; // this buffer has highlight matches + bool search_attr_from_match = false; // if search_attr is from :match + bool has_bufhl = false; // this buffer has highlight matches int bufhl_attr = 0; // attributes desired by bufhl bufhl_lineinfo_T bufhl_info; // bufhl data for this line @@ -2625,6 +2626,7 @@ win_line ( if ((long)shl->startcol < v) { // match at leftcol shl->attr_cur = shl->attr; search_attr = shl->attr; + search_attr_from_match = shl != &search_hl; } area_highlighting = true; } @@ -2962,6 +2964,7 @@ win_line ( /* Use attributes from match with highest priority among * 'search_hl' and the match list. */ + search_attr_from_match = false; search_attr = search_hl.attr_cur; cur = wp->w_match_head; shl_flag = FALSE; @@ -2974,8 +2977,10 @@ win_line ( shl_flag = TRUE; } else shl = &cur->hl; - if (shl->attr_cur != 0) + if (shl->attr_cur != 0) { search_attr = shl->attr_cur; + search_attr_from_match = shl != &search_hl; + } if (shl != &search_hl && cur != NULL) cur = cur->next; } @@ -3711,7 +3716,7 @@ win_line ( } // Don't override visual selection highlighting. - if (n_attr > 0 && draw_state == WL_LINE) { + if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) { char_attr = hl_combine_attr(char_attr, extra_attr); } -- cgit From f9a31e98505c0f5e83dd3ba957fbd0f4b150d30c Mon Sep 17 00:00:00 2001 From: lonerover Date: Mon, 27 Mar 2017 01:04:57 +0800 Subject: vim-patch:7.4.2349 (#6368) Problem: Valgrind reports using uninitialzed memory. (Dominique Pelle) Solution: Check the length before checking for a NUL. https://github.com/vim/vim/commit/2321ca2a78286bc026fa7f407281ddbeb04114bb --- src/nvim/message.c | 2 +- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index cc9ef15f13..bf54284881 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1986,7 +1986,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) char buf[4]; char *p; - while (*s != NUL && (maxlen < 0 || s - str < maxlen)) { + while ((maxlen < 0 || s - str < maxlen) && *s != NUL) { if (!(silent_mode && p_verbose == 0)) { // NL --> CR NL translation (for Unix, not for "--version") p = &buf[0]; diff --git a/src/nvim/version.c b/src/nvim/version.c index db3b9b51b2..a354634218 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -92,7 +92,7 @@ static int included_patches[] = { // 2352 NA // 2351 NA // 2350, - // 2349, + 2349, 2348, 2347, 2346, -- cgit From edc80f6b46f51ea1137289301914c0f90db19295 Mon Sep 17 00:00:00 2001 From: raichoo Date: Sun, 26 Mar 2017 23:15:53 +0200 Subject: vim-patch:7.4.2357 (#6354) Problem: Attempt to read history entry while not initialized. Solution: Skip when the index is negative. https://github.com/vim/vim/commit/46643713dc6bb04b4e84986b1763ef309e960161 --- src/nvim/ex_getln.c | 4 ++-- src/nvim/version.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 58979c0e43..8758a63bce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4677,8 +4677,8 @@ add_to_history ( * down, only lines that were added. */ if (histype == HIST_SEARCH && in_map) { - if (maptick == last_maptick) { - /* Current line is from the same mapping, remove it */ + 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]; diff --git a/src/nvim/version.c b/src/nvim/version.c index a354634218..a911e8ebc3 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -84,7 +84,7 @@ static int included_patches[] = { // 2360, // 2359 NA // 2358 NA - // 2357, + 2357, // 2356, 2355, // 2354, -- cgit From 20e7652b6980e0efed5db65e0f80c0d72ef8c68d Mon Sep 17 00:00:00 2001 From: lonerover Date: Thu, 23 Mar 2017 22:42:26 +0800 Subject: vim-patch:7.4.2307 Problem: Several tests are old style. Solution: Turn them into new style tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/cd055da370114f66c960be9c8b1eb0f33a9e0a85 --- src/nvim/testdir/Makefile | 3 ++ src/nvim/testdir/test_charsearch.vim | 62 +++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_fnameescape.vim | 21 ++++++++++++ src/nvim/testdir/test_substitute.vim | 41 +++++++++++++++++++++++ src/nvim/version.c | 2 +- 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/nvim/testdir/test_charsearch.vim create mode 100644 src/nvim/testdir/test_fnameescape.vim create mode 100644 src/nvim/testdir/test_substitute.vim (limited to 'src') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 531a07912f..fd557cca51 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -30,6 +30,7 @@ SCRIPTS ?= \ NEW_TESTS ?= \ test_autocmd.res \ test_bufwintabinfo.res \ + test_charsearch.res \ test_cmdline.res \ test_command_count.res \ test_cscope.res \ @@ -37,6 +38,7 @@ NEW_TESTS ?= \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_fnameescape.res \ test_fold.res \ test_glob2regpat.res \ test_gn.res \ @@ -55,6 +57,7 @@ NEW_TESTS ?= \ test_normal.res \ test_quickfix.res \ test_signs.res \ + test_substitute.res \ test_syntax.res \ test_tabpage.res \ test_textobjects.res \ diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim new file mode 100644 index 0000000000..115659a013 --- /dev/null +++ b/src/nvim/testdir/test_charsearch.vim @@ -0,0 +1,62 @@ + +function! Test_charsearch() + enew! + call append(0, ['Xabcdefghijkemnopqretuvwxyz', + \ 'Yabcdefghijkemnopqretuvwxyz', + \ 'Zabcdefghijkemnokqretkvwxyz']) + " check that "fe" and ";" work + 1 + normal! ylfep;;p,,p + call assert_equal('XabcdeXfghijkeXmnopqreXtuvwxyz', getline(1)) + " check that save/restore works + 2 + normal! ylfep + let csave = getcharsearch() + normal! fip + call setcharsearch(csave) + normal! ;p;p + call assert_equal('YabcdeYfghiYjkeYmnopqreYtuvwxyz', getline(2)) + + " check that setcharsearch() changes the settings. + 3 + normal! ylfep + call setcharsearch({'char': 'k'}) + normal! ;p + call setcharsearch({'forward': 0}) + normal! $;p + call setcharsearch({'until': 1}) + set cpo-=; + normal! ;;p + call assert_equal('ZabcdeZfghijkZZemnokqretkZvwxyz', getline(3)) + enew! +endfunction + +" Test for t,f,F,T movement commands and 'cpo-;' setting +function! Test_search_cmds() + enew! + call append(0, ["aaa two three four", " zzz", "yyy ", + \ "bbb yee yoo four", "ccc two three four", + \ "ddd yee yoo four"]) + set cpo-=; + 1 + normal! 0tt;D + 2 + normal! 0fz;D + 3 + normal! $Fy;D + 4 + normal! $Ty;D + set cpo+=; + 5 + normal! 0tt;;D + 6 + normal! $Ty;;D + + call assert_equal('aaa two', getline(1)) + call assert_equal(' z', getline(2)) + call assert_equal('y', getline(3)) + call assert_equal('bbb y', getline(4)) + call assert_equal('ccc', getline(5)) + call assert_equal('ddd yee y', getline(6)) + enew! +endfunction diff --git a/src/nvim/testdir/test_fnameescape.vim b/src/nvim/testdir/test_fnameescape.vim new file mode 100644 index 0000000000..ce2cd3f7a6 --- /dev/null +++ b/src/nvim/testdir/test_fnameescape.vim @@ -0,0 +1,21 @@ + +" Test if fnameescape is correct for special chars like ! +function! Test_fnameescape() + let fname = 'Xspa ce' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "Space") + call delete(fname) + + let fname = 'Xemark!' + let status = v:false + try + exe "w! " . fnameescape(fname) + let status = v:true + endtry + call assert_true(status, "ExclamationMark") + call delete(fname) +endfunction diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim new file mode 100644 index 0000000000..e2b6de03c3 --- /dev/null +++ b/src/nvim/testdir/test_substitute.vim @@ -0,0 +1,41 @@ +" Tests for multi-line regexps with ":s". + +function! Test_multiline_subst() + enew! + call append(0, ["1 aa", + \ "bb", + \ "cc", + \ "2 dd", + \ "ee", + \ "3 ef", + \ "gh", + \ "4 ij", + \ "5 a8", + \ "8b c9", + \ "9d", + \ "6 e7", + \ "77f", + \ "xxxxx"]) + + 1 + " test if replacing a line break works with a back reference + /^1/,/^2/s/\n\(.\)/ \1/ + " test if inserting a line break works with a back reference + /^3/,/^4/s/\(.\)$/\r\1/ + " test if replacing a line break with another line break works + /^5/,/^6/s/\(\_d\{3}\)/x\1x/ + call assert_equal('1 aa bb cc 2 dd ee', getline(1)) + call assert_equal('3 e', getline(2)) + call assert_equal('f', getline(3)) + call assert_equal('g', getline(4)) + call assert_equal('h', getline(5)) + call assert_equal('4 i', getline(6)) + call assert_equal('j', getline(7)) + call assert_equal('5 ax8', getline(8)) + call assert_equal('8xb cx9', getline(9)) + call assert_equal('9xd', getline(10)) + call assert_equal('6 ex7', getline(11)) + call assert_equal('7x7f', getline(12)) + call assert_equal('xxxxx', getline(13)) + enew! +endfunction diff --git a/src/nvim/version.c b/src/nvim/version.c index a911e8ebc3..ee66b00b4e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -134,7 +134,7 @@ static int included_patches[] = { // 2310 NA 2309, // 2308 NA - // 2307, + 2307, // 2306, 2305, // 2304 NA -- cgit From 85ba14af6ad19fa3c4ba1ade3819014254635b2f Mon Sep 17 00:00:00 2001 From: lonerover Date: Thu, 23 Mar 2017 22:54:30 +0800 Subject: vim-patch:7.4.2330 Problem: Coverity complains about not checking curwin to be NULL. Solution: Use firstwin to avoid the warning. https://github.com/vim/vim/commit/030cddc7ec0c3d2fe3969140cd1b92b2f18633c0 --- src/nvim/buffer.c | 7 ++++--- src/nvim/version.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3c416c157f..4a07884f98 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -627,10 +627,11 @@ void buf_freeall(buf_T *buf, int flags) */ if (buf == curbuf && !is_curbuf) return; - diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ - /* Remove any ownsyntax, unless exiting. */ - if (firstwin != NULL && curwin->w_buffer == buf) + diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer. + // Remove any ownsyntax, unless exiting. + if (curwin != NULL && curwin->w_buffer == buf) { reset_synblock(curwin); + } /* No folds in an empty buffer. */ FOR_ALL_TAB_WINDOWS(tp, win) { diff --git a/src/nvim/version.c b/src/nvim/version.c index ee66b00b4e..c18439c279 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -111,7 +111,7 @@ static int included_patches[] = { 2333, // 2332 NA 2331, - // 2330, + 2330, 2329, 2328, // 2327 NA -- cgit From 9cd7e199048161c5224aca885af8b7945236392d Mon Sep 17 00:00:00 2001 From: lonerover Date: Fri, 24 Mar 2017 12:24:25 +0800 Subject: vim-patch:7.4.2334 Problem: On MS-Windows test_getcwd leaves Xtopdir behind. Solution: Set 'noswapfile'. (Michael Soyka) https://github.com/vim/vim/commit/1b0c2fcf6e85c9b85c24757ba970061e1f3e4e80 --- src/nvim/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/version.c b/src/nvim/version.c index c18439c279..500e8984c0 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -107,7 +107,7 @@ static int included_patches[] = { 2337, 2336, 2335, - // 2334, + 2334, 2333, // 2332 NA 2331, -- cgit From 7bc37ffb22a84668bba5b2e3589c4c05ad43f7d0 Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Fri, 24 Mar 2017 20:21:05 +0100 Subject: terminal: global 'scrollback' #6352 Make the 'scrollback' option work like most other buffer-local options: - `:set scrollback=x` sets the global and local value - `:setglobal scrollback=x` sets only the global default - new terminal buffers inherit the global Normal buffers are still always -1, and :setlocal there is an error. Closes #6337 --- src/nvim/globals.h | 2 +- src/nvim/option.c | 21 +++++++++++---------- src/nvim/option_defs.h | 3 +++ src/nvim/options.lua | 2 +- src/nvim/terminal.c | 13 ++++++------- 5 files changed, 22 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ad14ec4f8c..de79ee2469 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1255,7 +1255,7 @@ typedef enum { kCdScopeInvalid = -1, kCdScopeWindow, ///< Affects one window. kCdScopeTab, ///< Affects one tab page. - kCdScopeGlobal, ///< Affects the entire instance of Neovim. + kCdScopeGlobal, ///< Affects the entire Nvim instance. } CdScope; #define MIN_CD_SCOPE kCdScopeWindow diff --git a/src/nvim/option.c b/src/nvim/option.c index 2a17ca69d1..b037b0ae35 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -156,7 +156,6 @@ static long p_ts; static long p_tw; static int p_udf; static long p_wm; -static long p_scbk; static char_u *p_keymap; /* Saved values for when 'bin' is set. */ @@ -4199,16 +4198,13 @@ set_num_option ( FOR_ALL_TAB_WINDOWS(tp, wp) { check_colorcolumn(wp); } - } else if (pp == &curbuf->b_p_scbk) { + } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { // 'scrollback' - if (!curbuf->terminal) { + if (*pp < -1 || *pp > SB_MAX + || (opt_flags == OPT_LOCAL && !curbuf->terminal)) { errmsg = e_invarg; - curbuf->b_p_scbk = -1; - } else { - if (curbuf->b_p_scbk < -1 || curbuf->b_p_scbk > 100000) { - errmsg = e_invarg; - curbuf->b_p_scbk = 1000; - } + *pp = old_value; + } else if (curbuf->terminal) { // Force the scrollback to take effect. terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX); } @@ -4331,6 +4327,11 @@ set_num_option ( if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp; + if (pp == &curbuf->b_p_scbk && !curbuf->terminal) { + // Normal buffer: reset local 'scrollback' after updating the global value. + curbuf->b_p_scbk = -1; + } + options[opt_idx].flags |= P_WAS_SET; if (!starting && errmsg == NULL) { @@ -4586,7 +4587,7 @@ get_option_value ( // // Pretends that option is absent if it is not present in the requested scope // (i.e. has no global, window-local or buffer-local value depending on -// opt_type). Uses +// opt_type). // // Returned flags: // 0 hidden or unknown option, also option that does not have requested diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index b171b23edb..94c6361236 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -524,6 +524,7 @@ EXTERN int p_ru; // 'ruler' EXTERN char_u *p_ruf; // 'rulerformat' EXTERN char_u *p_pp; // 'packpath' EXTERN char_u *p_rtp; // 'runtimepath' +EXTERN long p_scbk; // 'scrollback' EXTERN long p_sj; // 'scrolljump' EXTERN long p_so; // 'scrolloff' EXTERN char_u *p_sbo; // 'scrollopt' @@ -811,4 +812,6 @@ enum { /* Value for b_p_ul indicating the global value must be used. */ #define NO_LOCAL_UNDOLEVEL -123456 +#define SB_MAX 100000 // Maximum 'scrollback' value. + #endif // NVIM_OPTION_DEFS_H diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 853c2b52d7..ee2b8a563d 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1918,7 +1918,7 @@ return { vi_def=true, varname='p_scbk', redraw={'current_buffer'}, - defaults={if_true={vi=-1}} + defaults={if_true={vi=1000}} }, { full_name='scrollbind', abbreviation='scb', diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 87ee8f410f..dc418e25fa 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -85,8 +85,6 @@ typedef struct terminal_state { # include "terminal.c.generated.h" #endif -#define SB_MAX 100000 // Maximum 'scrollback' value. - // Delay for refreshing the terminal buffer after receiving updates from // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 @@ -231,10 +229,10 @@ Terminal *terminal_open(TerminalOptions opts) set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); // Default settings for terminal buffers - curbuf->b_p_ma = false; // 'nomodifiable' - curbuf->b_p_ul = -1; // 'undolevels' - curbuf->b_p_scbk = 1000; // 'scrollback' - curbuf->b_p_tw = 0; // 'textwidth' + curbuf->b_p_ma = false; // 'nomodifiable' + curbuf->b_p_ul = -1; // 'undolevels' + curbuf->b_p_scbk = p_scbk; // 'scrollback' + curbuf->b_p_tw = 0; // 'textwidth' set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); @@ -248,7 +246,8 @@ Terminal *terminal_open(TerminalOptions opts) apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); // Configure the scrollback buffer. - rv->sb_size = curbuf->b_p_scbk < 0 ? SB_MAX : (size_t)curbuf->b_p_scbk;; + rv->sb_size = curbuf->b_p_scbk < 0 + ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk); rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); if (!true_color) { -- cgit From 62774e43564b166d6907c7abc2e3431a65bd5596 Mon Sep 17 00:00:00 2001 From: Eiichi NISHINA Date: Sun, 12 Feb 2017 02:10:53 +0900 Subject: ci: Check that `#include "*.h"` works as a single include Lesser form of include-what-you-use: at least guarantees that header file did not forget to include something through some other included file. Activate run_single_includes_tests on CI. Fix some IWYU violations. References #5321 --- src/nvim/CMakeLists.txt | 156 ++++++++++++++++++++++++++++++++++++++++-------- src/nvim/eval_defs.h | 1 + src/nvim/ex_docmd.c | 1 + src/nvim/ex_docmd.h | 1 + src/nvim/mark.h | 2 +- src/nvim/os/fs_defs.h | 2 +- src/nvim/os/os_defs.h | 4 +- src/nvim/os/unix_defs.h | 2 +- src/nvim/os/win_defs.h | 6 +- src/nvim/strings.h | 3 +- 10 files changed, 145 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 22cf1f3a3d..8af2dcf5f6 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -48,6 +48,7 @@ file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(GLOB NEOVIM_SOURCES *.c) +file(GLOB NEOVIM_HEADERS *.h) foreach(subdir os @@ -65,10 +66,11 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) file(GLOB sources ${subdir}/*.c) + file(GLOB headers ${subdir}/*.h) list(APPEND NEOVIM_SOURCES ${sources}) + list(APPEND NEOVIM_HEADERS ${headers}) endforeach() -file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from @@ -152,6 +154,19 @@ separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS}) separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}}) set(gen_cflags ${gen_cflags} ${C_FLAGS_${build_type}_ARRAY} ${C_FLAGS_ARRAY}) +function(get_preproc_output varname iname) + if(MSVC) + set(${varname} /P /Fi${iname} PARENT_SCOPE) + else() + set(${varname} -E -o ${iname} PARENT_SCOPE) + endif() +endfunction() + +# NEOVIM_GENERATED_FOR_HEADERS: header files generated to be included in headers +# NEOVIM_GENERATED_FOR_SOURCES: header files generated to be included in sources +# NEOVIM_GENERATED_SOURCES: generated source files +# These lists should be mutually exclusive + foreach(sfile ${NEOVIM_SOURCES} "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" ${GENERATED_API_DISPATCH}) @@ -166,26 +181,22 @@ foreach(sfile ${NEOVIM_SOURCES} set(f "${d}/${f}") set(r "${d}/${r}") endif() - set(gf1 "${GENERATED_DIR}/${r}.c.generated.h") - set(gf2 "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") - set(gf3 "${GENERATED_DIR}/${r}.i") + set(gf_c_h "${GENERATED_DIR}/${r}.c.generated.h") + set(gf_h_h "${GENERATED_INCLUDES_DIR}/${r}.h.generated.h") + set(gf_i "${GENERATED_DIR}/${r}.i") - if(MSVC) - set(PREPROC_OUTPUT /P /Fi${gf3}) - else() - set(PREPROC_OUTPUT -E -o ${gf3}) - endif() + get_preproc_output(PREPROC_OUTPUT ${gf_i}) add_custom_command( - OUTPUT "${gf1}" "${gf2}" + OUTPUT "${gf_c_h}" "${gf_h_h}" COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} ${C_FLAGS_ARRAY} - COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf1}" "${gf2}" "${gf3}" + COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS "${HEADER_GENERATOR}" "${sfile}" ) - list(APPEND NEOVIM_GENERATED_SOURCES "${gf1}") - list(APPEND NEOVIM_GENERATED_SOURCES "${gf2}") + list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${gf_c_h}") + list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${gf_h_h}") if(${d} MATCHES "^api$" AND NOT ${f} MATCHES "^api/helpers.c$") - list(APPEND API_HEADERS ${gf2}) + list(APPEND API_HEADERS ${gf_h_h}) endif() endforeach() @@ -210,17 +221,23 @@ add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) -list(APPEND NEOVIM_GENERATED_SOURCES - "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" - "${GENERATED_API_DISPATCH}" +list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" - "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_ENUM}" +) + +list(APPEND NEOVIM_GENERATED_FOR_SOURCES + "${GENERATED_API_DISPATCH}" + "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" "${GENERATED_OPTIONS}" "${GENERATED_UNICODE_TABLES}" ) +list(APPEND NEOVIM_GENERATED_SOURCES + "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" +) + add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} @@ -237,7 +254,7 @@ add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} ) -list(APPEND NEOVIM_GENERATED_SOURCES +list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} @@ -252,6 +269,15 @@ add_custom_command(OUTPUT ${GENERATED_OPTIONS} DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} ) +# Check that NEOVIM_GENERATED_FOR_SOURCES and NEOVIM_GENERATED_FOR_HEADERS are mutually exclusive + +foreach(hfile ${NEOVIM_GENERATED_FOR_HEADERS}) + list(FIND NEOVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) + if(NOT ${hfile_idx} EQUAL -1) + message(FATAL_ERROR "File included in both NEOVIM_GENERATED_FOR_HEADERS and NEOVIM_GENERATED_FOR_SOURCES") + endif() +endforeach() + # Our dependencies come first. if (LibIntl_FOUND) @@ -286,8 +312,8 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() -add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} - ${NEOVIM_HEADERS}) +add_executable(nvim ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -361,17 +387,97 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} + ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) +set(NO_SINGLE_CHECK_HEADERS + buffer + charset + cursor_shape + diff + digraph + ex_cmds + ex_getln + file_search + fold + getchar + hardcopy + if_cscope + if_cscope_defs + mark + mbyte + memfile_defs + memline + memline_defs + menu + misc2 + move + msgpack_rpc/server + ops + option + os/shell + os_unix + os/win_defs + popupmnu + quickfix + regexp + regexp_defs + screen + search + sha256 + sign_defs + spell + syntax + syntax_defs + tag + terminal + tui/tui + ugrid + ui + ui_bridge + undo + undo_defs + version + window +) +foreach(hfile ${NEOVIM_HEADERS}) + get_filename_component(full_d ${hfile} PATH) + file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") + if(${d} MATCHES "^[.][.]") + file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") + endif() + get_filename_component(r ${hfile} NAME_WE) + if(NOT ${d} EQUAL ".") + set(r "${d}/${r}") + endif() + + if(NOT ${hfile} MATCHES "[.]c[.]h$") + set(tsource "${GENERATED_DIR}/${r}.test-include.c") + set(tresult "${GENERATED_DIR}/${r}.test-include.i") + string(REPLACE "/" "-" texe "${r}-test") + write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") + get_preproc_output(PREPROC_OUTPUT ${tresult}) + add_executable( + ${texe} + EXCLUDE_FROM_ALL + ${tsource} ${NEOVIM_HEADERS} ${NEOVIM_GENERATED_FOR_HEADERS}) + + list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) + if(${hfile_exclude_idx} EQUAL -1) + list(APPEND HEADER_CHECK_TARGETS ${texe}) + endif() + endif() +endforeach() +add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) + add_subdirectory(po) diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 39028fdb11..8f5e1a897d 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -3,6 +3,7 @@ #include #include +#include #include "nvim/hashtab.h" #include "nvim/lib/queue.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 486baaad47..d1557f9c82 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -67,6 +67,7 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/shada.h" +#include "nvim/globals.h" static int quitmore = 0; static int ex_pressedreturn = FALSE; diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 4def4cbbae..cff350de08 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -2,6 +2,7 @@ #define NVIM_EX_DOCMD_H #include "nvim/ex_cmds_defs.h" +#include "nvim/globals.h" // flags for do_cmdline() #define DOCMD_VERBOSE 0x01 // included command in error message diff --git a/src/nvim/mark.h b/src/nvim/mark.h index aff6e7273a..efba9708db 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -29,7 +29,7 @@ /// Clear given fmark #define CLEAR_FMARK(fmarkp_) \ - RESET_FMARK(fmarkp_, ((pos_T) {0, 0, 0}), 0) + RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0) /// Set given extended mark (regular mark + file name) #define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \ diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index 0bd9c37750..2277d926b3 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -14,7 +14,7 @@ typedef struct { uint64_t device_id; ///< @private The id of the device containing the file } FileID; -#define FILE_ID_EMPTY (FileID) {.inode = 0, .device_id = 0} +#define FILE_ID_EMPTY (FileID) { .inode = 0, .device_id = 0 } typedef struct { uv_fs_t request; ///< @private The request to uv for the directory. diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 14c210c69c..f81785675e 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -27,11 +27,11 @@ // Use up to 5 Mbyte for a buffer. #ifndef DFLT_MAXMEM -# define DFLT_MAXMEM (5*1024) +# define DFLT_MAXMEM (5 * 1024) #endif // use up to 10 Mbyte for Vim. #ifndef DFLT_MAXMEMTOT -# define DFLT_MAXMEMTOT (10*1024) +# define DFLT_MAXMEMTOT (10 * 1024) #endif // Note: Some systems need both string.h and strings.h (Savage). However, diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index c98aa88bfa..5c9daca476 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -8,7 +8,7 @@ // POSIX.1-2008 says that NAME_MAX should be in here #include -#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"} +#define TEMP_DIR_NAMES { "$TMPDIR", "/tmp", ".", "~" } #define TEMP_FILE_PATH_MAXLEN 256 #define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL) diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 8de896c490..827fb2f247 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -1,6 +1,10 @@ #ifndef NVIM_OS_WIN_DEFS_H #define NVIM_OS_WIN_DEFS_H +#ifndef WIN32 +# error Header must be included only when compiling for Windows. +#endif + // winsock2.h must be first to avoid incompatibilities // with winsock.h (included by windows.h) #include @@ -15,7 +19,7 @@ #define NAME_MAX _MAX_PATH -#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} +#define TEMP_DIR_NAMES { "$TMP", "$TEMP", "$USERPROFILE", "" } #define TEMP_FILE_PATH_MAXLEN _MAX_PATH #define FNAME_ILLEGAL "\"*?><|" diff --git a/src/nvim/strings.h b/src/nvim/strings.h index eb8b83c7d0..8aea374b96 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -1,9 +1,8 @@ #ifndef NVIM_STRINGS_H #define NVIM_STRINGS_H -#include #include -#include +#include #include "nvim/types.h" #include "nvim/eval_defs.h" -- cgit From e20e9645b2c74b8cf6e1ba90961c08b079bbee3c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 14:12:23 +0200 Subject: build: Rename NEOVIM_* to NVIM_* --- src/nvim/CMakeLists.txt | 64 ++++++++++++++++++++++------------------------ src/nvim/po/CMakeLists.txt | 10 ++++---- 2 files changed, 36 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8af2dcf5f6..877b403463 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -47,8 +47,8 @@ include_directories(${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) -file(GLOB NEOVIM_SOURCES *.c) -file(GLOB NEOVIM_HEADERS *.h) +file(GLOB NVIM_SOURCES *.c) +file(GLOB NVIM_HEADERS *.h) foreach(subdir os @@ -67,18 +67,18 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) file(GLOB sources ${subdir}/*.c) file(GLOB headers ${subdir}/*.h) - list(APPEND NEOVIM_SOURCES ${sources}) - list(APPEND NEOVIM_HEADERS ${headers}) + list(APPEND NVIM_SOURCES ${sources}) + list(APPEND NVIM_HEADERS ${headers}) endforeach() file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from # build to build. -list(SORT NEOVIM_SOURCES) -list(SORT NEOVIM_HEADERS) +list(SORT NVIM_SOURCES) +list(SORT NVIM_HEADERS) -foreach(sfile ${NEOVIM_SOURCES}) +foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) @@ -88,7 +88,7 @@ foreach(sfile ${NEOVIM_SOURCES}) endif() endforeach() -list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) +list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) # Legacy files that do not yet pass -Wconversion. set(CONV_SOURCES @@ -162,12 +162,11 @@ function(get_preproc_output varname iname) endif() endfunction() -# NEOVIM_GENERATED_FOR_HEADERS: header files generated to be included in headers -# NEOVIM_GENERATED_FOR_SOURCES: header files generated to be included in sources -# NEOVIM_GENERATED_SOURCES: generated source files -# These lists should be mutually exclusive - -foreach(sfile ${NEOVIM_SOURCES} +# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers +# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources +# NVIM_GENERATED_SOURCES: generated source files +# These lists must be mutually exclusive. +foreach(sfile ${NVIM_SOURCES} "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) @@ -193,8 +192,8 @@ foreach(sfile ${NEOVIM_SOURCES} COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS "${HEADER_GENERATOR}" "${sfile}" ) - list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${gf_c_h}") - list(APPEND NEOVIM_GENERATED_FOR_HEADERS "${gf_h_h}") + list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}") + list(APPEND NVIM_GENERATED_FOR_HEADERS "${gf_h_h}") if(${d} MATCHES "^api$" AND NOT ${f} MATCHES "^api/helpers.c$") list(APPEND API_HEADERS ${gf_h_h}) endif() @@ -221,12 +220,12 @@ add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) -list(APPEND NEOVIM_GENERATED_FOR_HEADERS +list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EVENTS_ENUM}" ) -list(APPEND NEOVIM_GENERATED_FOR_SOURCES +list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_API_DISPATCH}" "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" @@ -234,7 +233,7 @@ list(APPEND NEOVIM_GENERATED_FOR_SOURCES "${GENERATED_UNICODE_TABLES}" ) -list(APPEND NEOVIM_GENERATED_SOURCES +list(APPEND NVIM_GENERATED_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c" ) @@ -254,7 +253,7 @@ add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} ) -list(APPEND NEOVIM_GENERATED_FOR_SOURCES +list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} @@ -269,12 +268,11 @@ add_custom_command(OUTPUT ${GENERATED_OPTIONS} DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} ) -# Check that NEOVIM_GENERATED_FOR_SOURCES and NEOVIM_GENERATED_FOR_HEADERS are mutually exclusive - -foreach(hfile ${NEOVIM_GENERATED_FOR_HEADERS}) - list(FIND NEOVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) +# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. +foreach(hfile ${NVIM_GENERATED_FOR_HEADERS}) + list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx) if(NOT ${hfile_idx} EQUAL -1) - message(FATAL_ERROR "File included in both NEOVIM_GENERATED_FOR_HEADERS and NEOVIM_GENERATED_FOR_SOURCES") + message(FATAL_ERROR "File included in both NVIM_GENERATED_FOR_HEADERS and NVIM_GENERATED_FOR_SOURCES") endif() endforeach() @@ -312,8 +310,8 @@ if(JEMALLOC_FOUND) list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES}) endif() -add_executable(nvim ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -387,16 +385,16 @@ elseif(CLANG_TSAN) set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() -add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) +add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) target_link_libraries(libnvim ${NVIM_LINK_LIBRARIES}) set_target_properties(libnvim PROPERTIES POSITION_INDEPENDENT_CODE ON OUTPUT_NAME nvim) set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") -add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_FOR_SOURCES} ${NEOVIM_GENERATED_FOR_HEADERS} - ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) +add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) @@ -450,7 +448,7 @@ set(NO_SINGLE_CHECK_HEADERS version window ) -foreach(hfile ${NEOVIM_HEADERS}) +foreach(hfile ${NVIM_HEADERS}) get_filename_component(full_d ${hfile} PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") if(${d} MATCHES "^[.][.]") @@ -470,7 +468,7 @@ foreach(hfile ${NEOVIM_HEADERS}) add_executable( ${texe} EXCLUDE_FROM_ALL - ${tsource} ${NEOVIM_HEADERS} ${NEOVIM_GENERATED_FOR_HEADERS}) + ${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS}) list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) if(${hfile_exclude_idx} EQUAL -1) diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index d2b62f89a0..121f22129a 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -32,10 +32,10 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) zh_CN.UTF-8 zh_TW.UTF-8) - set(NEOVIM_RELATIVE_SOURCES) - foreach(SRC ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) + set(NVIM_RELATIVE_SOURCES) + foreach(SRC ${NVIM_SOURCES} ${NVIM_HEADERS}) file(RELATIVE_PATH RELATIVE_SRC ${CMAKE_CURRENT_SOURCE_DIR} ${SRC}) - list(APPEND NEOVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) + list(APPEND NVIM_RELATIVE_SOURCES ${RELATIVE_SRC}) endforeach() set(NVIM_POT ${CMAKE_CURRENT_BINARY_DIR}/nvim.pot) @@ -46,9 +46,9 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG) -DXGETTEXT_PRG=${XGETTEXT_PRG} -DPOT_FILE=${NVIM_POT} -DSEARCH_DIR=${CMAKE_CURRENT_SOURCE_DIR} - "\"-DSOURCES=${NEOVIM_RELATIVE_SOURCES}\"" + "\"-DSOURCES=${NVIM_RELATIVE_SOURCES}\"" -P ${PROJECT_SOURCE_DIR}/cmake/RunXgettext.cmake - DEPENDS ${NEOVIM_SOURCES}) + DEPENDS ${NVIM_SOURCES}) add_custom_target(potfile DEPENDS ${NVIM_POT}) -- cgit From 88124dfebc1a9c7eeb149beedd52a818058f9c16 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 14:44:14 +0200 Subject: build: Prefix check-single-includes artifacts. The previous naming scheme could conflict with the test fixture artifacts from `test/functional/fixtures/`, which also produce `foo-test` artifacts. --- src/nvim/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 877b403463..3ee4380538 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -435,6 +435,7 @@ set(NO_SINGLE_CHECK_HEADERS sha256 sign_defs spell + spellfile syntax syntax_defs tag @@ -462,7 +463,7 @@ foreach(hfile ${NVIM_HEADERS}) if(NOT ${hfile} MATCHES "[.]c[.]h$") set(tsource "${GENERATED_DIR}/${r}.test-include.c") set(tresult "${GENERATED_DIR}/${r}.test-include.i") - string(REPLACE "/" "-" texe "${r}-test") + string(REPLACE "/" "-" texe "test-incl-${r}") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") get_preproc_output(PREPROC_OUTPUT ${tresult}) add_executable( -- cgit From 9d200cd0a3ef749509cee3331ba1d0b5fb62e7f2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 27 Mar 2017 21:04:52 +0200 Subject: getcompletion("cmdline") (#6376) Closes #5823 --- src/nvim/eval.c | 18 +++++++++++++----- src/nvim/testdir/test_cmdline.vim | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d49fcbf17e..927a1dcb0e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10765,16 +10765,23 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) options |= WILD_KEEP_ALL; } + if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + + if (STRCMP(get_tv_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, get_tv_string(&argvars[0])); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); + goto theend; + } + ExpandInit(&xpc); xpc.xp_pattern = get_tv_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { - if (argvars[1].v_type == VAR_STRING) { - EMSG2(_(e_invarg2), argvars[1].vval.v_string); - } else { - EMSG(_(e_invarg)); - } + EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; } @@ -10793,6 +10800,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } +theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); rettv_list_alloc(rettv); if (pat != NULL) { diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 40db227d97..f56227250c 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -156,6 +156,20 @@ func Test_getcompletion() call assert_equal(['Testing'], l) endif + " Command line completion tests + let l = getcompletion('cd ', 'cmdline') + call assert_true(index(l, 'sautest/') >= 0) + let l = getcompletion('cd NoMatch', 'cmdline') + call assert_equal([], l) + let l = getcompletion('let v:n', 'cmdline') + call assert_true(index(l, 'v:null') >= 0) + let l = getcompletion('let v:notexists', 'cmdline') + call assert_equal([], l) + let l = getcompletion('call tag', 'cmdline') + call assert_true(index(l, 'taglist(') >= 0) + let l = getcompletion('call paint', 'cmdline') + call assert_equal([], l) + " For others test if the name is recognized. let names = ['buffer', 'environment', 'file_in_path', \ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user'] -- cgit From b9e7ab1484784903b3233309a402138dc42e7e59 Mon Sep 17 00:00:00 2001 From: Samuel Catherasoo Date: Tue, 28 Mar 2017 19:21:30 -0400 Subject: refactor/single-include: charset.h (#6385) --- src/nvim/CMakeLists.txt | 1 - src/nvim/charset.h | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3ee4380538..e752f5d4df 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -400,7 +400,6 @@ set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTIN set(NO_SINGLE_CHECK_HEADERS buffer - charset cursor_shape diff digraph diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 78d6f2a76c..8d25b828e5 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -1,6 +1,10 @@ #ifndef NVIM_CHARSET_H #define NVIM_CHARSET_H +#include "nvim/types.h" +#include "nvim/pos.h" +#include "nvim/buffer_defs.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif -- cgit From e86042ae178736d657b5da12adce4479633aa8d5 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 29 Mar 2017 07:30:54 +0800 Subject: vim-patch:7.4.2343 and mark NA patches (#6384) vim-patch:7.4.2343 Problem: Too many old file tests. Solution: Turn several into new style tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/53f1673cd909eb1c809c6a9086e3d104a0df9bed --- src/nvim/testdir/Makefile | 3 +++ src/nvim/testdir/test_charsearch.vim | 4 ++-- src/nvim/testdir/test_fnameescape.vim | 2 +- src/nvim/testdir/test_gf.vim | 33 +++++++++++++++++++++++++++++++++ src/nvim/testdir/test_hlsearch.vim | 34 ++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_smartindent.vim | 14 ++++++++++++++ src/nvim/testdir/test_tagjump.vim | 30 ++++++++++++++++++++++++++++++ src/nvim/version.c | 6 +++--- 8 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/nvim/testdir/test_gf.vim create mode 100644 src/nvim/testdir/test_hlsearch.vim create mode 100644 src/nvim/testdir/test_smartindent.vim (limited to 'src') diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index fd557cca51..70a9f2b8c4 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -41,10 +41,12 @@ NEW_TESTS ?= \ test_fnameescape.res \ test_fold.res \ test_glob2regpat.res \ + test_gf.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ + test_hlsearch.res \ test_increment.res \ test_increment_dbcs.res \ test_lambda.res \ @@ -57,6 +59,7 @@ NEW_TESTS ?= \ test_normal.res \ test_quickfix.res \ test_signs.res \ + test_smartindent.res \ test_substitute.res \ test_syntax.res \ test_tabpage.res \ diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index 115659a013..8b313b5a35 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -2,8 +2,8 @@ function! Test_charsearch() enew! call append(0, ['Xabcdefghijkemnopqretuvwxyz', - \ 'Yabcdefghijkemnopqretuvwxyz', - \ 'Zabcdefghijkemnokqretkvwxyz']) + \ 'Yabcdefghijkemnopqretuvwxyz', + \ 'Zabcdefghijkemnokqretkvwxyz']) " check that "fe" and ";" work 1 normal! ylfep;;p,,p diff --git a/src/nvim/testdir/test_fnameescape.vim b/src/nvim/testdir/test_fnameescape.vim index ce2cd3f7a6..cdff0dfbd9 100644 --- a/src/nvim/testdir/test_fnameescape.vim +++ b/src/nvim/testdir/test_fnameescape.vim @@ -6,7 +6,7 @@ function! Test_fnameescape() try exe "w! " . fnameescape(fname) let status = v:true - endtry + endtry call assert_true(status, "Space") call delete(fname) diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim new file mode 100644 index 0000000000..c4aa6f9218 --- /dev/null +++ b/src/nvim/testdir/test_gf.vim @@ -0,0 +1,33 @@ + +" This is a test if a URL is recognized by "gf", with the cursor before and +" after the "://". Also test ":\\". +function! Test_gf_url() + enew! + call append(0, [ + \ "first test for URL://machine.name/tmp/vimtest2a and other text", + \ "second test for URL://machine.name/tmp/vimtest2b. And other text", + \ "third test for URL:\\\\machine.name\\vimtest2c and other text", + \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text" + \ ]) + call cursor(1,1) + call search("^first") + call search("tmp") + call assert_equal("URL://machine.name/tmp/vimtest2a", expand("")) + call search("^second") + call search("URL") + call assert_equal("URL://machine.name/tmp/vimtest2b", expand("")) + if has("ebcdic") + set isf=@,240-249,/,.,-,_,+,,,$,:,~,\ + else + set isf=@,48-57,/,.,-,_,+,,,$,:,~,\ + endif + call search("^third") + call search("name") + call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("")) + call search("^fourth") + call search("URL") + call assert_equal("URL:\\\\machine.name\\tmp\\vimtest2d", expand("")) + + set isf&vim + enew! +endfunction diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim new file mode 100644 index 0000000000..066fdd0250 --- /dev/null +++ b/src/nvim/testdir/test_hlsearch.vim @@ -0,0 +1,34 @@ +" Test for v:hlsearch + +function! Test_hlsearch() + new + call setline(1, repeat(['aaa'], 10)) + set hlsearch nolazyredraw + let r=[] + " redraw is needed to make hlsearch highlight the matches + exe "normal! /aaa\" | redraw + let r1 = screenattr(1, 1) + nohlsearch | redraw + call assert_notequal(r1, screenattr(1,1)) + let v:hlsearch=1 | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + set hlsearch | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! n" | redraw + call assert_equal(r1, screenattr(1,1)) + let v:hlsearch=0 | redraw + call assert_notequal(r1, screenattr(1,1)) + exe "normal! /\" | redraw + call assert_equal(r1, screenattr(1,1)) + set nohls + exe "normal! /\" | redraw + call assert_notequal(r1, screenattr(1,1)) + call assert_fails('let v:hlsearch=[]', 'E745') + call garbagecollect(1) + call getchar(1) + enew! +endfunction diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim new file mode 100644 index 0000000000..d00eac9798 --- /dev/null +++ b/src/nvim/testdir/test_smartindent.vim @@ -0,0 +1,14 @@ + +" Tests for not doing smart indenting when it isn't set. +function! Test_nosmartindent() + new + call append(0, [" some test text", + \ " test text", + \ "test text", + \ " test text"]) + set nocindent nosmartindent autoindent + exe "normal! gg/some\" + exe "normal! 2cc#test\" + call assert_equal(" #test", getline(1)) + enew! | close +endfunction diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 2044c23f79..0d697b3f3e 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -65,4 +65,34 @@ func Test_duplicate_tagjump() call delete('Xfile1') endfunc +" Tests for [ CTRL-I and CTRL-W CTRL-I commands +function Test_keyword_jump() + call writefile(["#include Xinclude", "", + \ "", + \ "/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xtestfile') + call writefile(["/* test text test tex start here", + \ " some text", + \ " test text", + \ " start OK if found this line", + \ " start found wrong line", + \ "test text"], 'Xinclude') + new Xtestfile + call cursor(1,1) + call search("start") + exe "normal! 5[\" + call assert_equal(" start OK if found this line", getline('.')) + call cursor(1,1) + call search("start") + exe "normal! 5\\" + call assert_equal(" start OK if found this line", getline('.')) + enew! | only + call delete('Xtestfile') + call delete('Xinclude') +endfunction + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index 500e8984c0..dd583f6ffd 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -74,10 +74,10 @@ static char *features[] = { // clang-format off static int included_patches[] = { - // 2367, + // 2367,NA // 2366 NA // 2365 NA - // 2364, + // 2364,NA // 2363 NA 2362, // 2361 NA @@ -98,7 +98,7 @@ static int included_patches[] = { 2346, // 2345 NA // 2344 NA - // 2343, + 2343, // 2342 NA 2341, // 2340 NA -- cgit From 18e7d552008b92dd3ecd42bf6855530838fd22ab Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 02:13:50 +0200 Subject: terminal.c:redraw(): Avoid invalid cursor col (#6379) Removed the call to validate_cursor() because mb_check_adjust_col() is already called in adjust_topline(). Closes #6378 References #6203 https://s3.amazonaws.com/archive.travis-ci.org/jobs/215498258/log.txt [ ERROR ] ...ovim/neovim/test/functional/terminal/scrollback_spec.lua @ 386: 'scrollback' option set to 0 behaves as 1 (10621.17 ms) ==================== File /home/travis/build/neovim/neovim/build/log/ubsan.12836 ==================== = ================================================================= = ==12836==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100002cd00 at pc 0x000000eafe90 bp 0x7ffc8661fe50 sp 0x7ffc8661fe48 = READ of size 1 at 0x62100002cd00 thread T0 = #0 0xeafe8f in utf_head_off /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1457:7 = #1 0x6b890e in getvcol /home/travis/build/neovim/neovim/src/nvim/charset.c:1169:15 = #2 0x6bc777 in getvvcol /home/travis/build/neovim/neovim/src/nvim/charset.c:1336:5 = #3 0xfc067b in curs_columns /home/travis/build/neovim/neovim/src/nvim/move.c:730:5 = #4 0xfbc8db in validate_cursor /home/travis/build/neovim/neovim/src/nvim/move.c:510:5 = #5 0x14479ed in setcursor /home/travis/build/neovim/neovim/src/nvim/screen.c:6363:5 = #6 0x17fe054 in redraw /home/travis/build/neovim/neovim/src/nvim/terminal.c:1175:5 = #7 0x17f95e4 in terminal_enter /home/travis/build/neovim/neovim/src/nvim/terminal.c:392:3 = #8 0x70eb2b in edit /home/travis/build/neovim/neovim/src/nvim/edit.c:1300:7 = #9 0x11097d1 in normal_finish_command /home/travis/build/neovim/neovim/src/nvim/normal.c:947:13 = #10 0x1081191 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1138:3 = #11 0x170b813 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #12 0x103631b in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:464:3 = #13 0xdfb7a8 in main /home/travis/build/neovim/neovim/src/nvim/main.c:552:3 = #14 0x2b8a3c85bf44 in __libc_start_main /build/eglibc-MjiXCM/eglibc-2.19/csu/libc-start.c:287 = #15 0x447b25 in _start (/home/travis/build/neovim/neovim/build/bin/nvim+0x447b25) = = 0x62100002cd00 is located 0 bytes to the right of 4096-byte region [0x62100002bd00,0x62100002cd00) = allocated by thread T0 here: = #0 0x4f1e98 in malloc (/home/travis/build/neovim/neovim/build/bin/nvim+0x4f1e98) = #1 0xf28774 in try_malloc /home/travis/build/neovim/neovim/src/nvim/memory.c:84:15 = #2 0xf28934 in xmalloc /home/travis/build/neovim/neovim/src/nvim/memory.c:118:15 = #3 0xec7be8 in mf_alloc_bhdr /home/travis/build/neovim/neovim/src/nvim/memfile.c:646:17 = #4 0xec58d4 in mf_new /home/travis/build/neovim/neovim/src/nvim/memfile.c:297:12 = #5 0xeda8a8 in ml_new_data /home/travis/build/neovim/neovim/src/nvim/memline.c:2697:16 = #6 0xed7beb in ml_open /home/travis/build/neovim/neovim/src/nvim/memline.c:349:8 = #7 0x643fcd in open_buffer /home/travis/build/neovim/neovim/src/nvim/buffer.c:109:7 = #8 0xa7038c in do_ecmd /home/travis/build/neovim/neovim/src/nvim/ex_cmds.c:2483:24 = #9 0xb5bb49 in do_exedit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6839:9 = #10 0xb7b6d8 in ex_edit /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:6767:3 = #11 0xb2a598 in do_one_cmd /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:2208:5 = #12 0xb08f47 in do_cmdline /home/travis/build/neovim/neovim/src/nvim/ex_docmd.c:602:20 = #13 0x109997b in nv_colon /home/travis/build/neovim/neovim/src/nvim/normal.c:4492:18 = #14 0x1081188 in normal_execute /home/travis/build/neovim/neovim/src/nvim/normal.c:1135:3 = #15 0x170b813 in state_enter /home/travis/build/neovim/neovim/src/nvim/state.c:58:26 = #16 0x103631b in normal_enter /home/travis/build/neovim/neovim/src/nvim/normal.c:464:3 = #17 0xdfb7a8 in main /home/travis/build/neovim/neovim/src/nvim/main.c:552:3 = #18 0x2b8a3c85bf44 in __libc_start_main /build/eglibc-MjiXCM/eglibc-2.19/csu/libc-start.c:287 = = SUMMARY: AddressSanitizer: heap-buffer-overflow /home/travis/build/neovim/neovim/src/nvim/mbyte.c:1457:7 in utf_head_off = Shadow bytes around the buggy address: = 0x0c427fffd950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = 0x0c427fffd990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 = =>0x0c427fffd9a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = 0x0c427fffd9f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa = Shadow byte legend (one shadow byte represents 8 application bytes): = Addressable: 00 = Partially addressable: 01 02 03 04 05 06 07 = Heap left redzone: fa = Heap right redzone: fb = Freed heap region: fd = Stack left redzone: f1 = Stack mid redzone: f2 = Stack right redzone: f3 = Stack partial redzone: f4 = Stack after return: f5 = Stack use after scope: f8 = Global redzone: f9 = Global init order: f6 = Poisoned by user: f7 = Container overflow: fc = Array cookie: ac = Intra object redzone: bb = ASan internal: fe = Left alloca redzone: ca = Right alloca redzone: cb = ==12836==ABORTING ===================================================================================================== ./test/helpers.lua:82: assertion failed! stack traceback: ./test/helpers.lua:82: in function 'check_logs' ./test/functional/helpers.lua:643: in function <./test/functional/helpers.lua:642> --- src/nvim/terminal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index dc418e25fa..c81e883fb9 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1155,7 +1155,6 @@ static void redraw(bool restore_cursor) save_col = ui_current_col(); } block_autocmds(); - validate_cursor(); if (must_redraw) { update_screen(0); @@ -1172,7 +1171,7 @@ static void redraw(bool restore_cursor) int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); curwin->w_cursor.coladd = 0; - setcursor(); + mb_check_adjust_col(curwin); } unblock_autocmds(); -- cgit From fb146e80aa1ead96518f38b9684e39249bc83485 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Jul 2016 23:16:23 +0300 Subject: eval: Split eval.c into smaller files --- src/clint.py | 7 +- src/nvim/api/private/helpers.c | 17 +- src/nvim/api/vim.c | 5 +- src/nvim/buffer.c | 2 +- src/nvim/buffer_defs.h | 45 +- src/nvim/charset.c | 26 +- src/nvim/cursor.c | 15 +- src/nvim/edit.c | 13 +- src/nvim/eval.c | 2961 ++++++++++++++------------------------- src/nvim/eval/decode.c | 98 +- src/nvim/eval/decode.h | 2 +- src/nvim/eval/encode.c | 9 +- src/nvim/eval/executor.c | 114 ++ src/nvim/eval/executor.h | 9 + src/nvim/eval/gc.c | 11 + src/nvim/eval/gc.h | 12 + src/nvim/eval/typval.c | 1171 ++++++++++++++++ src/nvim/eval/typval.h | 285 ++++ src/nvim/eval/typval_encode.c.h | 2 +- src/nvim/eval/typval_encode.h | 2 +- src/nvim/eval_defs.h | 286 ---- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_cmds.h | 2 +- src/nvim/ex_cmds2.c | 24 +- src/nvim/ex_docmd.c | 4 +- src/nvim/ex_eval.c | 14 +- src/nvim/ex_getln.c | 6 +- src/nvim/ex_getln.h | 2 +- src/nvim/globals.h | 16 +- src/nvim/main.c | 7 +- src/nvim/mark.c | 23 + src/nvim/mark_defs.h | 2 +- src/nvim/mbyte.c | 41 +- src/nvim/mbyte.h | 23 + src/nvim/memory.c | 13 + src/nvim/normal.c | 14 +- src/nvim/ops.c | 73 +- src/nvim/ops.h | 2 +- src/nvim/quickfix.c | 2 +- src/nvim/regexp.c | 51 +- src/nvim/regexp_nfa.c | 4 +- src/nvim/shada.c | 36 +- src/nvim/spell.c | 2 +- src/nvim/strings.c | 2 +- src/nvim/strings.h | 2 +- src/nvim/tag.c | 8 +- src/nvim/undo.c | 4 +- src/nvim/window.c | 15 +- 48 files changed, 2961 insertions(+), 2525 deletions(-) create mode 100644 src/nvim/eval/executor.c create mode 100644 src/nvim/eval/executor.h create mode 100644 src/nvim/eval/gc.c create mode 100644 src/nvim/eval/gc.h create mode 100644 src/nvim/eval/typval.c create mode 100644 src/nvim/eval/typval.h delete mode 100644 src/nvim/eval_defs.h (limited to 'src') diff --git a/src/clint.py b/src/clint.py index ce31822ada..61c53d128e 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2268,11 +2268,14 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # //!< Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment + # or they are Vim {{{ fold markers match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^!< ', line[commentend:]) or Search(r'^/< ', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) + Search(r'^/+ ', line[commentend:]) or + Search(r'^(?:\{{3}|\}{3})\d*(?: |$)', + line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -3575,7 +3578,7 @@ def main(): if __name__ == '__main__': main() -# vim: ts=4 sts=4 sw=4 +# vim: ts=4 sts=4 sw=4 foldmarker=▶,▲ # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7efa086af2..ff45cad8f5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -14,6 +14,7 @@ #include "nvim/window.h" #include "nvim/memory.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/option.h" @@ -87,7 +88,7 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (uint8_t *) key.data); + hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); if (HASHITEM_EMPTY(hi)) { api_set_error(err, Validation, _("Key not found")); @@ -177,13 +178,13 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (retval) { rv = vim_to_object(&di->di_tv); } - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); } // Update the value copy_tv(&tv, &di->di_tv); // Clear the temporary variable - clear_tv(&tv); + tv_clear(&tv); } return rv; @@ -682,20 +683,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; case kObjectTypeArray: { - list_T *list = list_alloc(); + list_T *const list = tv_list_alloc(); for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); if (!object_to_vim(item, &li->li_tv, err)) { // cleanup - listitem_free(li); - list_free(list); + tv_list_item_free(li); + tv_list_free(list); return false; } - list_append(list, li); + tv_list_append(list, li); } list->lv_refcount++; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 413456c615..59c0200395 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -22,6 +22,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/option.h" #include "nvim/syntax.h" #include "nvim/getchar.h" @@ -237,11 +238,11 @@ Object nvim_call_function(String fname, Array args, Error *err) if (!try_end(err)) { rv = vim_to_object(&rettv); } - clear_tv(&rettv); + tv_clear(&rettv); free_vim_args: while (i > 0) { - clear_tv(&vim_args[--i]); + tv_clear(&vim_args[--i]); } return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4a07884f98..f7333fead4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1472,7 +1472,7 @@ static inline void buf_init_changedtick(buf_T *const buf) { STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key), "buf->changedtick_di cannot hold large enough keys"); - buf->changedtick_di = (dictitem16_T) { + buf->changedtick_di = (ChangedtickDictItem) { .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC. .di_tv = (typval_T) { .v_type = VAR_NUMBER, diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 9d350c763e..20a2b931bd 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -21,8 +21,6 @@ typedef struct { #include "nvim/pos.h" // for the number window-local and buffer-local options #include "nvim/option_defs.h" -// for optional iconv support -#include "nvim/iconv.h" // for jump list and tag stack sizes in a buffer and mark types #include "nvim/mark_defs.h" // for u_header_T; needs buf_T. @@ -30,7 +28,9 @@ typedef struct { // for hashtab_T #include "nvim/hashtab.h" // for dict_T -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" +// for proftime_T +#include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" // for Map(K, V) @@ -318,25 +318,6 @@ typedef struct { String save_inputbuf; } tasave_T; -/* - * Used for conversion of terminal I/O and script files. - */ -typedef struct { - int vc_type; /* zero or one of the CONV_ values */ - int vc_factor; /* max. expansion factor */ -# ifdef USE_ICONV - iconv_t vc_fd; /* for CONV_ICONV */ -# endif - bool vc_fail; /* fail for invalid char, don't use '?' */ -} vimconv_T; - -#define CONV_NONE 0 -#define CONV_TO_UTF8 1 -#define CONV_9_TO_UTF8 2 -#define CONV_TO_LATIN1 3 -#define CONV_TO_LATIN9 4 -#define CONV_ICONV 5 - /* * Structure used for mappings and abbreviations. */ @@ -447,6 +428,10 @@ typedef struct { char_u *b_syn_isk; // iskeyword option } synblock_T; +/// Type used for changedtick_di member in buf_T +/// +/// Primary exists so that literals of relevant type can be made. +typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem; #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 @@ -491,7 +476,7 @@ struct file_buffer { // file has been changed and not written out. /// Change identifier incremented for each change, including undo #define b_changedtick changedtick_di.di_tv.vval.v_number - dictitem16_T changedtick_di; // b:changedtick dictionary item. + ChangedtickDictItem changedtick_di; // b:changedtick dictionary item. bool b_saving; /* Set to true if we are in the middle of saving the buffer. */ @@ -735,8 +720,8 @@ struct file_buffer { int b_bad_char; /* "++bad=" argument when edit started or 0 */ int b_start_bomb; /* 'bomb' when it was read */ - dictitem_T b_bufvar; /* variable for "b:" Dictionary */ - dict_T *b_vars; /* internal variables, local to buffer */ + ScopeDictDictItem b_bufvar; ///< Variable for "b:" Dictionary. + dict_T *b_vars; ///< b: scope dictionary. /* When a buffer is created, it starts without a swap file. b_may_swap is * then set to indicate that a swap file may be opened later. It is reset @@ -824,9 +809,9 @@ struct tabpage_S { buf_T *(tp_diffbuf[DB_COUNT]); int tp_diff_invalid; ///< list of diffs is outdated frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots - dictitem_T tp_winvar; ///< variable for "t:" Dictionary - dict_T *tp_vars; ///< internal variables, local to tab page - char_u *tp_localdir; ///< Absolute path of local CWD or NULL + ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary. + dict_T *tp_vars; ///< Internal variables, local to tab page. + char_u *tp_localdir; ///< Absolute path of local cwd or NULL. }; /* @@ -1118,8 +1103,8 @@ struct window_S { long w_scbind_pos; - dictitem_T w_winvar; /* variable for "w:" Dictionary */ - dict_T *w_vars; /* internal variables, local to window */ + ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary. + dict_T *w_vars; ///< Dictionary with w: variables. int w_farsi; /* for the window dependent Farsi functions */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index efe32b915f..cb6a9fa43c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -41,8 +41,10 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) +#define GET_CHARTAB_TAB(chartab, c) \ + ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) + GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; @@ -634,7 +636,7 @@ int char2cells(int c) /// @param p /// /// @return number of display cells. -int ptr2cells(char_u *p) +int ptr2cells(const char_u *p) { // For UTF-8 we need to look at more bytes if the first byte is >= 0x80. if (*p >= 0x80) { @@ -776,6 +778,21 @@ bool vim_iswordc(int c) return vim_iswordc_buf(c, curbuf); } +/// Check that "c" is a keyword character +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param[in] c Character to check. +/// @param[in] chartab Buffer chartab. +bool vim_iswordc_tab(const int c, const uint64_t *const chartab) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (c >= 0x100) { + return utf_class(c) >= 2; + } + return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; +} + /// Check that "c" is a keyword character: /// Letters and characters from 'iskeyword' option for given buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). @@ -785,10 +802,7 @@ bool vim_iswordc(int c) bool vim_iswordc_buf(int c, buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB(buf, c) != 0; + return vim_iswordc_tab(c, buf->b_chartab); } /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 82f1bf0a16..45abd314fc 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -12,6 +12,7 @@ #include "nvim/state.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/mark.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "cursor.c.generated.h" @@ -227,9 +228,10 @@ static int coladvance2( } } - /* prevent from moving onto a trail byte */ - if (has_mbyte) - mb_adjustpos(curbuf, pos); + // Prevent from moving onto a trail byte. + if (has_mbyte) { + mark_mb_adjustpos(curbuf, pos); + } if (col < wcol) return FAIL; @@ -361,9 +363,10 @@ void check_cursor_col_win(win_T *win) win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; - /* Move the cursor to the head byte. */ - if (has_mbyte) - mb_adjustpos(win->w_buffer, &win->w_cursor); + // Move the cursor to the head byte. + if (has_mbyte) { + mark_mb_adjustpos(win->w_buffer, &win->w_cursor); + } } } else if (win->w_cursor.col < 0) { win->w_cursor.col = 0; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5544f0b163..77d5b2c816 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -15,6 +15,7 @@ #include "nvim/cursor.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/farsi.h" @@ -3461,8 +3462,8 @@ expand_by_function ( matchdict = rettv.vval.v_dict; break; default: - /* TODO: Give error message? */ - clear_tv(&rettv); + // TODO(brammool): Give error message? + tv_clear(&rettv); break; } } @@ -3484,10 +3485,12 @@ expand_by_function ( ins_compl_add_dict(matchdict); theend: - if (matchdict != NULL) + if (matchdict != NULL) { dict_unref(matchdict); - if (matchlist != NULL) - list_unref(matchlist); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } } /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 927a1dcb0e..03d6eef9e3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -94,7 +94,11 @@ #include "nvim/lib/kvec.h" #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" -#include "nvim/eval/typval_encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -150,7 +154,6 @@ typedef struct lval_S { static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_listidx = N_("E684: list index out of range: %" PRId64); static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); @@ -167,17 +170,21 @@ static char *e_funcexts = N_( static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_letwrong = N_("E734: Wrong variable type for %s="); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); -static char_u * const empty_string = (char_u *)""; +// TODO(ZyX-I): move to eval/executor +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + static char_u * const namespace_char = (char_u *)"abglstvw"; -static dictitem_T globvars_var; /* variable used for g: */ +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/// g: value #define globvarht globvardict.dv_hashtab /* @@ -188,12 +195,15 @@ static hashtab_T compat_hashtab; hashtab_T func_hashtab; +// Used for checking if local variables or arguments used in a lambda. +static int *eval_lavars_used = NULL; + /* * Array to hold the hashtab with variables local to each sourced script. * Each item holds a variable (nameless) that points to the dict_T. */ typedef struct { - dictitem_T sv_var; + ScopeDictDictItem sv_var; dict_T sv_dict; } scriptvar_T; @@ -231,17 +241,44 @@ typedef enum { // The names of packages that once were loaded are remembered. static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL }; -// List heads for garbage collection. Although there can be a reference loop -// from partial to dict to partial, we don't need to keep track of the partial, -// since it will get freed when the dict is unused and gets freed. -static dict_T *first_dict = NULL; // list of all dicts -static list_T *first_list = NULL; // list of all lists - -#define FLEN_FIXED 40 - #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] +/// Short variable name length +#define VAR_SHORT_LEN 20 +/// Number of fixed variables used for arguments +#define FIXVAR_CNT 12 + +struct funccall_S { + ufunc_T *func; ///< Function being called. + int linenr; ///< Next line to be executed. + int returned; ///< ":return" used. + /// Fixed variables for arguments. + TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; + dict_T l_vars; ///< l: local function variables. + ScopeDictDictItem l_vars_var; ///< Variable for l: scope. + dict_T l_avars; ///< a: argument variables. + ScopeDictDictItem l_avars_var; ///< Variable for a: scope. + list_T l_varlist; ///< List for a:000. + listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. + typval_T *rettv; ///< Return value. + linenr_T breakpoint; ///< Next line with breakpoint or zero. + int dbg_tick; ///< Debug_tick when breakpoint was set. + int level; ///< Top nesting level of executed function. + proftime_T prof_child; ///< Time spent in a child. + funccall_T *caller; ///< Calling function or NULL. + int fc_refcount; ///< Number of user functions that reference this funccall. + int fc_copyID; ///< CopyID used for garbage collection. + garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". +}; + +///< Structure used by trans_function_name() +typedef struct { + dict_T *fd_dict; ///< Dictionary used. + char_u *fd_newkey; ///< New key in "dict" in allocated memory. + dictitem_T *fd_di; ///< Dictionary item used. +} funcdict_T; + /* * Info used by a ":for" loop. */ @@ -283,8 +320,8 @@ typedef enum { // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - dictitem16_T vv_di; ///< Value and name for key (max 16 chars) - char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. + TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { // VV_ tails differing from upcased string literals: @@ -389,7 +426,10 @@ static struct vimvar { #define vv_dict vv_di.di_tv.vval.v_dict #define vv_tv vv_di.di_tv -static dictitem_T vimvars_var; // variable used for v: +/// Variable used for v: +static ScopeDictDictItem vimvars_var; + +/// v: hashtab #define vimvarht vimvardict.dv_hashtab typedef enum { @@ -465,6 +505,17 @@ typedef struct fst { KHASH_MAP_INIT_STR(functions, VimLFuncDef) +/// Type of assert_* check being performed +typedef enum +{ + ASSERT_EQUAL, + ASSERT_NOTEQUAL, + ASSERT_MATCH, + ASSERT_NOTMATCH, + ASSERT_INRANGE, + ASSERT_OTHER, +} assert_type_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -547,7 +598,7 @@ void eval_init(void) dict_T *const msgpack_types_dict = dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = list_alloc(); + list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); @@ -570,7 +621,7 @@ void eval_init(void) dict_T *v_event = dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -601,7 +652,7 @@ void eval_clear(void) xfree(p->vv_str); p->vv_str = NULL; } else if (p->vv_di.di_tv.v_type == VAR_LIST) { - list_unref(p->vv_list); + tv_list_unref(p->vv_list); p->vv_list = NULL; } } @@ -832,7 +883,7 @@ void var_redir_stop(void) int eval_charconvert(const char *const enc_from, const char *const enc_to, const char *const fname_from, const char *const fname_to) { - int err = false; + bool err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); @@ -854,7 +905,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, int eval_printexpr(const char *const fname, const char *const args) { - int err = false; + bool err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); @@ -874,7 +925,7 @@ int eval_printexpr(const char *const fname, const char *const args) void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { - int err = FALSE; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_NEW, newfile, -1); @@ -888,7 +939,7 @@ void eval_diff(const char *const origfile, const char *const newfile, void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) { - int err; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); @@ -907,27 +958,29 @@ void eval_patch(const char *const origfile, const char *const difffile, int eval_to_bool ( char_u *arg, - int *error, + bool *error, char_u **nextcmd, int skip /* only parse, don't execute */ ) { typval_T tv; - int retval = FALSE; + bool retval = false; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) - *error = TRUE; - else { - *error = FALSE; + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; if (!skip) { retval = (get_tv_number_chk(&tv, error) != 0); - clear_tv(&tv); + tv_clear(&tv); } } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -953,7 +1006,7 @@ eval_to_string_skip ( retval = NULL; else { retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } if (skip) --emsg_skip; @@ -992,9 +1045,10 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv.vval.v_list->lv_len > 0) { ga_append(&ga, NL); + } } ga_append(&ga, NUL); retval = (char_u *)ga.ga_data; @@ -1003,7 +1057,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) retval = vim_strsave(numbuf); } else retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } return retval; @@ -1047,7 +1101,7 @@ int eval_to_number(char_u *expr) retval = -1; else { retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); } --emsg_off; @@ -1104,11 +1158,12 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == OK) { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { list = rettv.vval.v_list; + } } if (p_verbose == 0) @@ -1207,7 +1262,7 @@ int call_vim_function( ++sandbox; } - rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); @@ -1218,7 +1273,7 @@ int call_vim_function( xfree(argvars); if (ret == FAIL) { - clear_tv(rettv); + tv_clear(rettv); } return ret; @@ -1245,7 +1300,7 @@ call_func_retnr ( return -1; retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1270,7 +1325,7 @@ call_func_retstr ( return NULL; retval = vim_strsave(get_tv_string(&rettv)); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1294,7 +1349,7 @@ call_func_retlist ( return NULL; if (rettv.v_type != VAR_LIST) { - clear_tv(&rettv); + tv_clear(&rettv); return NULL; } @@ -1392,7 +1447,7 @@ int eval_foldexpr(char_u *arg, int *cp) *cp = *s++; retval = atol((char *)s); } - clear_tv(&tv); + tv_clear(&tv); } --emsg_off; if (use_sandbox) @@ -1464,13 +1519,13 @@ void ex_let(exarg_T *eap) ++emsg_skip; i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); if (eap->skip) { - if (i != FAIL) - clear_tv(&rettv); - --emsg_skip; + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, - op); - clear_tv(&rettv); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op); + tv_clear(&rettv); } } } @@ -1516,7 +1571,7 @@ ex_let_vars ( return FAIL; } - i = list_len(l); + i = tv_list_len(l); if (semicolon == 0 && var_count < i) { EMSG(_("E687: Less targets than List items")); return FAIL; @@ -1538,9 +1593,9 @@ ex_let_vars ( if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = list_alloc(); + l = tv_list_alloc(); while (item != NULL) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } @@ -1549,11 +1604,12 @@ ex_let_vars ( ltv.vval.v_list = l; l->lv_refcount = 1; - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, - (char_u *)"]", nextchars); - clear_tv(<v); - if (arg == NULL) + arg = ex_let_one(skipwhite(arg + 1), <v, false, + (char_u *)"]", nextchars); + tv_clear(<v); + if (arg == NULL) { return FAIL; + } break; } else if (*arg != ',' && *arg != ']') { EMSG2(_(e_intern2), "ex_let_vars()"); @@ -1774,7 +1830,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) tv.v_type, s == NULL ? "" : s, first); xfree(s); } - clear_tv(&tv); + tv_clear(&tv); } } } @@ -1788,6 +1844,8 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. * Returns a pointer to the char just after the var name. @@ -1952,6 +2010,8 @@ ex_let_one ( return arg_end; } +// TODO(ZyX-I): move to eval/executor + /// Get an lvalue /// /// Lvalue may be @@ -2081,8 +2141,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ return NULL; if (get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + // Not a number or string. + tv_clear(&var1); return NULL; } } @@ -2090,35 +2150,41 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Optionally get the second index [ :expr]. */ if (*p == ':') { if (lp->ll_tv->v_type == VAR_DICT) { - if (!quiet) + if (!quiet) { EMSG(_(e_dictrange)); - if (!empty1) - clear_tv(&var1); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)) { - if (!quiet) - EMSG(_("E709: [:] requires a List value")); - if (!empty1) - clear_tv(&var1); + if (!quiet) { + emsgf(_("E709: [:] requires a List value")); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } p = skipwhite(p + 1); - if (*p == ']') - lp->ll_empty2 = TRUE; - else { - lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return NULL; } } @@ -2127,12 +2193,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_range = FALSE; if (*p != ']') { - if (!quiet) - EMSG(_(e_missbrac)); - if (!empty1) - clear_tv(&var1); - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); + if (!quiet) { + emsgf(_(e_missbrac)); + } + if (!empty1) { + tv_clear(&var1); + } + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } return NULL; } @@ -2145,7 +2214,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, // "[key]": get key from "var1" key = get_tv_string_chk(&var1); // is number or string if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return NULL; } } @@ -2184,56 +2253,63 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Key does not exist in dict: may need to add it. */ if (*p == '[' || *p == '.' || unlet) { - if (!quiet) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); + if (!quiet) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } return NULL; } - if (len == -1) + if (len == -1) { lp->ll_newkey = vim_strsave(key); - else + } else { lp->ll_newkey = vim_strnsave(key, len); - if (len == -1) - clear_tv(&var1); + } + if (len == -1) { + tv_clear(&var1); + } break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { if (len == -1) { - clear_tv(&var1); + tv_clear(&var1); } return NULL; } - if (len == -1) - clear_tv(&var1); + if (len == -1) { + tv_clear(&var1); + } lp->ll_tv = &lp->ll_di->di_tv; } else { /* * Get the number and item for the only or first index of the List. */ - if (empty1) + if (empty1) { lp->ll_n1 = 0; - else { - lp->ll_n1 = get_tv_number(&var1); /* is number or string */ - clear_tv(&var1); + } else { + lp->ll_n1 = get_tv_number(&var1); // Is number or string. + tv_clear(&var1); } lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); if (lp->ll_li == NULL) { if (lp->ll_n1 < 0) { lp->ll_n1 = 0; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - if (!quiet) + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); + } return NULL; } @@ -2244,24 +2320,26 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); /* is number or string */ - clear_tv(&var2); + lp->ll_n2 = get_tv_number(&var2); // Is number or string. + tv_clear(&var2); if (lp->ll_n2 < 0) { - ni = list_find(lp->ll_list, lp->ll_n2); + ni = tv_list_find(lp->ll_list, lp->ll_n2); if (ni == NULL) { if (!quiet) EMSGN(_(e_listidx), lp->ll_n2); return NULL; } - lp->ll_n2 = list_idx_of_item(lp->ll_list, ni); + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); } - /* Check that lp->ll_n2 isn't before lp->ll_n1. */ - if (lp->ll_n1 < 0) - lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li); + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n2); + } return NULL; } } @@ -2273,6 +2351,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return p; } +// TODO(ZyX-I): move to eval/executor + /* * Clear lval "lp" that was filled by get_lval(). */ @@ -2282,6 +2362,8 @@ static void clear_lval(lval_T *lp) xfree(lp->ll_newkey); } +// TODO(ZyX-I): move to eval/executor + /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. @@ -2308,10 +2390,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch STRLEN(lp->ll_name)) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) - && tv_op(&tv, rettv, op) == OK) { + && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { set_var(lp->ll_name, &tv, false); } - clear_tv(&tv); + tv_clear(&tv); } } else { set_var(lp->ll_name, rettv, copy); @@ -2344,18 +2426,18 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch * Assign the List values to the list items. */ for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { - if (op != NULL && *op != '=') - tv_op(&lp->ll_li->li_tv, &ri->li_tv, op); - else { - clear_tv(&lp->ll_li->li_tv); + if (op != NULL && *op != '=') { + eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + } else { + tv_clear(&lp->ll_li->li_tv); copy_tv(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) break; if (lp->ll_li->li_next == NULL) { - /* Need to add an empty item. */ - list_append_number(lp->ll_list, 0); + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); assert(lp->ll_li->li_next); } lp->ll_li = lp->ll_li->li_next; @@ -2398,10 +2480,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } if (op != NULL && *op != '=') { - tv_op(lp->ll_tv, rettv, op); + eexe_mod_op(lp->ll_tv, rettv, (const char *)op); goto notify; } else { - clear_tv(lp->ll_tv); + tv_clear(lp->ll_tv); } } @@ -2421,145 +2503,13 @@ notify: } else { dictitem_T *di = lp->ll_di; dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); - clear_tv(&oldtv); - } - } - } -} - -/* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" - * Returns OK or FAIL. - */ -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) -{ - long n; - char_u numbuf[NUMBUFLEN]; - char_u *s; - - // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { - switch (tv1->v_type) { - case VAR_DICT: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: - break; - - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) - break; - /* List += List */ - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) - list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); - return OK; - - case VAR_NUMBER: - case VAR_STRING: - if (tv2->v_type == VAR_LIST) - break; - if (*op == '+' || *op == '-') { - /* nr += nr or nr -= nr*/ - n = get_tv_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = n; - - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; - clear_tv(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - if (*op == '+') - n += get_tv_number(tv2); - else - n -= get_tv_number(tv2); - clear_tv(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - if (tv2->v_type == VAR_FLOAT) - break; - - /* str .= str */ - s = get_tv_string(tv1); - s = concat_str(s, get_tv_string_buf(tv2, numbuf)); - clear_tv(tv1); - tv1->v_type = VAR_STRING; - tv1->vval.v_string = s; + tv_clear(&oldtv); } - return OK; - - case VAR_FLOAT: - { - float_T f; - - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) - break; - if (tv2->v_type == VAR_FLOAT) - f = tv2->vval.v_float; - else - f = get_tv_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; - } - return OK; - - case VAR_UNKNOWN: - assert(false); - } - } - - EMSG2(_(e_letwrong), op); - return FAIL; -} - -/* - * Add a watcher to a list. - */ -void list_add_watch(list_T *l, listwatch_T *lw) -{ - lw->lw_next = l->lv_watch; - l->lv_watch = lw; -} - -/* - * Remove a watcher from a list. - * No warning when it isn't found... - */ -void list_rem_watch(list_T *l, listwatch_T *lwrem) -{ - listwatch_T *lw, **lwp; - - lwp = &l->lv_watch; - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - if (lw == lwrem) { - *lwp = lw->lw_next; - break; } - lwp = &lw->lw_next; } } -/* - * Just before removing an item from a list: advance watchers to the next - * item. - */ -static void list_fix_watch(list_T *l, listitem_T *item) -{ - listwatch_T *lw; - - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) - if (lw->lw_item == item) - lw->lw_item = item->li_next; -} +// TODO(ZyX-I): move to eval/ex_cmds /* * Evaluate the expression used in a ":for var in expr" command. @@ -2567,14 +2517,14 @@ static void list_fix_watch(list_T *l, listitem_T *item) * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) +void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); char_u *expr; typval_T tv; list_T *l; - *errp = TRUE; /* default: there is an error */ + *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) @@ -2589,20 +2539,20 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) if (skip) ++emsg_skip; if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { - *errp = FALSE; + *errp = false; if (!skip) { l = tv.vval.v_list; if (tv.v_type != VAR_LIST) { EMSG(_(e_listreq)); - clear_tv(&tv); + tv_clear(&tv); } else if (l == NULL) { // a null list is like an empty list: do nothing - clear_tv(&tv); + tv_clear(&tv); } else { /* No need to increment the refcount, it's already set for the * list being used in "tv". */ fi->fi_list = l; - list_add_watch(l, &fi->fi_lw); + tv_list_watch_add(l, &fi->fi_lw); fi->fi_lw.lw_item = l->lv_first; } } @@ -2613,6 +2563,8 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) return fi; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Use the first item in a ":for" list. Advance to the next. * Assign the values to the variable (list). "arg" points to the first one. @@ -2636,6 +2588,8 @@ int next_for_item(void *fi_void, char_u *arg) return result; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Free the structure used to store info used by ":for". */ @@ -2644,8 +2598,8 @@ void free_for_info(void *fi_void) forinfo_T *fi = (forinfo_T *)fi_void; if (fi != NULL && fi->fi_list != NULL) { - list_rem_watch(fi->fi_list, &fi->fi_lw); - list_unref(fi->fi_list); + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); } xfree(fi); } @@ -2733,6 +2687,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } +// TODO(ZyX-I): move to eval/ex_cmds /* * ":1,25call func(arg1, arg2)" function call. @@ -2752,13 +2707,14 @@ void ex_call(exarg_T *eap) partial_T *partial = NULL; if (eap->skip) { - /* trans_function_name() doesn't work well when skipping, use eval0() - * instead to skip to any following command, e.g. for: - * :if 0 | call dict.foo().bar() | endif */ - ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_skip; + // trans_function_name() doesn't work well when skipping, use eval0() + // instead to skip to any following command, e.g. for: + // :if 0 | call dict.foo().bar() | endif. + emsg_skip++; + if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; return; } @@ -2786,7 +2742,7 @@ void ex_call(exarg_T *eap) /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { EMSG2(_("E107: Missing parentheses: %s"), eap->arg); @@ -2825,9 +2781,10 @@ void ex_call(exarg_T *eap) break; } - clear_tv(&rettv); - if (doesrange || eap->skip) + tv_clear(&rettv); + if (doesrange || eap->skip) { break; + } /* Stop when immediately aborting on error, or when an interrupt * occurred or an exception was thrown but not caught. @@ -2853,6 +2810,8 @@ end: xfree(tofree); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet[!] var1 ... " command. */ @@ -2861,6 +2820,8 @@ void ex_unlet(exarg_T *eap) ex_unletlock(eap, eap->arg, 0); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":lockvar" and ":unlockvar" commands */ @@ -2879,6 +2840,8 @@ void ex_lockvar(exarg_T *eap) ex_unletlock(eap, arg, deep); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. */ @@ -2912,8 +2875,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) error = TRUE; } else { if (do_lock_var(&lv, name_end, deep, - eap->cmdidx == CMD_lockvar) == FAIL) - error = TRUE; + eap->cmdidx == CMD_lockvar) == FAIL) { + error = true; + } } } @@ -2926,6 +2890,8 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) eap->nextcmd = check_nextcmd(arg); } +// TODO(ZyX-I): move to eval/ex_cmds + static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) { int ret = OK; @@ -2966,14 +2932,14 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) /* Delete a range of List items. */ while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { li = lp->ll_li->li_next; - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; ++lp->ll_n1; } } else { if (lp->ll_list != NULL) { // unlet a List item. - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; @@ -2992,7 +2958,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) if (watched) { dictwatcher_notify(d, key, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); xfree(key); } } @@ -3001,6 +2967,8 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) return ret; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * "unlet" a variable. Return OK if it existed, FAIL if not. * When "forceit" is TRUE don't complain if the variable doesn't exist. @@ -3060,7 +3028,7 @@ int do_unlet(char_u *name, int forceit) if (watched) { dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } return OK; } @@ -3071,12 +3039,15 @@ int do_unlet(char_u *name, int forceit) return FAIL; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Lock or unlock variable indicated by "lp". * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) +static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, + const bool lock) { int ret = OK; @@ -3104,117 +3075,28 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) } else { di->di_flags &= ~DI_FLAGS_LOCK; } - item_lock(&di->di_tv, deep, lock); + tv_item_lock(&di->di_tv, deep, lock); } } else if (lp->ll_range) { listitem_T *li = lp->ll_li; /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - item_lock(&li->li_tv, deep, lock); + tv_item_lock(&li->li_tv, deep, lock); li = li->li_next; ++lp->ll_n1; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(&lp->ll_li->li_tv, deep, lock); } else { // (un)lock a Dictionary item. - item_lock(&lp->ll_di->di_tv, deep, lock); + tv_item_lock(&lp->ll_di->di_tv, deep, lock); } return ret; } -/* - * Lock or unlock an item. "deep" is nr of levels to go. - */ -static void item_lock(typval_T *tv, int deep, int lock) -{ - static int recurse = 0; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E743: variable nested too deep for (un)lock")); - return; - } - if (deep == 0) { - return; - } - recurse++; - - // lock/unlock the item itself -#define CHANGE_LOCK(var, lock) \ - do { \ - var = ((VarLockStatus[]) { \ - [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_FIXED] = VAR_FIXED, \ - })[var]; \ - } while (0) - CHANGE_LOCK(tv->v_lock, lock); - - switch (tv->v_type) { - case VAR_LIST: { - list_T *const l = tv->vval.v_list; - if (l != NULL) { - CHANGE_LOCK(l->lv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - item_lock(&li->li_tv, deep - 1, lock); - } - } - } - break; - } - case VAR_DICT: { - dict_T *const d = tv->vval.v_dict; - if (d != NULL) { - CHANGE_LOCK(d->dv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } - } - } - break; - } - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_STRING: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: { - break; - } - case VAR_UNKNOWN: { - assert(false); - } - } -#undef CHANGE_LOCK - recurse--; -} - -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ -static int tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - /* * Delete all "menutrans_" variables. */ @@ -3343,6 +3225,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) return NULL; } +// TODO(ZyX-I): move to eval/expressions /// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. @@ -3379,6 +3262,8 @@ typedef enum { , TYPE_NOMATCH /* !~ */ } exptype_T; +// TODO(ZyX-I): move to eval/expressions + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -3400,15 +3285,15 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); if (ret == FAIL || !ends_excmd(*p)) { - if (ret != FAIL) - clear_tv(rettv); - /* - * Report the invalid expression unless the expression evaluation has - * been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), arg); + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting()) { + emsgf(_(e_invexpr2), arg); + } ret = FAIL; } if (nextcmd != NULL) @@ -3417,6 +3302,8 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle top level expression: * expr2 ? expr1 : expr1 @@ -3442,13 +3329,15 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if ((*arg)[0] == '?') { result = FALSE; if (evaluate) { - int error = FALSE; + bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; + } } /* @@ -3462,9 +3351,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Check for the ":". */ if ((*arg)[0] != ':') { - EMSG(_("E109: Missing ':' after '?'")); - if (evaluate && result) - clear_tv(rettv); + emsgf(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } @@ -3472,9 +3362,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Get the third variable. */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */ - if (evaluate && result) - clear_tv(rettv); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } if (evaluate && !result) @@ -3484,6 +3375,8 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle first level expression: * expr2 || expr2 || expr2 logical OR @@ -3498,7 +3391,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3513,12 +3406,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3532,11 +3427,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) - result = TRUE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3547,6 +3444,8 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle second level expression: * expr3 && expr3 && expr3 logical AND @@ -3561,7 +3460,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3576,12 +3475,14 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) - result = FALSE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3595,11 +3496,13 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) - result = FALSE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3610,6 +3513,8 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle third level expression: * var1 == var2 @@ -3706,7 +3611,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(p + len); if (eval5(arg, &var2, evaluate) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3728,15 +3633,15 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { EMSG(_("E692: Invalid operation for List")); } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { if (type_is) { @@ -3750,8 +3655,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) EMSG(_("E735: Can only compare Dictionary with Dictionary")); else EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { /* Compare two Dictionaries for being equal or unequal. */ @@ -3765,8 +3670,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) || var2.v_type == VAR_PARTIAL) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if ((rettv->v_type == VAR_PARTIAL @@ -3868,8 +3773,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_UNKNOWN: break; /* avoid gcc warning */ } } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; } @@ -3878,6 +3783,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fourth level expression: * + number addition @@ -3925,7 +3832,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * without evaluating the 2nd operand. So check before to avoid * side effects after an error. */ if (evaluate && get_tv_string_chk(rettv) == NULL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } } @@ -3935,7 +3842,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(*arg + 1); if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3946,28 +3853,28 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) if (op == '.') { s1 = get_tv_string_buf(rettv, buf1); /* already checked */ s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { /* type error ? */ - clear_tv(rettv); - clear_tv(&var2); + if (s2 == NULL) { // Type error ? + tv_clear(rettv); + tv_clear(&var2); return FAIL; } p = concat_str(s1, s2); - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { - /* concatenate Lists */ - if (list_concat(rettv->vval.v_list, var2.vval.v_list, - &var3) == FAIL) { - clear_tv(rettv); - clear_tv(&var2); + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); return FAIL; } - clear_tv(rettv); + tv_clear(rettv); *rettv = var3; } else { - int error = FALSE; + bool error = false; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -3978,7 +3885,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned * before evaluating the 2nd operand. */ - clear_tv(rettv); + tv_clear(rettv); return FAIL; } if (var2.v_type == VAR_FLOAT) @@ -3990,14 +3897,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) } else { n2 = get_tv_number_chk(&var2, &error); if (error) { - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if (rettv->v_type == VAR_FLOAT) f2 = n2; } - clear_tv(rettv); + tv_clear(rettv); /* If there is a float on either side the result is a float. */ if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { @@ -4016,12 +3923,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) rettv->vval.v_number = n1; } } - clear_tv(&var2); + tv_clear(&var2); } } return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fifth level expression: * * number multiplication @@ -4046,7 +3955,7 @@ eval6 ( long n1, n2; int use_float = FALSE; float_T f1 = 0, f2; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -4069,11 +3978,13 @@ eval6 ( n1 = 0; } else n1 = get_tv_number_chk(rettv, &error); - clear_tv(rettv); - if (error) + tv_clear(rettv); + if (error) { return FAIL; - } else + } + } else { n1 = 0; + } /* * Get the second variable. @@ -4092,11 +4003,13 @@ eval6 ( n2 = 0; } else { n2 = get_tv_number_chk(&var2, &error); - clear_tv(&var2); - if (error) + tv_clear(&var2); + if (error) { return FAIL; - if (use_float) + } + if (use_float) { f2 = n2; + } } /* @@ -4154,6 +4067,8 @@ eval6 ( return OK; } +// TODO(ZyX-I): move to eval/expressions + // Handle sixth level expression: // number number constant // "string" string constant @@ -4192,7 +4107,7 @@ static int eval7( int ret = OK; char_u *alias; - // Initialise variable so that clear_tv() can't mistake this for a + // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; @@ -4308,7 +4223,7 @@ static int eval7( ++*arg; } else if (ret == OK) { EMSG(_("E110: Missing ')'")); - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } break; @@ -4349,7 +4264,7 @@ static int eval7( // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = empty_string; + rettv->vval.v_string = (char_u *)tv_empty_string; rettv->v_type = VAR_FUNC; } @@ -4358,7 +4273,7 @@ static int eval7( // an exception was thrown but not caught. if (aborting()) { if (ret == OK) { - clear_tv(rettv); + tv_clear(rettv); } ret = FAIL; } @@ -4382,7 +4297,7 @@ static int eval7( // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - int error = false; + bool error = false; int val = 0; float_T f = 0.0; @@ -4392,7 +4307,7 @@ static int eval7( val = get_tv_number_chk(rettv, &error); } if (error) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } else { while (end_leader > start_leader) { @@ -4412,10 +4327,10 @@ static int eval7( } } if (rettv->v_type == VAR_FLOAT) { - clear_tv(rettv); + tv_clear(rettv); rettv->vval.v_float = f; } else { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = val; } @@ -4425,6 +4340,8 @@ static int eval7( return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". * "*arg" points to the '[' or '.'. @@ -4499,13 +4416,13 @@ eval_index ( * Get the (first) variable from inside the []. */ *arg = skipwhite(*arg + 1); - if (**arg == ':') - empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - else if (evaluate && get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + // Not a number or string. + tv_clear(&var1); return FAIL; } @@ -4515,28 +4432,32 @@ eval_index ( if (**arg == ':') { range = TRUE; *arg = skipwhite(*arg + 1); - if (**arg == ']') - empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return FAIL; } else if (evaluate && get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return FAIL; } } /* Check for the ']'. */ if (**arg != ']') { - if (verbose) - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); + if (verbose) { + emsgf(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } return FAIL; } *arg = skipwhite(*arg + 1); /* skip the ']' */ @@ -4546,14 +4467,14 @@ eval_index ( n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { n1 = get_tv_number(&var1); - clear_tv(&var1); + tv_clear(&var1); } if (range) { if (empty2) n2 = -1; else { n2 = get_tv_number(&var2); - clear_tv(&var2); + tv_clear(&var2); } } @@ -4587,15 +4508,16 @@ eval_index ( else s = vim_strnsave(s + n1, 1); } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = s; break; case VAR_LIST: - len = list_len(rettv->vval.v_list); - if (n1 < 0) + len = tv_list_len(rettv->vval.v_list); + if (n1 < 0) { n1 = len + n1; + } if (!empty1 && (n1 < 0 || n1 >= len)) { /* For a range we allow invalid values and return an empty * list. A list index out of range is an error. */ @@ -4616,29 +4538,31 @@ eval_index ( n2 = len - 1; if (!empty2 && (n2 < 0 || n2 + 1 < n1)) n2 = -1; - l = list_alloc(); - item = list_find(rettv->vval.v_list, n1); + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; ++l->lv_refcount; } else { - copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1); - clear_tv(rettv); + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); *rettv = var1; } break; case VAR_DICT: if (range) { - if (verbose) - EMSG(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } return FAIL; } { @@ -4647,22 +4571,25 @@ eval_index ( if (len == -1) { key = get_tv_string_chk(&var1); if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return FAIL; } } item = dict_find(rettv->vval.v_dict, key, (int)len); - if (item == NULL && verbose) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) + if (item == NULL && verbose) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL) { return FAIL; + } copy_tv(&item->di_tv, &var1); - clear_tv(rettv); + tv_clear(rettv); *rettv = var1; } break; @@ -4678,6 +4605,8 @@ eval_index ( return OK; } +// TODO(ZyX-I): move to eval/executor + /// Get an option value /// /// @param[in,out] arg Points to the '&' or '+' before the option name. Is @@ -4931,10 +4860,12 @@ char_u *partial_name(partial_T *pt) return pt->pt_func->uf_name; } +// TODO(ZyX-I): Move to eval/typval.h + static void partial_free(partial_T *pt) { for (int i = 0; i < pt->pt_argc; i++) { - clear_tv(&pt->pt_argv[i]); + tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); dict_unref(pt->pt_dict); @@ -4947,6 +4878,8 @@ static void partial_free(partial_T *pt) xfree(pt); } +// TODO(ZyX-I): Move to eval/typval.h + /// Unreference a closure: decrement the reference count and free it when it /// becomes zero. void partial_unref(partial_T *pt) @@ -4965,7 +4898,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) listitem_T *item; if (evaluate) { - l = list_alloc(); + l = tv_list_alloc(); } *arg = skipwhite(*arg + 1); @@ -4973,10 +4906,10 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ goto failret; if (evaluate) { - item = listitem_alloc(); + item = tv_list_item_alloc(); item->li_tv = tv; item->li_tv.v_lock = 0; - list_append(l, item); + tv_list_append(l, item); } if (**arg == ']') @@ -4992,7 +4925,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { - list_free(l); + tv_list_free(l); } return FAIL; } @@ -5007,142 +4940,8 @@ failret: return OK; } -/* - * Allocate an empty header for a list. - * Caller should take care of the reference count. - */ -list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET -{ - list_T *list = xcalloc(1, sizeof(list_T)); - /* Prepend the list to the list of lists for garbage collection. */ - if (first_list != NULL) - first_list->lv_used_prev = list; - list->lv_used_prev = NULL; - list->lv_used_next = first_list; - first_list = list; - return list; -} - -/* - * Allocate an empty list for a return value. - */ -static list_T *rettv_list_alloc(typval_T *rettv) -{ - list_T *l = list_alloc(); - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - rettv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; - return l; -} - -/// Unreference a list: decrement the reference count and free it when it -/// becomes zero. -void list_unref(list_T *l) { - if (l != NULL && --l->lv_refcount <= 0) { - list_free(l); - } -} - -/// Free a list, including all items it points to. -/// Ignores the reference count. -static void list_free_contents(list_T *l) { - listitem_T *item; - - for (item = l->lv_first; item != NULL; item = l->lv_first) { - // Remove the item before deleting it. - l->lv_first = item->li_next; - clear_tv(&item->li_tv); - xfree(item); - } -} - -static void list_free_list(list_T *l) { - // Remove the list from the list of lists for garbage collection. - if (l->lv_used_prev == NULL) { - first_list = l->lv_used_next; - } else { - l->lv_used_prev->lv_used_next = l->lv_used_next; - } - if (l->lv_used_next != NULL) { - l->lv_used_next->lv_used_prev = l->lv_used_prev; - } - - xfree(l); -} - -void list_free(list_T *l) { - if (!in_free_unref_items) { - list_free_contents(l); - list_free_list(l); - } -} - -/* - * Allocate a list item. - * It is not initialized, don't forget to set v_lock. - */ -listitem_T *listitem_alloc(void) FUNC_ATTR_NONNULL_RET -{ - return xmalloc(sizeof(listitem_T)); -} - -/* - * Free a list item. Also clears the value. Does not notify watchers. - */ -void listitem_free(listitem_T *item) -{ - clear_tv(&item->li_tv); - xfree(item); -} - -/* - * Remove a list item from a List and free it. Also clears the value. - */ -void listitem_remove(list_T *l, listitem_T *item) -{ - vim_list_remove(l, item, item); - listitem_free(item); -} - -/* - * Get the number of items in a list. - */ -static long list_len(list_T *l) -{ - if (l == NULL) - return 0L; - return l->lv_len; -} - -/* - * Return TRUE when two lists have exactly the same values. - */ -static int -list_equal ( - list_T *l1, - list_T *l2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - listitem_T *item1, *item2; - - if (l1 == NULL || l2 == NULL) - return FALSE; - if (l1 == l2) - return TRUE; - if (list_len(l1) != list_len(l2)) - return FALSE; - - for (item1 = l1->lv_first, item2 = l2->lv_first; - item1 != NULL && item2 != NULL; - item1 = item1->li_next, item2 = item2->li_next) - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) - return FALSE; - return item1 == NULL && item2 == NULL; -} +// TODO(ZyX-I): move to eval/typval /* * Return the dictitem that an entry in a hashtable points to. @@ -5152,6 +4951,8 @@ dictitem_T *dict_lookup(hashitem_T *hi) return HI2DI(hi); } +// TODO(ZyX-I): move to eval/typval + /* * Return TRUE when two dictionaries have exactly the same key/values. */ @@ -5255,8 +5056,7 @@ static bool func_equal( * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ -static int -tv_equal ( +int tv_equal( typval_T *tv1, typval_T *tv2, int ic, /* ignore case */ @@ -5298,501 +5098,41 @@ tv_equal ( switch (tv1->v_type) { case VAR_LIST: - ++recursive_cnt; - r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); - --recursive_cnt; + recursive_cnt++; + r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); + recursive_cnt--; return r; case VAR_DICT: ++recursive_cnt; r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - -/* - * Locate item with index "n" in list "l" and return it. - * A negative index is counted from the end; -1 is the last item. - * Returns NULL when "n" is out of range. - */ -listitem_T *list_find(list_T *l, long n) -{ - listitem_T *item; - long idx; - - if (l == NULL) - return NULL; - - /* Negative index is relative to the end. */ - if (n < 0) - n = l->lv_len + n; - - /* Check for index out of range. */ - if (n < 0 || n >= l->lv_len) - return NULL; - - /* When there is a cached index may start search from there. */ - if (l->lv_idx_item != NULL) { - if (n < l->lv_idx / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else if (n > (l->lv_idx + l->lv_len) / 2) { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } else { - /* closest to the cached index */ - item = l->lv_idx_item; - idx = l->lv_idx; - } - } else { - if (n < l->lv_len / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } - } - - while (n > idx) { - /* search forward */ - item = item->li_next; - ++idx; - } - while (n < idx) { - /* search backward */ - item = item->li_prev; - --idx; - } - - /* cache the used index */ - l->lv_idx = idx; - l->lv_idx_item = item; - - return item; -} - -/* - * Get list item "l[idx]" as a number. - */ -static long -list_find_nr ( - list_T *l, - long idx, - int *errorp /* set to TRUE when something wrong */ -) -{ - listitem_T *li; - - li = list_find(l, idx); - if (li == NULL) { - if (errorp != NULL) - *errorp = TRUE; - return -1L; - } - return get_tv_number_chk(&li->li_tv, errorp); -} - -/* - * Get list item "l[idx - 1]" as a string. Returns NULL for failure. - */ -char_u *list_find_str(list_T *l, long idx) -{ - listitem_T *li; - - li = list_find(l, idx - 1); - if (li == NULL) { - EMSGN(_(e_listidx), idx); - return NULL; - } - return get_tv_string(&li->li_tv); -} - -/* - * Locate "item" list "l" and return its index. - * Returns -1 when "item" is not in the list. - */ -static long list_idx_of_item(list_T *l, listitem_T *item) -{ - long idx = 0; - listitem_T *li; - - if (l == NULL) - return -1; - idx = 0; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) - ++idx; - if (li == NULL) - return -1; - return idx; -} - -/* - * Append item "item" to the end of list "l". - */ -void list_append(list_T *l, listitem_T *item) -{ - if (l->lv_last == NULL) { - /* empty list */ - l->lv_first = item; - l->lv_last = item; - item->li_prev = NULL; - } else { - l->lv_last->li_next = item; - item->li_prev = l->lv_last; - l->lv_last = item; - } - ++l->lv_len; - item->li_next = NULL; -} - -/* - * Append typval_T "tv" to the end of list "l". - */ -void list_append_tv(list_T *l, typval_T *tv) -{ - listitem_T *li = listitem_alloc(); - copy_tv(tv, &li->li_tv); - list_append(l, li); -} - -/* - * Add a list to a list. - */ -void list_append_list(list_T *list, list_T *itemlist) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = itemlist; - list_append(list, li); - ++itemlist->lv_refcount; -} - -/* - * Add a dictionary to a list. Used by getqflist(). - */ -void list_append_dict(list_T *list, dict_T *dict) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_dict = dict; - list_append(list, li); - ++dict->dv_refcount; -} - -/// Make a copy of "str" and append it as an item to list "l" -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -/// @param[in] len Length of the appended string. May be negative, in this -/// case string is considered to be usual zero-terminated -/// string. -void list_append_string(list_T *l, const char_u *str, int len) - FUNC_ATTR_NONNULL_ARG(1) -{ - if (str == NULL) { - list_append_allocated_string(l, NULL); - } else { - list_append_allocated_string(l, (len >= 0 - ? xmemdupz((char *) str, len) - : xstrdup((char *) str))); - } -} - -/// Append given string to the list -/// -/// Unlike list_append_string this function does not copy the string. -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -void list_append_allocated_string(list_T *l, char *const str) - FUNC_ATTR_NONNULL_ARG(1) -{ - listitem_T *li = listitem_alloc(); - - list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) str; -} - -/* - * Append "n" to list "l". - */ -void list_append_number(list_T *l, varnumber_T n) -{ - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_number = n; - list_append(l, li); -} - -/* - * Insert typval_T "tv" in list "l" before "item". - * If "item" is NULL append at the end. - */ -void list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) -{ - listitem_T *ni = listitem_alloc(); - - copy_tv(tv, &ni->li_tv); - list_insert(l, ni, item); -} - -void list_insert(list_T *l, listitem_T *ni, listitem_T *item) -{ - if (item == NULL) - /* Append new item at end of list. */ - list_append(l, ni); - else { - /* Insert new item before existing item. */ - ni->li_prev = item->li_prev; - ni->li_next = item; - if (item->li_prev == NULL) { - l->lv_first = ni; - ++l->lv_idx; - } else { - item->li_prev->li_next = ni; - l->lv_idx_item = NULL; - } - item->li_prev = ni; - ++l->lv_len; - } -} - -/* - * Extend "l1" with "l2". - * If "bef" is NULL append at the end, otherwise insert before this item. - */ -static void list_extend(list_T *l1, list_T *l2, listitem_T *bef) -{ - listitem_T *item; - int todo = l2->lv_len; - - /* We also quit the loop when we have inserted the original item count of - * the list, avoid a hang when we extend a list with itself. */ - for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) { - list_insert_tv(l1, &item->li_tv, bef); - } -} - -/* - * Concatenate lists "l1" and "l2" into a new list, stored in "tv". - * Return FAIL on failure to copy. - */ -static int list_concat(list_T *l1, list_T *l2, typval_T *tv) -{ - list_T *l; - - if (l1 == NULL || l2 == NULL) - return FAIL; - - /* make a copy of the first list. */ - l = list_copy(NULL, l1, false, 0); - if (l == NULL) - return FAIL; - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - - /* append all items from the second list */ - list_extend(l, l2, NULL); - return OK; -} - -/// Make a copy of list -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original list to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied list. May be NULL in case original list is NULL or some -/// failure happens. The refcount of the new list is set to 1. -static list_T *list_copy(const vimconv_T *const conv, - list_T *const orig, - const bool deep, - const int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - listitem_T *item; - listitem_T *ni; - - if (orig == NULL) - return NULL; - - list_T *copy = list_alloc(); - if (copyID != 0) { - /* Do this before adding the items, because one of the items may - * refer back to this list. */ - orig->lv_copyID = copyID; - orig->lv_copylist = copy; - } - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { - ni = listitem_alloc(); - if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { - xfree(ni); - break; - } - } else - copy_tv(&item->li_tv, &ni->li_tv); - list_append(copy, ni); - } - ++copy->lv_refcount; - if (item != NULL) { - list_unref(copy); - copy = NULL; - } - - return copy; -} - -/// Remove items "item" to "item2" from list "l". -/// @warning Does not free the listitem or the value! -void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) -{ - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { - --l->lv_len; - list_fix_watch(l, ip); - if (ip == item2) { - break; - } - } - - if (item2->li_next == NULL) { - l->lv_last = item->li_prev; - } else { - item2->li_next->li_prev = item->li_prev; - } - if (item->li_prev == NULL) { - l->lv_first = item2->li_next; - } else { - item->li_prev->li_next = item2->li_next; - } - l->lv_idx_item = NULL; -} - -typedef struct join_S { - char_u *s; - char_u *tofree; -} join_T; - -/// Join list into a string, helper function -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l List to join. -/// @param[in] sep Used separator. -/// @param[in] join_gap Garray to keep each list item string. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join_inner(garray_T *const gap, list_T *const l, - const char *const sep, garray_T *const join_gap) - FUNC_ATTR_NONNULL_ALL -{ - int sumlen = 0; - bool first = true; - listitem_T *item; - - /* Stringify each item in the list. */ - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - char *s; - size_t len; - s = encode_tv2echo(&item->li_tv, &len); - if (s == NULL) { - return FAIL; - } - - sumlen += (int) len; - - join_T *const p = GA_APPEND_VIA_PTR(join_T, join_gap); - p->tofree = p->s = (char_u *) s; - - line_breakcheck(); - } - - /* Allocate result buffer with its total size, avoid re-allocation and - * multiple copy operations. Add 2 for a tailing ']' and NUL. */ - if (join_gap->ga_len >= 2) - sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); - ga_grow(gap, sumlen + 2); - - for (int i = 0; i < join_gap->ga_len && !got_int; ++i) { - if (first) { - first = false; - } else { - ga_concat(gap, (const char_u *) sep); - } - const join_T *const p = ((const join_T *)join_gap->ga_data) + i; - - if (p->s != NULL) - ga_concat(gap, p->s); - line_breakcheck(); - } - - return OK; -} - -/// Join list into a string using given separator -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l Joined list. -/// @param[in] sep Separator. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join(garray_T *const gap, list_T *const l, - const char *const sep) - FUNC_ATTR_NONNULL_ALL -{ - if (l->lv_len < 1) { - return OK; - } + return r; - garray_T join_ga; - int retval; + case VAR_NUMBER: + return tv1->vval.v_number == tv2->vval.v_number; - ga_init(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, &join_ga); + case VAR_FLOAT: + return tv1->vval.v_float == tv2->vval.v_float; + + case VAR_STRING: + s1 = get_tv_string_buf(tv1, buf1); + s2 = get_tv_string_buf(tv2, buf2); + return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; -# define FREE_JOIN_TOFREE(join) xfree((join)->tofree) - GA_DEEP_CLEAR(&join_ga, join_T, FREE_JOIN_TOFREE); + case VAR_SPECIAL: + return tv1->vval.v_special == tv2->vval.v_special; - return retval; + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_UNKNOWN: + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does + // not equal anything, not even self. + return false; + } + + assert(false); + return false; } /// Get next (unique) copy ID @@ -6035,7 +5375,7 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// Note: This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// /// @param copyID Free lists/dictionaries that don't have this ID. /// @return true, if something was freed. @@ -6048,14 +5388,14 @@ static int free_unref_items(int copyID) // Let all "free" functions know that we are here. This means no // dictionaries, lists, or jobs are to be freed, because we will // do that here. - in_free_unref_items = true; + tv_in_free_unref_items = true; // PASS 1: free the contents of the items. We don't free the items // themselves yet, so that it is possible to decrement refcount counters. // Go through the list of dicts and free items without the copyID. // Don't free dicts that are referenced internally. - for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) { + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list @@ -6068,36 +5408,36 @@ static int free_unref_items(int copyID) // Go through the list of lists and free items without the copyID. // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. - for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) { + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_contents(ll); + tv_list_free_contents(ll); did_free = true; } } // PASS 2: free the items themselves. - for (dd = first_dict; dd != NULL; dd = dd_next) { + for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { dict_free_dict(dd); } } - for (ll = first_list; ll != NULL; ll = ll_next) { + for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_list(ll); + tv_list_free_list(ll); } } - in_free_unref_items = false; + tv_in_free_unref_items = false; return did_free; } @@ -6369,12 +5709,13 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET { dict_T *d = xmalloc(sizeof(dict_T)); - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; d->dv_used_prev = NULL; - first_dict = d; + gc_first_dict = d; hash_init(&d->dv_hashtab); d->dv_lock = VAR_UNLOCKED; @@ -6434,21 +5775,18 @@ void dict_unref(dict_T *d) /// Free a Dictionary, including all items it contains. /// Ignores the reference count. -static void dict_free_contents(dict_T *d) { - int todo; - hashitem_T *hi; - dictitem_T *di; - - - /* Lock the hashtab, we don't want it to resize while freeing items. */ +static void dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. hash_lock(&d->dv_hashtab); assert(d->dv_hashtab.ht_locked > 0); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { + size_t todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { if (!HASHITEM_EMPTY(hi)) { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = HI2DI(hi); hash_remove(&d->dv_hashtab, hi); dictitem_free(di); todo--; @@ -6465,10 +5803,12 @@ static void dict_free_contents(dict_T *d) { hash_clear(&d->dv_hashtab); } -static void dict_free_dict(dict_T *d) { +static void dict_free_dict(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ // Remove the dict from the list of dicts for garbage collection. if (d->dv_used_prev == NULL) { - first_dict = d->dv_used_next; + gc_first_dict = d->dv_used_next; } else { d->dv_used_prev->dv_used_next = d->dv_used_next; } @@ -6479,8 +5819,10 @@ static void dict_free_dict(dict_T *d) { xfree(d); } -void dict_free(dict_T *d) { - if (!in_free_unref_items) { +void dict_free(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { dict_free_contents(d); dict_free_dict(d); } @@ -6536,7 +5878,7 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item) */ void dictitem_free(dictitem_T *item) { - clear_tv(&item->di_tv); + tv_clear(&item->di_tv); if (item->di_flags & DI_FLAGS_ALLOC) { xfree(item); } @@ -6767,7 +6109,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) copy_tv(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); - clear_tv(&tv); + tv_clear(&tv); return res; } @@ -6845,34 +6187,35 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (**arg != ':') { EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } if (evaluate) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL) { // "key" is NULL when get_tv_string_buf_chk() gave an errmsg - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */ - if (evaluate) - clear_tv(&tvkey); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } goto failret; } if (evaluate) { item = dict_find(d, key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); + tv_clear(&tvkey); + tv_clear(&tv); goto failret; } item = dictitem_alloc(key); - clear_tv(&tvkey); + tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; if (dict_add(d, item) == FAIL) { @@ -7391,8 +6734,9 @@ get_func_tv ( } } - while (--argcount >= 0) - clear_tv(&argvars[argcount]); + while (--argcount >= 0) { + tv_clear(&argvars[argcount]); + } *arg = skipwhite(argp); return ret; @@ -7688,7 +7032,7 @@ call_func( } while (argv_clear > 0) { - clear_tv(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear]); } xfree(tofree); xfree(name); @@ -7804,7 +7148,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_op_wrapper(argvars, rettv, (FunPtr)&fabs); } else { varnumber_T n; - int error = FALSE; + bool error = false; n = get_tv_number_chk(&argvars[0], &error); if (error) @@ -7829,7 +7173,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - list_append_tv(l, &argvars[1]); + tv_list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } } else @@ -7950,9 +7294,10 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; } else { - rettv_list_alloc(rettv); - for (idx = 0; idx < ARGCOUNT; ++idx) { - list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1); + tv_list_alloc_ret(rettv); + for (idx = 0; idx < ARGCOUNT; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&ARGLIST[idx]), -1); } } } @@ -8024,10 +7369,10 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); } - list_append_string(vimvars[VV_ERRORS].vv_list, - gap->ga_data, gap->ga_len); + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } static void assert_equal_common(typval_T *argvars, assert_type_T atype) @@ -8116,10 +7461,10 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { - int error = (int)false; - varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + bool error = false; + const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); + const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); + const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); if (error) { return; @@ -8129,8 +7474,9 @@ void assert_inrange(typval_T *argvars) prepare_assert_error(&ga); char msg[55]; - vim_snprintf(msg, sizeof(msg), "range %" PRId64 " - %" PRId64 ",", - (int64_t)lower, (int64_t)upper); + vim_snprintf(msg, sizeof(msg), + "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", + lower, upper); fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], ASSERT_INRANGE); assert_error(&ga); @@ -8141,7 +7487,7 @@ void assert_inrange(typval_T *argvars) // Common for assert_true() and assert_false(). static void assert_bool(typval_T *argvars, bool is_true) { - int error = (int)false; + bool error = false; garray_T ga; if ((argvars[0].v_type != VAR_NUMBER @@ -8363,7 +7709,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = false; + bool error = false; char_u *name; rettv->vval.v_number = -1; @@ -8512,9 +7858,10 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, &dummy, true, partial, selfdict); } - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); + // Free the arguments. + while (argc > 0) { + tv_clear(&argv[--argc]); + } return r; } @@ -8707,8 +8054,8 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u buf2[NUMBUFLEN]; int def = 1; int type = VIM_GENERIC; - char_u *typestr; - int error = FALSE; + char_u *typestr; + bool error = false; message = get_tv_string_chk(&argvars[0]); if (message == NULL) @@ -8768,15 +8115,16 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { li = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; ic = get_tv_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { idx = get_tv_number_chk(&argvars[3], &error); if (!error) { - li = list_find(l, idx); - if (li == NULL) + li = tv_list_find(l, idx); + if (li == NULL) { EMSGN(_(e_listidx), idx); + } } } if (error) @@ -8793,7 +8141,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) hashitem_T *hi; if ((d = argvars[0].vval.v_dict) != NULL) { - int error = FALSE; + bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { ic = get_tv_number_chk(&argvars[2], &error); @@ -9349,7 +8697,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Handle d.key, l[idx], f(expr). n = (handle_subscript((const char **)&p, &tv, true, false) == OK); if (n) { - clear_tv(&tv); + tv_clear(&tv); } } } @@ -9372,8 +8720,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; expand_T xpc; - int error = FALSE; - char_u *result; + bool error = false; + char_u *result; rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN @@ -9390,9 +8738,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars(s, s, &len, NULL, &errormsg, NULL); --emsg_off; if (rettv->v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (result != NULL) { - list_append_string(rettv->vval.v_list, result, -1); + tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } } else rettv->vval.v_string = result; @@ -9407,14 +8755,14 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, - options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } @@ -9479,12 +8827,12 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) copy_tv(&di1->di_tv, &oldtv); } - clear_tv(&di1->di_tv); + tv_clear(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); if (watched) { dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -9504,7 +8852,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l1, *l2; listitem_T *item; long before; - int error = FALSE; + bool error = false; l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; @@ -9515,10 +8863,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (error) return; /* type error; errmsg already given */ - if (before == l1->lv_len) + if (before == l1->lv_len) { item = NULL; - else { - item = list_find(l1, before); + } else { + item = tv_list_find(l1, before); if (item == NULL) { EMSGN(_(e_listidx), before); return; @@ -9526,7 +8874,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else item = NULL; - list_extend(l1, l2, item); + tv_list_extend(l1, l2, item); copy_tv(&argvars[0], rettv); } @@ -9616,8 +8964,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) char_u *p; char_u pathbuf[NUMBUFLEN]; int count = 1; - int first = TRUE; - int error = FALSE; + bool first = true; + bool error = false; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -9632,13 +8980,14 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) if (*p != NUL) path = p; - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { count = get_tv_number_chk(&argvars[2], &error); + } } } if (count < 0) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (*fname != NUL && !error) { @@ -9652,11 +9001,11 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) (find_what == FINDFILE_DIR ? (char_u *)"" : curbuf->b_p_sua)); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); + first = false; + if (fresult != NULL && rettv->v_type == VAR_LIST) { + tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1); + } } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } @@ -9736,9 +9085,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); - clear_tv(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { break; + } if (!map && rem) { if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { @@ -9762,9 +9112,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL || did_emsg) break; - if (!map && rem) - listitem_remove(l, li); - ++idx; + if (!map && rem) { + tv_list_item_remove(l, li); + } + idx++; } } @@ -9819,24 +9170,25 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } } if (map) { - /* map(): replace the list item value */ - clear_tv(tv); + // map(): replace the list item value. + tv_clear(tv); rettv.v_lock = 0; *tv = rettv; } else { - int error = FALSE; + bool error = false; - /* filter(): when expr is zero remove the item */ + // filter(): when expr is zero remove the item *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by get_tv_number_chk(). + if (error) { goto theend; + } } retval = OK; theend: - clear_tv(&vimvars[VV_VAL].vv_tv); + tv_clear(&vimvars[VV_VAL].vv_tv); return retval; } @@ -10278,11 +9630,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { - int error = FALSE; + bool error = false; - li = list_find(l, get_tv_number_chk(&argvars[1], &error)); - if (!error && li != NULL) + li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + if (!error && li != NULL) { tv = &li->li_tv; + } } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { @@ -10326,11 +9679,9 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (STRCMP(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (rettv_list_alloc(rettv) != NULL) { - int i; - - for (i = 0; i < pt->pt_argc; i++) { - list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + if (tv_list_alloc_ret(rettv) != NULL) { + for (int i = 0; i < pt->pt_argc; i++) { + tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } } } else { @@ -10353,13 +9704,13 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *d = dict_alloc(); + dict_T *const d = dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); - list_append_dict(l, d); + tv_list_append_dict(l, d); } } @@ -10384,17 +9735,17 @@ static dict_T *get_buffer_info(buf_T *buf) dict_add_dict(dict, "variables", buf->b_vars); // List of windows displaying this buffer - list_T *windows = list_alloc(); + list_T *const windows = tv_list_alloc(); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { - list_append_number(windows, (varnumber_T)wp->handle); + tv_list_append_number(windows, (varnumber_T)wp->handle); } } dict_add_list(dict, "windows", windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *signs = list_alloc(); + list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); dict_add_list(dict, "signs", signs); } @@ -10410,7 +9761,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -10452,9 +9803,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } - dict_T *d = get_buffer_info(buf); + dict_T *const d = get_buffer_info(buf); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (argbuf != NULL) { return; @@ -10475,7 +9826,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (retlist) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) @@ -10496,8 +9847,8 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli if (end > buf->b_ml.ml_line_count) end = buf->b_ml.ml_line_count; while (start <= end) { - list_append_string( - rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)ml_get_buf(buf, start++, false), -1); } } } @@ -10594,7 +9945,7 @@ f_getbufvar_end: static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n; - int error = FALSE; + bool error = false; no_mapping++; for (;; ) { @@ -10802,12 +10153,13 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (pat != NULL) { ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } } xfree(pat); @@ -11110,7 +10462,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -11145,7 +10497,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (cur != NULL) { dict_T *dict = dict_alloc(); if (cur->match.regprog == NULL) { @@ -11158,11 +10510,11 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = list_alloc(); - list_append_number(l, (varnumber_T)llpos->lnum); + list_T *l = tv_list_alloc(); + tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { - list_append_number(l, (varnumber_T)llpos->col); - list_append_number(l, (varnumber_T)llpos->len); + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); } sprintf(buf, "pos%d", i + 1); dict_add_list(dict, buf, l); @@ -11181,7 +10533,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); } - list_append_dict(rettv->vval.v_list, dict); + tv_list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } } @@ -11205,20 +10557,22 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = rettv_list_alloc(rettv); - list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); - list_append_number(l, - (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, - (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + list_T *l = tv_list_alloc_ret(rettv); + tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) + ? (varnumber_T)fp->lnum + : (varnumber_T)0)); + tv_list_append_number( + l, ((fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0)); + tv_list_append_number( + l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { update_curswant(); - list_append_number(l, curwin->w_curswant == MAXCOL + tv_list_append_number(l, (curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL - : (varnumber_T)curwin->w_curswant + 1); + : (varnumber_T)curwin->w_curswant + 1)); } } @@ -11251,7 +10605,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) int regname; int arg2 = false; bool return_list = false; - int error = false; + bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -11279,7 +10633,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = list_alloc(); + rettv->vval.v_list = tv_list_alloc(); } rettv->vval.v_list->lv_refcount++; } else { @@ -11328,9 +10682,9 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) dict_add_nr_str(dict, "tabnr", tp_idx, NULL); - list_T *l = list_alloc(); + list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - list_append_number(l, (varnumber_T)wp->handle); + tv_list_append_number(l, (varnumber_T)wp->handle); } dict_add_list(dict, "windows", l); @@ -11345,7 +10699,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -11362,9 +10716,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tparg != NULL && tp != tparg) { continue; } - dict_T *d = get_tabpage_info(tp, tpnr); + dict_T *const d = get_tabpage_info(tp, tpnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (tparg != NULL) { return; @@ -11447,7 +10801,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -11467,9 +10821,9 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } winnr++; - dict_T *d = get_win_info(wp, tabnr, winnr); + dict_T *const d = get_win_info(wp, tabnr, winnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (wparg != NULL) { // found information about a specific window @@ -11504,7 +10858,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = get_tv_number_chk(vp, NULL); + int nr = (int)get_tv_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -11645,7 +10999,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int options = WILD_SILENT|WILD_USE_NL; expand_T xpc; - int error = FALSE; + bool error = false; /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ @@ -11669,14 +11023,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, + options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } ExpandCleanup(&xpc); } @@ -11688,7 +11043,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; // Flags for globpath. - int error = false; + bool error = false; // Return a string, or a list if the optional third argument is non-zero. rettv->v_type = VAR_STRING; @@ -11722,10 +11077,10 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); for (int i = 0; i < ga.ga_len; i++) { - list_append_string(rettv->vval.v_list, - ((char_u **)(ga.ga_data))[i], -1); + tv_list_append_string(rettv->vval.v_list, + ((const char **)(ga.ga_data))[i], -1); } } @@ -12249,11 +11604,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l != NULL) { item = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, get_tv_number_chk(&argvars[2], &error)); + // Start at specified item. Use the cached index that tv_list_find() + // sets, so that a negative number also works. + item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); idx = l->lv_idx; if (argvars[3].v_type != VAR_UNKNOWN) ic = get_tv_number_chk(&argvars[3], &error); @@ -12456,10 +11811,8 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long before = 0; - listitem_T *item; - list_T *l; - int error = false; + list_T *l; + bool error = false; const char *const arg_errmsg = _("insert() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); @@ -12467,6 +11820,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "insert()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); } @@ -12475,17 +11829,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (before == l->lv_len) - item = NULL; - else { - item = list_find(l, before); + listitem_T *item = NULL; + if (before != l->lv_len) { + item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); l = NULL; } } if (l != NULL) { - list_insert_tv(l, &argvars[1], item); + tv_list_insert_tv(l, &argvars[1], item); copy_tv(&argvars[0], rettv); } } @@ -12574,7 +11927,7 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) if ((d = argvars[0].vval.v_dict) == NULL) return; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { @@ -12582,8 +11935,8 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) --todo; di = HI2DI(hi); - li = listitem_alloc(); - list_append(rettv->vval.v_list, li); + li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); if (what == 0) { /* keys() */ @@ -12594,21 +11947,21 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) /* values() */ copy_tv(&di->di_tv, &li->li_tv); } else { - /* items() */ - l2 = list_alloc(); + // items() + l2 = tv_list_alloc(); li->li_tv.v_type = VAR_LIST; li->li_tv.v_lock = 0; li->li_tv.vval.v_list = l2; ++l2->lv_refcount; - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); li2->li_tv.v_type = VAR_STRING; li2->li_tv.v_lock = 0; li2->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); copy_tv(&di->di_tv, &li2->li_tv); } } @@ -12983,7 +12336,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } list_T *args = argvars[0].vval.v_list; - list_T *rv = list_alloc(); + list_T *rv = tv_list_alloc(); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -12994,11 +12347,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER || !(data = find_job(arg->li_tv.vval.v_number))) { - list_append_number(rv, -3); + tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the // status code when the job exits - list_append_number(rv, -1); + tv_list_append_number(rv, -1); data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue @@ -13097,7 +12450,7 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (sep != NULL) { ga_init(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, (char *) sep); + tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } else @@ -13181,7 +12534,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_tv_string(&argvars[0])); break; case VAR_LIST: - rettv->vval.v_number = list_len(argvars[0].vval.v_list); + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); @@ -13435,12 +12788,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 3 || type == 4) { // type 3: return empty list when there are no matches. // type 4: return ["", -1, -1, -1] - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (type == 4) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); + tv_list_append_string(rettv->vval.v_list, "", 0); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); } } else if (type == 2) { rettv->v_type = VAR_STRING; @@ -13461,16 +12814,17 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[2], &error); if (error) goto theend; if (l != NULL) { - li = list_find(l, start); - if (li == NULL) + li = tv_list_find(l, start); + if (li == NULL) { goto theend; - idx = l->lv_idx; /* use the cached index */ + } + idx = l->lv_idx; // Use the cached index. } else { if (start < 0) start = 0; @@ -13552,11 +12906,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* return list with matched string and submatches */ for (i = 0; i < NSUBEXP; ++i) { if (regmatch.endp[i] == NULL) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { - list_append_string(rettv->vval.v_list, - regmatch.startp[i], - (int)(regmatch.endp[i] - regmatch.startp[i])); + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); } } } else if (type == 2) { @@ -13583,7 +12937,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && l == NULL) { // matchstrpos() without a list: drop the second item - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first->li_next); + tv_list_item_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); } theend: @@ -13609,7 +12964,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ int prio = 10; /* default priority */ int id = -1; - int error = false; + bool error = false; char_u *conceal_char = NULL; rettv->vval.v_number = -1; @@ -13667,7 +13022,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int error = false; + bool error = false; int prio = 10; int id = -1; char_u *conceal_char = NULL; @@ -13708,7 +13063,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); int id = get_tv_number(&argvars[0]); @@ -13716,11 +13071,12 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *m; if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { - list_append_string(rettv->vval.v_list, syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); } else { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } } } @@ -13768,7 +13124,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) { long n = 0; long i; - int error = FALSE; + bool error = false; if (argvars[0].v_type == VAR_LIST) { list_T *l; @@ -13938,7 +13294,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL) { return; @@ -13966,7 +13322,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL || list->lv_first == NULL) { return; @@ -14011,9 +13367,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(ret_list, li); + tv_list_append(ret_list, li); if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; @@ -14188,11 +13544,11 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long start; - long end; - long stride = 1; + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; long i; - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { @@ -14204,16 +13560,17 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) stride = get_tv_number_chk(&argvars[2], &error); } - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else { - rettv_list_alloc(rettv); + if (error) { + return; // Type error; errmsg already given. + } + if (stride == 0) { + emsgf(_("E726: Stride is zero")); + } else if (stride > 0 ? end + 1 < start : end - 1 > start) { + emsgf(_("E727: Start past end")); + } else { + tv_list_alloc_ret(rettv); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { - list_append_number(rettv->vval.v_list, (varnumber_T)i); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } } } @@ -14244,7 +13601,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) maxline = get_tv_number(&argvars[2]); } - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); /* Always open the file in binary mode, library functions have a mind of * their own about CR-LF conversion. */ @@ -14293,11 +13650,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) @@ -14372,8 +13729,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; + tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + cnt--; } xfree(prev); @@ -14395,9 +13752,9 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL return FAIL; } - int error = false; - varnumber_T n1 = list_find_nr(arg->vval.v_list, 0L, &error); - varnumber_T n2 = list_find_nr(arg->vval.v_list, 1L, &error); + bool error = false; + varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0L, &error); + varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1L, &error); if (error) { return FAIL; } @@ -14456,9 +13813,9 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - rettv_list_alloc(rettv); - list_append_number(rettv->vval.v_list, u.split.high); - list_append_number(rettv->vval.v_list, u.split.low); + tv_list_alloc_ret(rettv); + tv_list_append_number(rettv->vval.v_list, u.split.high); + tv_list_append_number(rettv->vval.v_list, u.split.low); } /// f_reltimestr - return a string that represents the value of {time} @@ -14519,27 +13876,27 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listdictarg), "remove()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - int error = (int)false; + bool error = false; idx = get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, idx)) == NULL) { EMSGN(_(e_listidx), idx); - else { + } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - vim_list_remove(l, item, item); + tv_list_remove_items(l, item, item); *rettv = item->li_tv; xfree(item); } else { /* Remove range of items, return list with values. */ end = get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, end)) == NULL) { EMSGN(_(e_listidx), end); - else { + } else { int cnt = 0; for (li = item; li != NULL; li = li->li_next) { @@ -14547,11 +13904,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == item2) break; } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else { - vim_list_remove(l, item, item2); - l = rettv_list_alloc(rettv); + if (li == NULL) { // Didn't find "item2" after "item". + emsgf(_(e_invrange)); + } else { + tv_list_remove_items(l, item, item2); + l = tv_list_alloc_ret(rettv); l->lv_first = item; l->lv_last = item2; item->li_prev = NULL; @@ -14588,10 +13945,10 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].vval.v_list != NULL) { while (n-- > 0) { - list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } } else { @@ -14802,12 +14159,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) l->lv_len = 0; while (li != NULL) { listitem_T *const ni = li->li_prev; - list_append(l, li); + tv_list_append(l, li); li = ni; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + l->lv_refcount++; l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -15243,7 +14600,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int locally = 1; int thisblock = 0; - int error = FALSE; + bool error = false; rettv->vval.v_number = 1; /* default: FAIL */ @@ -15346,15 +14703,15 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); } /* @@ -15386,7 +14743,6 @@ do_searchpair ( int n; int r; int nest = 1; - int err; int options = SEARCH_KEEP; proftime_T tm; @@ -15442,7 +14798,8 @@ do_searchpair ( if (*skip != NUL) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); + bool err; + r = eval_to_bool(skip, &err, NULL, false); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ @@ -15513,7 +14870,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int flags = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); n = search_cmn(argvars, &match_pos, &flags); if (n > 0) { @@ -15521,10 +14878,11 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); - if (flags & SP_SUBPAT) - list_append_number(rettv->vval.v_list, (varnumber_T)n); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); + if (flags & SP_SUBPAT) { + tv_list_append_number(rettv->vval.v_list, (varnumber_T)n); + } } /// "serverlist()" function @@ -15534,13 +14892,13 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = rettv_list_alloc(rettv); + list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) addrs[i]; - list_append(l, li); + li->li_tv.vval.v_string = (char_u *)addrs[i]; + tv_list_append(l, li); } xfree(addrs); } @@ -15610,7 +14968,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; aco_save_T aco; // set curbuf to be our buf, temporarily @@ -15915,7 +15273,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) d = li->li_tv.vval.v_dict; if (dict_find(d, (char_u *)"pattern", -1) == NULL) { if (s == NULL) { - s = list_alloc(); + s = tv_list_alloc(); if (s == NULL) { return; } @@ -15929,7 +15287,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &di->di_tv); s->lv_refcount++; } else { break; @@ -15949,7 +15307,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); - list_unref(s); + tv_list_unref(s); s = NULL; } xfree(group); @@ -16193,7 +15551,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; ++varname; numval = get_tv_number_chk(varp, &error); @@ -16272,7 +15630,7 @@ typedef struct { char_u *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; - int item_compare_func_err; + bool item_compare_func_err; } sortinfo_T; static sortinfo_T *sortinfo = NULL; @@ -16399,13 +15757,13 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func(func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); + tv_clear(&argv[0]); + tv_clear(&argv[1]); if (res == FAIL) { res = ITEM_COMPARE_FAIL; @@ -16415,7 +15773,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type } - clear_tv(&rettv); + tv_clear(&rettv); // When the result would be zero, compare the pointers themselves. Makes // the sort stable. @@ -16470,7 +15828,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) rettv->v_type = VAR_LIST; ++l->lv_refcount; - len = list_len(l); + len = tv_list_len(l); if (len <= 1) { goto theend; // short list sorts pretty quickly } @@ -16490,7 +15848,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { - int error = FALSE; + bool error = false; i = get_tv_number_chk(&argvars[1], &error); if (error) { @@ -16569,7 +15927,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i].item); + tv_list_append(l, ptrs[i].item); } } } @@ -16605,8 +15963,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { l->lv_last = ptrs[i].item; } - list_fix_watch(l, li); - listitem_free(li); + tv_list_watch_fix(l, li); + tv_list_item_free(li); l->lv_len--; } } @@ -16667,7 +16025,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { /* Find the start and length of the badly spelled word. */ @@ -16692,14 +16050,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - list_append_string(rettv->vval.v_list, word, (int)len); - list_append_string(rettv->vval.v_list, - (char_u *)(attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - ""), - -1); + tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, + (attr == HLF_SPB ? "bad" + : attr == HLF_SPR ? "rare" + : attr == HLF_SPL ? "local" + : attr == HLF_SPC ? "caps" + : NULL), -1); } /* @@ -16708,13 +16065,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *str; - int typeerr = FALSE; + bool typeerr = false; int maxcount; garray_T ga; listitem_T *li; bool need_capital = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { str = get_tv_string(&argvars[0]); @@ -16735,11 +16092,11 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (int i = 0; i < ga.ga_len; ++i) { str = ((char_u **)ga.ga_data)[i]; - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); } @@ -16755,8 +16112,8 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *save_cpo; int match; colnr_T col = 0; - int keepempty = FALSE; - int typeerr = FALSE; + bool keepempty = false; + bool typeerr = false; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; @@ -16765,15 +16122,17 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) str = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = get_tv_number_chk(&argvars[2], &typeerr); + if (pat == NULL) { + typeerr = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + } } if (pat == NULL || *pat == NUL) pat = (char_u *)"[\\x01- ]\\+"; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (typeerr) return; @@ -16793,7 +16152,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 && *str != NUL && match && end < regmatch.endp[0])) { - list_append_string(rettv->vval.v_list, str, (int)(end - str)); + tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); } if (!match) break; @@ -16917,33 +16276,29 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "strgetchar()" function static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - int len; - int error = false; - int charidx; - rettv->vval.v_number = -1; - str = get_tv_string_chk(&argvars[0]); + + char_u *str = get_tv_string_chk(&argvars[0]); if (str == NULL) { return; } - len = (int)STRLEN(str); - charidx = get_tv_number_chk(&argvars[1], &error); + + bool error = false; + varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); if (error) { return; } - { - int byteidx = 0; + const size_t len = STRLEN(str); + size_t byteidx = 0; - while (charidx >= 0 && byteidx < len) { - if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); - break; - } - charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; } + charidx--; + byteidx += MB_CPTR2LEN(str + byteidx); } } @@ -16966,7 +16321,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; /* type error; errmsg already given */ if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start_idx = get_tv_number_chk(&argvars[2], &error); if (error || start_idx >= (int)STRLEN(haystack)) @@ -17048,22 +16403,17 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "strcharpart()" function -static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int nchar; - int nbyte = 0; - int charlen; - int len = 0; - int slen; - int error = false; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); +static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *const p = get_tv_string(&argvars[0]); + const size_t slen = STRLEN(p); - nchar = get_tv_number_chk(&argvars[1], &error); + int nbyte = 0; + bool error = false; + varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { - while (nchar > 0 && nbyte < slen) { + while (nchar > 0 && (size_t)nbyte < slen) { nbyte += MB_CPTR2LEN(p + nbyte); nchar--; } @@ -17071,9 +16421,10 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { nbyte = nchar; } } + int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - charlen = get_tv_number(&argvars[2]); - while (charlen > 0 && nbyte + len < slen) { + int charlen = get_tv_number(&argvars[2]); + while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; if (off < 0) { @@ -17092,12 +16443,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (nbyte < 0) { len += nbyte; nbyte = 0; - } else if (nbyte > slen) { + } else if ((size_t)nbyte > slen) { nbyte = slen; } if (len < 0) { len = 0; - } else if (nbyte + len > slen) { + } else if (nbyte + len > (int)slen) { len = slen - nbyte; } @@ -17114,7 +16465,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int len; int slen; - int error = FALSE; + bool error = false; p = get_tv_string(&argvars[0]); slen = (int)STRLEN(p); @@ -17205,7 +16556,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = FALSE; + bool error = false; int no = (int)get_tv_number_chk(&argvars[0], &error); if (error) { return; @@ -17270,7 +16621,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) long lnum; long col; int trans; - int transerr = FALSE; + bool transerr = false; lnum = get_tv_lnum(argvars); /* -1 on type error */ col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ @@ -17396,7 +16747,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); @@ -17417,10 +16768,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); + tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen - list_append_string(rettv->vval.v_list, str, -1); - list_append_number(rettv->vval.v_list, matchid); + tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); + tv_list_append_number(rettv->vval.v_list, matchid); } /* @@ -17441,43 +16792,24 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum))) { - rettv_list_alloc(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); + tv_list_alloc_ret(rettv); + (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); int id; int i = 0; while ((id = syn_get_stack_item(i++)) >= 0) { - list_append_number(rettv->vval.v_list, id); + tv_list_append_number(rettv->vval.v_list, id); } } } -static list_T* string_to_list(char_u *str, size_t len, bool keepempty) +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { - list_T *list = list_alloc(); - - // Copy each line to a list element using NL as the delimiter. - for (size_t i = 0; i < len; i++) { - char_u *start = str + i; - size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start; - i += line_len; - - // Don't use a str function to copy res as it may contains NULs. - char_u *s = xmemdupz(start, line_len); - memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation - - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(list, li); + if (!keepempty && str[len - 1] == NL) { + len--; } - - // Optionally retain final newline, if present - if (keepempty && str[len-1] == NL) { - list_append_string(list, (char_u*)"", 0); - } - + list_T *const list = tv_list_alloc(); + encode_list_write(list, str, len); return list; } @@ -17522,7 +16854,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -17534,7 +16866,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { keepempty = get_tv_number(&argvars[2]); } - rettv->vval.v_list = string_to_list((char_u *) res, nread, keepempty != 0); + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; rettv->v_type = VAR_LIST; @@ -17588,9 +16920,9 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) wp = (tp == curtab) ? firstwin : tp->tp_firstwin; } if (wp != NULL) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (wp != NULL) { - list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); + tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; } } @@ -17683,16 +17015,16 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; + char *fname; tagname_T tn; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); fname = xmalloc(MAXPATHL); - int first = TRUE; - while (get_tagfname(&tn, first, fname) == OK) { - list_append_string(rettv->vval.v_list, fname, -1); - first = FALSE; + bool first = true; + while (get_tagfname(&tn, first, (char_u *)fname) == OK) { + tv_list_append_string(rettv->vval.v_list, fname, -1); + first = false; } tagname_free(&tn); @@ -17712,7 +17044,7 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*tag_pattern == NUL) return; - (void)get_tags(rettv_list_alloc(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); } /* @@ -18029,7 +17361,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { // special case: timeout=0 means the callback will be @@ -18295,7 +17627,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); - list = list_alloc(); + list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); dict_add_list(dict, "entries", list); } @@ -18356,7 +17688,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_findbuf(argvars, rettv->vval.v_list); } @@ -18375,7 +17707,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_id2tabwin(argvars, rettv->vval.v_list); } @@ -18786,40 +18118,47 @@ var2fpos ( if (varp->v_type == VAR_LIST) { list_T *l; int len; - int error = FALSE; - listitem_T *li; + bool error = false; + listitem_T *li; l = varp->vval.v_list; if (l == NULL) return NULL; - /* Get the line number */ - pos.lnum = list_find_nr(l, 0L, &error); - if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) - return NULL; /* invalid line number */ + // Get the line number. + pos.lnum = tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. + return NULL; + } - /* Get the column number */ - pos.col = list_find_nr(l, 1L, &error); - if (error) + // Get the column number. + pos.col = tv_list_find_nr(l, 1L, &error); + if (error) { return NULL; + } len = (long)STRLEN(ml_get(pos.lnum)); - /* We accept "$" for the column number: last column. */ - li = list_find(l, 1L); + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); if (li != NULL && li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) + && STRCMP(li->li_tv.vval.v_string, "$") == 0) { pos.col = len + 1; + } - /* Accept a position up to the NUL after the line. */ - if (pos.col == 0 || (int)pos.col > len + 1) - return NULL; /* invalid column number */ - --pos.col; + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; - /* Get the virtual offset. Defaults to zero. */ - pos.coladd = list_find_nr(l, 2L, &error); - if (error) + // Get the virtual offset. Defaults to zero. + pos.coladd = tv_list_find_nr(l, 2L, &error); + if (error) { pos.coladd = 0; + } return &pos; } @@ -18890,32 +18229,37 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) return FAIL; if (fnump != NULL) { - n = list_find_nr(l, i++, NULL); /* fnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { return FAIL; - if (n == 0) - n = curbuf->b_fnum; /* current buffer */ + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } *fnump = n; } - n = list_find_nr(l, i++, NULL); /* lnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { return FAIL; + } posp->lnum = n; - n = list_find_nr(l, i++, NULL); /* col */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { return FAIL; + } posp->col = n; - n = list_find_nr(l, i, NULL); // off - if (n < 0) + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { posp->coladd = 0; - else + } else { posp->coladd = n; + } if (curswantp != NULL) { - *curswantp = list_find_nr(l, i + 1, NULL); // curswant + *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant } return OK; @@ -19259,7 +18603,7 @@ void set_vcount(long count, long count1, int set_prevcount) /// @param[in] val Value to set to. void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_NUMBER; vimvars[idx].vv_nr = val; } @@ -19270,7 +18614,7 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) /// @param[in] val Value to set to. void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_SPECIAL; vimvars[idx].vv_special = val; } @@ -19284,7 +18628,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_STRING; if (val == NULL) { vimvars[idx].vv_str = NULL; @@ -19301,7 +18645,7 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, /// @param[in,out] val Value to set to. Reference count will be incremented. void set_vim_var_list(const VimVarIndex idx, list_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { @@ -19316,7 +18660,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) /// Also keys of the dictionary will be made read-only. void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; @@ -19536,17 +18880,19 @@ handle_subscript( curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, pt, selfdict); - /* Clear the funcref afterwards, so that deleting it while - * evaluating the arguments is possible (see test55). */ - if (evaluate) - clear_tv(&functv); + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } /* Stop the expression evaluation when immediately aborting on * error, or when an interrupt occurred or an exception was thrown * but not caught. */ if (aborting()) { - if (ret == OK) - clear_tv(rettv); + if (ret == OK) { + tv_clear(rettv); + } ret = FAIL; } dict_unref(selfdict); @@ -19560,7 +18906,7 @@ handle_subscript( } else selfdict = NULL; if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } } @@ -19667,7 +19013,7 @@ void free_tv(typval_T *varp) partial_unref(varp->vval.v_partial); break; case VAR_LIST: - list_unref(varp->vval.v_list); + tv_list_unref(varp->vval.v_list); break; case VAR_DICT: dict_unref(varp->vval.v_dict); @@ -19682,247 +19028,6 @@ void free_tv(typval_T *varp) } } -#define TYPVAL_ENCODE_ALLOW_SPECIALS false - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - do { \ - tv->vval.v_special = kSpecialVarFalse; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - TYPVAL_ENCODE_CONV_NIL(tv) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - do { \ - (void)num; \ - tv->vval.v_number = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - do { \ - tv->vval.v_float = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ - do { \ - xfree(buf); \ - tv->vval.v_string = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) - -static inline int _nothing_conv_func_start(typval_T *const tv, - char_u *const fun) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) -{ - tv->v_lock = VAR_UNLOCKED; - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt_ = tv->vval.v_partial; - if (pt_ != NULL && pt_->pt_refcount > 1) { - pt_->pt_refcount--; - tv->vval.v_partial = NULL; - return OK; - } - } else { - func_unref(fun); - if (fun != empty_string) { - xfree(fun); - } - tv->vval.v_string = NULL; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ - return OK; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) - -static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt = tv->vval.v_partial; - if (pt == NULL) { - return; - } - // Dictionary should already be freed by the time. - // If it was not freed then it is a part of the reference cycle. - assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); - pt->pt_dict = NULL; - // As well as all arguments. - pt->pt_argc = 0; - assert(pt->pt_refcount <= 1); - partial_unref(pt); - tv->vval.v_partial = NULL; - assert(tv->v_lock == VAR_UNLOCKED); - } -} -#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) - -#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - do { \ - list_unref(tv->vval.v_list); \ - tv->vval.v_list = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - do { \ - assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ - } while (0) - -static inline int _nothing_conv_real_list_after_start( - typval_T *const tv, MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - assert(tv != NULL); - tv->v_lock = VAR_UNLOCKED; - if (tv->vval.v_list->lv_refcount > 1) { - tv->vval.v_list->lv_refcount--; - tv->vval.v_list = NULL; - mpsv->data.l.li = NULL; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) - -#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ - do { \ - if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) - -static inline void _nothing_conv_list_end(typval_T *const tv) - FUNC_ATTR_ALWAYS_INLINE -{ - if (tv == NULL) { - return; - } - assert(tv->v_type == VAR_LIST); - list_T *const list = tv->vval.v_list; - list_unref(list); - tv->vval.v_list = NULL; -} -#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) - -static inline int _nothing_conv_real_dict_after_start( - typval_T *const tv, dict_T **const dictp, const void *const nodictvar, - MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv != NULL) { - tv->v_lock = VAR_UNLOCKED; - } - if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { - (*dictp)->dv_refcount--; - *dictp = NULL; - mpsv->data.d.todo = 0; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) - -#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ - do { \ - if (_nothing_conv_real_dict_after_start( \ - tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ - &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) - -static inline void _nothing_conv_dict_end(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) - FUNC_ATTR_ALWAYS_INLINE -{ - if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); - *dictp = NULL; - } -} -#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - _nothing_conv_dict_end(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) - -#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) - -#define TYPVAL_ENCODE_SCOPE static -#define TYPVAL_ENCODE_NAME nothing -#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const -#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored -#include "nvim/eval/typval_encode.c.h" -#undef TYPVAL_ENCODE_SCOPE -#undef TYPVAL_ENCODE_NAME -#undef TYPVAL_ENCODE_FIRST_ARG_TYPE -#undef TYPVAL_ENCODE_FIRST_ARG_NAME - -#undef TYPVAL_ENCODE_ALLOW_SPECIALS -#undef TYPVAL_ENCODE_CONV_NIL -#undef TYPVAL_ENCODE_CONV_BOOL -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#undef TYPVAL_ENCODE_CONV_STRING -#undef TYPVAL_ENCODE_CONV_STR_STRING -#undef TYPVAL_ENCODE_CONV_EXT_STRING -#undef TYPVAL_ENCODE_CONV_FUNC_START -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF -#undef TYPVAL_ENCODE_CONV_FUNC_END -#undef TYPVAL_ENCODE_CONV_EMPTY_LIST -#undef TYPVAL_ENCODE_CONV_EMPTY_DICT -#undef TYPVAL_ENCODE_CONV_LIST_START -#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_DICT_START -#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START -#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY -#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_DICT_END -#undef TYPVAL_ENCODE_CONV_RECURSE - -/// Free memory for a variable value and set the value to NULL or 0 -/// -/// @param[in,out] varp Value to free. -void clear_tv(typval_T *varp) -{ - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument"); - (void)evn_ret; - assert(evn_ret == OK); - } -} - /* * Set the value of a variable to NULL without freeing items. */ @@ -19932,69 +19037,25 @@ static void init_tv(typval_T *varp) memset(varp, 0, sizeof(typval_T)); } -/// Check that given value is a number or string +// TODO(ZyX-I): move to eval/typval + +/// Get the number value of a variable /// -/// Error messages are compatible with get_tv_number() previously used for the -/// same purpose in buf*() functions. Special values are not accepted (previous -/// behaviour: silently fail to find buffer). +/// @note Use get_tv_number_chk() if you need to determine whether there was an +/// error. /// -/// @param[in] tv Value to check. +/// @param[in] varp Variable to get value from. /// -/// @return true if everything is OK, false otherwise. -bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE -{ - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } - } - assert(false); - return false; -} - -/* - * Get the number value of a variable. - * If it is a String variable, uses vim_str2nr(). - * For incompatible types, return 0. - * get_tv_number_chk() is similar to get_tv_number(), but informs the - * caller of incompatible types: it sets *denote to TRUE if "denote" - * is not NULL or returns -1 otherwise. - */ -long get_tv_number(typval_T *varp) +/// @return Number value: vim_str2nr() output for VAR_STRING variables, value +/// for VAR_NUMBER variables, -1 for other types. +varnumber_T get_tv_number(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - int error = FALSE; - - return get_tv_number_chk(varp, &error); /* return 0L on error */ + bool error = false; + return get_tv_number_chk(varp, &error); } -long get_tv_number_chk(typval_T *varp, int *denote) +varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { long n = 0L; @@ -20087,7 +19148,7 @@ static linenr_T get_tv_lnum(typval_T *argvars) rettv.v_type = VAR_NUMBER; f_line(argvars, &rettv, NULL); lnum = rettv.vval.v_number; - clear_tv(&rettv); + tv_clear(&rettv); } return lnum; } @@ -20107,35 +19168,41 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) return get_tv_number_chk(&argvars[0], NULL); } -/* - * Get the string value of a variable. - * If it is a Number variable, the number is converted into a string. - * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! - * get_tv_string_buf() uses a given buffer. - * If the String variable has never been set, return an empty string. - * Never returns NULL; - * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return - * NULL on error. - */ -static char_u *get_tv_string(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +// TODO(ZyX-I): move to eval/typval + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use get_tv_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +char_u *get_tv_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); } -static char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *res = get_tv_string_buf_chk(varp, buf); + char_u *const res = get_tv_string_buf_chk(varp, buf); return res != NULL ? res : (char_u *)""; } /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; @@ -20143,7 +19210,7 @@ char_u *get_tv_string_chk(const typval_T *varp) } char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (varp->v_type) { case VAR_NUMBER: @@ -20438,7 +19505,7 @@ void new_script_vars(scid_T id) * Initialize dictionary "dict" as a scope and set variable "dict_var" to * point to it. */ -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); dict->dv_lock = VAR_UNLOCKED; @@ -20494,7 +19561,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // later. v = HI2DI(hi); if (free_val) { - clear_tv(&v->di_tv); + tv_clear(&v->di_tv); } if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); @@ -20514,7 +19581,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) dictitem_T *di = HI2DI(hi); hash_remove(ht, hi); - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); xfree(di); } @@ -20652,11 +19719,11 @@ set_var ( if (watched) { copy_tv(&v->di_tv, &oldtv); } - clear_tv(&v->di_tv); - } else { /* add a new variable */ - /* Can't add "v:" variable. */ + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" variable. if (ht == &vimvarht) { - EMSG2(_(e_illvar), name); + emsgf(_(e_illvar), name); return; } @@ -20686,7 +19753,7 @@ set_var ( dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -20934,7 +20001,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_list = from->vval.v_list->lv_copylist; ++to->vval.v_list->lv_refcount; } else { - to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID); + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } if (to->vval.v_list == NULL) ret = FAIL; @@ -21036,7 +20103,7 @@ void ex_echo(exarg_T *eap) } xfree(tofree); } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); @@ -21111,7 +20178,7 @@ void ex_execute(exarg_T *eap) ga.ga_len += len; } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } @@ -21670,9 +20737,10 @@ void ex_function(exarg_T *eap) xfree(fp); goto erret; } - } else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); + } else { + // Overwrite existing dict entry. + tv_clear(&fudi.fd_di->di_tv); + } fudi.fd_di->di_tv.v_type = VAR_FUNC; fudi.fd_di->di_tv.v_lock = 0; fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); @@ -21733,7 +20801,7 @@ ret_free: /// Advances "pp" to just after the function name (if no error). /// /// @return the function name in allocated memory, or NULL for failure. -char_u * +static char_u * trans_function_name( char_u **pp, int skip, /* only find the end, don't evaluate */ @@ -22720,9 +21788,9 @@ call_user_func( // Init l: variables. init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); if (selfdict != NULL) { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + // Set l:self to "selfdict". Use "name" to avoid a warning from + // some compiler that checks the destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "self"); @@ -22741,11 +21809,11 @@ call_user_func( * Set a:000 to a list with room for the "..." arguments. */ init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + // Use "name" to avoid a warning from some compiler that checks the + // destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "000"); @@ -22762,10 +21830,10 @@ call_user_func( // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. // Set a:N to the "..." arguments. - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "firstline", (varnumber_T)firstline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "lastline", (varnumber_T)lastline); for (int i = 0; i < argcount; i++) { bool addlocal = false; @@ -22782,7 +21850,7 @@ call_user_func( name = numbuf; } if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { - v = &fc->fixvar[fixvar_idx++].var; + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); @@ -22805,7 +21873,7 @@ call_user_func( } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); + tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); fc->l_listitems[ai].li_tv = argvars[i]; fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; } @@ -22913,7 +21981,7 @@ call_user_func( // when the function was aborted because of an error, return -1 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; } @@ -23109,7 +22177,7 @@ free_funccal ( // Free the a:000 variables if they were allocated. if (free_val) { for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - clear_tv(&li->li_tv); + tv_clear(&li->li_tv); } } @@ -23152,10 +22220,11 @@ void ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); + if (!eap->skip) { + returning = do_return(eap, false, true, &rettv); + } else { + tv_clear(&rettv); + } } /* It's safer to return also on error. */ else if (!eap->skip) { @@ -23242,7 +22311,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) * a return immediately after reanimation, the value is already * there. */ if (!reanimate && rettv != NULL) { - clear_tv(current_funccal->rettv); + tv_clear(current_funccal->rettv); *current_funccal->rettv = *(typval_T *)rettv; if (!is_cmd) xfree(rettv); @@ -23652,12 +22721,12 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char_u *p = list_find_str(l, nr); + char *p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = expand_env_save(p); - eap->arg = p; + p = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)p; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); xfree(p); @@ -24148,7 +23217,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, JobEvent event_data; event_data.received = NULL; if (buf) { - event_data.received = list_alloc(); + event_data.received = tv_list_alloc(); char *ptr = buf; size_t remaining = count; size_t off = 0; @@ -24156,7 +23225,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, while (off < remaining) { // append the line if (ptr[off] == NL) { - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); size_t skip = off + 1; ptr += skip; remaining -= skip; @@ -24169,7 +23238,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, } off++; } - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); } else { event_data.status = status; } @@ -24318,7 +23387,7 @@ static void on_job_event(JobEvent *ev) typval_T rettv; init_tv(&rettv); callback_call(ev->callback, 3, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); } static TerminalJobData *find_job(uint64_t id) @@ -24341,8 +23410,8 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = list_alloc(); - list_append_string(args, argvars[0].vval.v_string, -1); + list_T *args = tv_list_alloc(); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } @@ -24387,7 +23456,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) NULL, NULL); - list_unref(arguments); + tv_list_unref(arguments); // Restore caller scope information restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; @@ -24479,12 +23548,12 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, watcher->busy = true; callback_call(&watcher->callback, 3, argv, &rettv); watcher->busy = false; - clear_tv(&rettv); + tv_clear(&rettv); } } for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - clear_tv(argv + i); + tv_clear(argv + i); } } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..1d30f51f55 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -2,7 +2,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" @@ -118,18 +118,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, if (last_container.container.vval.v_list->lv_len != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = listitem_alloc(); + listitem_T *obj_li = tv_list_item_alloc(); obj_li->li_tv = obj.val; - list_append(last_container.container.vval.v_list, obj_li); + tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } ValuesStackItem key = kv_pop(*stack); @@ -139,33 +139,33 @@ static inline int json_decoder_pop(ValuesStackItem obj, || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); - clear_tv(&key.val); + tv_clear(&key.val); if (dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = list_alloc(); - list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv = key.val; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv = obj.val; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); } } else { // Object with key only if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { EMSG2(_("E474: Expected string key: %s"), *pp); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } else if (!obj.didcomma && (last_container.special_val == NULL && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } // Handle empty key and key represented as special dictionary @@ -175,14 +175,14 @@ static inline int json_decoder_pop(ValuesStackItem obj, || *obj.val.vval.v_string == NUL || dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { - clear_tv(&obj.val); + tv_clear(&obj.val); // Restart (void) kv_pop(*container_stack); ValuesStackItem last_container_val = kv_A(*stack, last_container.stack_index); while (kv_size(*stack) > last_container.stack_index) { - clear_tv(&(kv_pop(*stack).val)); + tv_clear(&(kv_pop(*stack).val)); } *pp = last_container.s; *didcomma = last_container_val.didcomma; @@ -430,7 +430,7 @@ static inline int parse_json_string(vimconv_T *const conv, } if (hasnul) { typval_T obj; - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(&obj, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -439,7 +439,7 @@ static inline int parse_json_string(vimconv_T *const conv, })); if (encode_list_write((void *) list, str, (size_t) (str_end - str)) == -1) { - clear_tv(&obj); + tv_clear(&obj); goto parse_json_string_fail; } xfree(str); @@ -806,7 +806,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); list->lv_refcount++; typval_T tv = { .v_type = VAR_LIST, @@ -827,7 +827,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); + val_list = tv_list_alloc(); val_list->lv_refcount++; create_special_dict(&tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -887,7 +887,7 @@ json_decode_string_after_cycle: json_decode_string_fail: ret = FAIL; while (kv_size(stack)) { - clear_tv(&(kv_pop(stack).val)); + tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: kv_destroy(stack); @@ -933,7 +933,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -941,10 +941,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, })); uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + tv_list_append_number(list, 1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -956,18 +956,18 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + uint64_t n = -((uint64_t)mobj.via.i64); + tv_list_append_number(list, -1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -980,7 +980,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -1002,7 +1002,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; break; } - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPBinary, ((typval_T) { .v_type = VAR_LIST, @@ -1016,7 +1016,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1024,9 +1024,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); + listitem_T *const li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); + tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { return FAIL; } @@ -1057,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) di->di_tv.v_type = VAR_UNKNOWN; if (dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map - clear_tv(rettv); + tv_clear(rettv); xfree(di); goto msgpack_to_vim_generic_map; } @@ -1067,7 +1067,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -1075,14 +1075,14 @@ msgpack_to_vim_generic_map: {} .vval = { .v_list = list }, })); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(list, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { return FAIL; } @@ -1093,11 +1093,11 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); + tv_list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = tv_list_alloc(); + tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h index 5c25a64f7a..c8e7a189e3 100644 --- a/src/nvim/eval/decode.h +++ b/src/nvim/eval/decode.h @@ -5,7 +5,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/decode.h.generated.h" diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ee66b7cf09..1416806ca6 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -13,7 +13,7 @@ #include "nvim/eval/encode.h" #include "nvim/buffer_defs.h" // vimconv_T #include "nvim/eval.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/garray.h" #include "nvim/mbyte.h" #include "nvim/message.h" @@ -45,7 +45,8 @@ const char *const encode_special_var_names[] = { #endif /// Msgpack callback for writing to readfile()-style list -int encode_list_write(void *data, const char *buf, size_t len) +int encode_list_write(void *const data, const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ARG(1) { if (len == 0) { return 0; @@ -80,11 +81,11 @@ int encode_list_write(void *data, const char *buf, size_t len) str = xmemdupz(line_start, line_length); memchrsub(str, NUL, NL, line_length); } - list_append_allocated_string(list, str); + tv_list_append_allocated_string(list, str); line_end++; } if (line_end == end) { - list_append_allocated_string(list, NULL); + tv_list_append_allocated_string(list, NULL); } return 0; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c new file mode 100644 index 0000000000..ab48ace400 --- /dev/null +++ b/src/nvim/eval/executor.c @@ -0,0 +1,114 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval.h" +#include "nvim/message.h" +#include "nvim/vim.h" +#include "nvim/globals.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.c.generated.h" +#endif + +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + +char *e_listidx = N_("E684: list index out of range: %" PRId64); + +/// Hanle tv1 += tv2, -=, .= +/// +/// @param[in,out] tv1 First operand, modified typval. +/// @param[in] tv2 Second operand. +/// @param[in] op Used operator. +/// +/// @return OK or FAIL. +int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, + const char *const op) + FUNC_ATTR_NONNULL_ALL +{ + // Can't do anything with a Funcref, a Dict or special value on the right. + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { + switch (tv1->v_type) { + case VAR_DICT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_LIST: { + if (*op != '+' || tv2->v_type != VAR_LIST) { + break; + } + // List += List + if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { + tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); + } + return OK; + } + case VAR_NUMBER: + case VAR_STRING: { + if (tv2->v_type == VAR_LIST) { + break; + } + if (*op == '+' || *op == '-') { + // nr += nr or nr -= nr + varnumber_T n = get_tv_number(tv1); + if (tv2->v_type == VAR_FLOAT) { + float_T f = n; + + if (*op == '+') { + f += tv2->vval.v_float; + } else { + f -= tv2->vval.v_float; + } + tv_clear(tv1); + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f; + } else { + if (*op == '+') { + n += get_tv_number(tv2); + } else { + n -= get_tv_number(tv2); + } + tv_clear(tv1); + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n; + } + } else { + // str .= str + if (tv2->v_type == VAR_FLOAT) { + break; + } + char *s = (char *)get_tv_string(tv1); + char numbuf[NUMBUFLEN]; + s = (char *)concat_str((char_u *)s, + get_tv_string_buf(tv2, (char_u *)numbuf)); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = (char_u *)s; + } + return OK; + } + case VAR_FLOAT: { + if (*op == '.' || (tv2->v_type != VAR_FLOAT + && tv2->v_type != VAR_NUMBER + && tv2->v_type != VAR_STRING)) { + break; + } + const float_T f = (tv2->v_type == VAR_FLOAT + ? tv2->vval.v_float + : get_tv_number(tv2)); + if (*op == '+') { + tv1->vval.v_float += f; + } else { + tv1->vval.v_float -= f; + } + return OK; + } + case VAR_UNKNOWN: { + assert(false); + } + } + } + + EMSG2(_(e_letwrong), op); + return FAIL; +} diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h new file mode 100644 index 0000000000..19e2a75914 --- /dev/null +++ b/src/nvim/eval/executor.h @@ -0,0 +1,9 @@ +#ifndef NVIM_EVAL_EXECUTOR_H +#define NVIM_EVAL_EXECUTOR_H + +extern char *e_listidx; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.h.generated.h" +#endif +#endif // NVIM_EVAL_EXECUTOR_H diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c new file mode 100644 index 0000000000..5ce52ddd70 --- /dev/null +++ b/src/nvim/eval/gc.c @@ -0,0 +1,11 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.c.generated.h" +#endif + +/// Head of list of all dictionaries +dict_T *gc_first_dict = NULL; +/// Head of list of all lists +list_T *gc_first_list = NULL; diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h new file mode 100644 index 0000000000..c2e862e469 --- /dev/null +++ b/src/nvim/eval/gc.h @@ -0,0 +1,12 @@ +#ifndef NVIM_EVAL_GC_H +#define NVIM_EVAL_GC_H + +#include "nvim/eval/typval.h" + +extern dict_T *gc_first_dict; +extern list_T *gc_first_list; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.h.generated.h" +#endif +#endif // NVIM_EVAL_GC_H diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c new file mode 100644 index 0000000000..7726e106a1 --- /dev/null +++ b/src/nvim/eval/typval.c @@ -0,0 +1,1171 @@ +#include +#include +#include + +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval_encode.h" +#include "nvim/eval.h" +#include "nvim/types.h" +#include "nvim/assert.h" +#include "nvim/memory.h" +#include "nvim/globals.h" +// TODO(ZyX-I): Move line_breakcheck out of misc1 +#include "nvim/misc1.h" // For line_breakcheck + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.c.generated.h" +#endif + +bool tv_in_free_unref_items = false; + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 + +const char *const tv_empty_string = ""; + +//{{{1 Lists +//{{{2 List item + +/// Allocate a list item +/// +/// @warning Allocated item is not initialized, do not forget to initialize it +/// and specifically set lv_lock. +/// +/// @return [allocated] new list item. +listitem_T *tv_list_item_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + return xmalloc(sizeof(listitem_T)); +} + +/// Free a list item +/// +/// Also clears the value. Does not touch watchers. +/// +/// @param[out] item Item to free. +void tv_list_item_free(listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->li_tv); + xfree(item); +} + +/// Remove a list item from a List and free it +/// +/// Also clears the value. +/// +/// @param[out] l List to remove item from. +/// @param[in,out] item Item to remove. +void tv_list_item_remove(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item); + tv_list_item_free(item); +} + +//{{{2 List watchers + +/// Add a watcher to a list +/// +/// @param[out] l List to add watcher to. +/// @param[in] lw Watcher to add. +void tv_list_watch_add(list_T *const l, listwatch_T *const lw) + FUNC_ATTR_NONNULL_ALL +{ + lw->lw_next = l->lv_watch; + l->lv_watch = lw; +} + +/// Remove a watcher from a list +/// +/// Does not give a warning if watcher was not found. +/// +/// @param[out] l List to remove watcher from. +/// @param[in] lwrem Watcher to remove. +void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem) + FUNC_ATTR_NONNULL_ALL +{ + listwatch_T **lwp = &l->lv_watch; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw == lwrem) { + *lwp = lw->lw_next; + break; + } + lwp = &lw->lw_next; + } +} + +/// Advance watchers to the next item +/// +/// Used just before removing an item from a list. +/// +/// @param[out] l List from which item is removed. +/// @param[in] item List item being removed. +void tv_list_watch_fix(list_T *const l, const listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw->lw_item == item) { + lw->lw_item = item->li_next; + } + } +} + +//{{{2 Lists +//{{{3 Alloc/free + +/// Allocate an empty list +/// +/// Caller should take care of the reference count. +/// +/// @return [allocated] new list. +list_T *tv_list_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + list_T *const list = xcalloc(1, sizeof(list_T)); + + // Prepend the list to the list of lists for garbage collection. + if (gc_first_list != NULL) { + gc_first_list->lv_used_prev = list; + } + list->lv_used_prev = NULL; + list->lv_used_next = gc_first_list; + gc_first_list = list; + return list; +} + +/// Free items contained in a list +/// +/// @param[in,out] l List to clear. +void tv_list_free_contents(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { + // Remove the item before deleting it. + l->lv_first = item->li_next; + tv_clear(&item->li_tv); + xfree(item); + } + l->lv_len = 0; + l->lv_idx_item = NULL; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + lw->lw_item = NULL; + } +} + +/// Free a list itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] l List to free. +void tv_list_free_list(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the list from the list of lists for garbage collection. + if (l->lv_used_prev == NULL) { + gc_first_list = l->lv_used_next; + } else { + l->lv_used_prev->lv_used_next = l->lv_used_next; + } + if (l->lv_used_next != NULL) { + l->lv_used_next->lv_used_prev = l->lv_used_prev; + } + + xfree(l); +} + +/// Free a list, including all items it points to +/// +/// Ignores the reference count. Does not do anything if +/// tv_in_free_unref_items is true. +/// +/// @param[in,out] l List to free. +void tv_list_free(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_list_free_contents(l); + tv_list_free_list(l); + } +} + +/// Unreference a list +/// +/// Decrements the reference count and frees when it becomes zero or less. +/// +/// @param[in,out] l List to unreference. +void tv_list_unref(list_T *const l) +{ + if (l != NULL && --l->lv_refcount <= 0) { + tv_list_free(l); + } +} + +//{{{3 Add/remove + +/// Remove items "item" to "item2" from list "l". +/// +/// @warning Does not free the listitem or the value! +/// +/// @param[out] l List to remove from. +/// @param[in] item First item to remove. +/// @param[in] item2 Last item to remove. +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) +{ + // notify watchers + for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + l->lv_len--; + tv_list_watch_fix(l, ip); + if (ip == item2) { + break; + } + } + + if (item2->li_next == NULL) { + l->lv_last = item->li_prev; + } else { + item2->li_next->li_prev = item->li_prev; + } + if (item->li_prev == NULL) { + l->lv_first = item2->li_next; + } else { + item->li_prev->li_next = item2->li_next; + } + l->lv_idx_item = NULL; +} + +/// Insert list item +/// +/// @param[out] l List to insert to. +/// @param[in,out] ni Item to insert. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert(list_T *const l, listitem_T *const ni, + listitem_T *const item) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + if (item == NULL) { + // Append new item at end of list. + tv_list_append(l, ni); + } else { + // Insert new item before existing item. + ni->li_prev = item->li_prev; + ni->li_next = item; + if (item->li_prev == NULL) { + l->lv_first = ni; + l->lv_idx++; + } else { + item->li_prev->li_next = ni; + l->lv_idx_item = NULL; + } + item->li_prev = ni; + l->lv_len++; + } +} + +/// Insert VimL value into a list +/// +/// @param[out] l List to insert to. +/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// allocated listitem_T and inserted. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert_tv(list_T *const l, typval_T *const tv, + listitem_T *const item) +{ + listitem_T *const ni = tv_list_item_alloc(); + + copy_tv(tv, &ni->li_tv); + tv_list_insert(l, ni, item); +} + +/// Append item to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] item Item to append. +void tv_list_append(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_last == NULL) { + // empty list + l->lv_first = item; + l->lv_last = item; + item->li_prev = NULL; + } else { + l->lv_last->li_next = item; + item->li_prev = l->lv_last; + l->lv_last = item; + } + l->lv_len++; + item->li_next = NULL; +} + +/// Append VimL value to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// allocated listitem_T. +void tv_list_append_tv(list_T *const l, typval_T *const tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + copy_tv(tv, &li->li_tv); + tv_list_append(l, li); +} + +/// Append a list to a list as one item +/// +/// @param[out] l List to append to. +/// @param[in,out] itemlist List to append. Reference count is increased. +void tv_list_append_list(list_T *const list, list_T *const itemlist) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = itemlist; + tv_list_append(list, li); + if (itemlist != NULL) { + itemlist->lv_refcount++; + } +} + +/// Append a dictionary to a list +/// +/// @param[out] l List to append to. +/// @param[in,out] dict Dictionary to append. Reference count is increased. +void tv_list_append_dict(list_T *const list, dict_T *const dict) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_DICT; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_dict = dict; + tv_list_append(list, li); + if (dict != NULL) { + dict->dv_refcount++; + } +} + +/// Make a copy of "str" and append it as an item to list "l" +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +/// @param[in] len Length of the appended string. May be -1, in this +/// case string is considered to be usual zero-terminated +/// string or NULL “empty” string. +void tv_list_append_string(list_T *const l, const char *const str, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (str == NULL) { + assert(len == 0 || len == -1); + tv_list_append_allocated_string(l, NULL); + } else { + tv_list_append_allocated_string(l, (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))); + } +} + +/// Append given string to the list +/// +/// Unlike list_append_string this function does not copy the string. +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +void tv_list_append_allocated_string(list_T *const l, char *const str) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + tv_list_append(l, li); + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = (char_u *)str; +} + +/// Append number to the list +/// +/// @param[out] l List to append to. +/// @param[in] n Number to append. Will be recorded in the allocated +/// listitem_T. +void tv_list_append_number(list_T *const l, const varnumber_T n) +{ + listitem_T *const li = tv_list_item_alloc(); + li->li_tv.v_type = VAR_NUMBER; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_number = n; + tv_list_append(l, li); +} + +//{{{3 Operations on the whole list + +/// Make a copy of list +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original list to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied list. May be NULL in case original list is NULL or some +/// failure happens. The refcount of the new list is set to 1. +list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, + const bool deep, const int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (orig == NULL) { + return NULL; + } + + list_T *copy = tv_list_alloc(); + if (copyID != 0) { + // Do this before adding the items, because one of the items may + // refer back to this list. + orig->lv_copyID = copyID; + orig->lv_copylist = copy; + } + listitem_T *item; + for (item = orig->lv_first; item != NULL && !got_int; + item = item->li_next) { + listitem_T *const ni = tv_list_item_alloc(); + if (deep) { + if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + xfree(ni); + break; + } + } else { + copy_tv(&item->li_tv, &ni->li_tv); + } + tv_list_append(copy, ni); + } + copy->lv_refcount++; + if (item != NULL) { + tv_list_unref(copy); + copy = NULL; + } + + return copy; +} + +/// Extend first list with the second +/// +/// @param[out] l1 List to extend. +/// @param[in] l2 List to extend with. +/// @param[in] bef If not NULL, extends before this item. +void tv_list_extend(list_T *const l1, list_T *const l2, + listitem_T *const bef) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + int todo = l2->lv_len; + // We also quit the loop when we have inserted the original item count of + // the list, avoid a hang when we extend a list with itself. + for (listitem_T *item = l2->lv_first + ; item != NULL && --todo >= 0 + ; item = item->li_next) { + tv_list_insert_tv(l1, &item->li_tv, bef); + } +} + +/// Concatenate lists into a new list +/// +/// @param[in] l1 First list. +/// @param[in] l2 Second list. +/// @param[out] ret_tv Location where new list is saved. +/// +/// @return OK or FAIL. +int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + return FAIL; + } + + // make a copy of the first list. + list_T *const l = tv_list_copy(NULL, l1, false, 0); + if (l == NULL) { + return FAIL; + } + tv->v_type = VAR_LIST; + tv->vval.v_list = l; + + // append all items from the second list + tv_list_extend(l, l2, NULL); + return OK; +} + +typedef struct { + char_u *s; + char_u *tofree; +} Join; + +/// Join list into a string, helper function +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l List to join. +/// @param[in] sep Used separator. +/// @param[in] join_gap Garray to keep each list item string. +/// +/// @return OK in case of success, FAIL otherwise. +static int list_join_inner(garray_T *const gap, list_T *const l, + const char *const sep, garray_T *const join_gap) + FUNC_ATTR_NONNULL_ALL +{ + size_t sumlen = 0; + bool first = true; + listitem_T *item; + + // Stringify each item in the list. + for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + char *s; + size_t len; + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { + return FAIL; + } + + sumlen += len; + + Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); + p->tofree = p->s = (char_u *)s; + + line_breakcheck(); + } + + // Allocate result buffer with its total size, avoid re-allocation and + // multiple copy operations. Add 2 for a tailing ']' and NUL. + if (join_gap->ga_len >= 2) { + sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); + } + ga_grow(gap, (int)sumlen + 2); + + for (int i = 0; i < join_gap->ga_len && !got_int; i++) { + if (first) { + first = false; + } else { + ga_concat(gap, (const char_u *)sep); + } + const Join *const p = ((const Join *)join_gap->ga_data) + i; + + if (p->s != NULL) { + ga_concat(gap, p->s); + } + line_breakcheck(); + } + + return OK; +} + +/// Join list into a string using given separator +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l Joined list. +/// @param[in] sep Separator. +/// +/// @return OK in case of success, FAIL otherwise. +int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_len < 1) { + return OK; + } + + garray_T join_ga; + int retval; + + ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + retval = list_join_inner(gap, l, sep, &join_ga); + +#define FREE_JOIN_TOFREE(join) xfree((join)->tofree) + GA_DEEP_CLEAR(&join_ga, Join, FREE_JOIN_TOFREE); +#undef FREE_JOIN_TOFREE + + return retval; +} + +/// Chech whether two lists are equal +/// +/// @param[in] l1 First list to compare. +/// @param[in] l2 Second list to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + // FIXME? compare empty list with NULL list equal + return false; + } + if (l1 == l2) { + return true; + } + if (tv_list_len(l1) != tv_list_len(l2)) { + return false; + } + + listitem_T *item1 = l1->lv_first; + listitem_T *item2 = l2->lv_first; + for (; item1 != NULL && item2 != NULL + ; item1 = item1->li_next, item2 = item2->li_next) { + if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + return false; + } + } + assert(item1 == NULL && item2 == NULL); + return true; +} + +//{{{3 Indexing/searching + +/// Locate item with a given index in a list and return it +/// +/// @param[in] l List to index. +/// @param[in] n Index. Negative index is counted from the end, -1 is the last +/// item. +/// +/// @return Item at the given index or NULL if `n` is out of range. +listitem_T *tv_list_find(list_T *const l, int n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + STATIC_ASSERT(sizeof(n) == sizeof(l->lv_idx), + "n and lv_idx sizes do not match"); + if (l == NULL) { + return NULL; + } + + // Negative index is relative to the end. + if (n < 0) { + n = l->lv_len + n; + } + + // Check for index out of range. + if (n < 0 || n >= l->lv_len) { + return NULL; + } + + int idx; + listitem_T *item; + + // When there is a cached index may start search from there. + if (l->lv_idx_item != NULL) { + if (n < l->lv_idx / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else if (n > (l->lv_idx + l->lv_len) / 2) { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } else { + // Closest to the cached index. + item = l->lv_idx_item; + idx = l->lv_idx; + } + } else { + if (n < l->lv_len / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } + } + + while (n > idx) { + // Search forward. + item = item->li_next; + idx++; + } + while (n < idx) { + // Search backward. + item = item->li_prev; + idx--; + } + + assert(idx == n); + // Cache the used index. + l->lv_idx = idx; + l->lv_idx_item = item; + + return item; +} + +/// Get list item l[n] as a number +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// @param[out] ret_error Location where 1 will be saved if index was not +/// found. May be NULL. If everything is OK, +/// `*ret_error` is not touched. +/// +/// @return Integer value at the given index or -1. +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const listitem_T *const li = tv_list_find(l, n); + if (li == NULL) { + if (ret_error != NULL) { + *ret_error = true; + } + return -1; + } + return get_tv_number_chk(&li->li_tv, ret_error); +} + +/// Get list item l[n - 1] as a string +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// +/// @return [allocated] Copy of the list item string value. +char *tv_list_find_str(list_T *l, int n) + FUNC_ATTR_MALLOC +{ + const listitem_T *const li = tv_list_find(l, n - 1); + if (li == NULL) { + EMSGN(_(e_listidx), n); + return NULL; + } + return (char *)get_tv_string(&li->li_tv); +} + +/// Locate item in a list and return its index +/// +/// @param[in] l List to search. +/// @param[in] item Item to search for. +/// +/// @return Index of an item or -1 if item is not in the list. +long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (l == NULL) { + return -1; + } + long idx = 0; + listitem_T *li; + for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + idx++; + } + if (li == NULL) { + return -1; + } + return idx; +} +//{{{1 Generic typval operations +//{{{2 Init/alloc/clear +//{{{3 Alloc + +/// Allocate an empty list for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where list is saved. +/// +/// @return [allocated] pointer to the created list. +list_T *tv_list_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC +{ + list_T *const l = tv_list_alloc(); + ret_tv->vval.v_list = l; + ret_tv->v_type = VAR_LIST; + ret_tv->v_lock = VAR_UNLOCKED; + l->lv_refcount++; + return l; +} + +//{{{3 Clear +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + do { \ + tv->vval.v_special = kSpecialVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + do { \ + (void)num; \ + tv->vval.v_number = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + do { \ + tv->vval.v_float = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ + do { \ + xfree(buf); \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) + +static inline int _nothing_conv_func_start(typval_T *const tv, + char_u *const fun) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) +{ + tv->v_lock = VAR_UNLOCKED; + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt_ = tv->vval.v_partial; + if (pt_ != NULL && pt_->pt_refcount > 1) { + pt_->pt_refcount--; + tv->vval.v_partial = NULL; + return OK; + } + } else { + func_unref(fun); + if ((const char *)fun != tv_empty_string) { + xfree(fun); + } + tv->vval.v_string = NULL; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) + +static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt = tv->vval.v_partial; + if (pt == NULL) { + return; + } + // Dictionary should already be freed by the time. + // If it was not freed then it is a part of the reference cycle. + assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); + pt->pt_dict = NULL; + // As well as all arguments. + pt->pt_argc = 0; + assert(pt->pt_refcount <= 1); + partial_unref(pt); + tv->vval.v_partial = NULL; + assert(tv->v_lock == VAR_UNLOCKED); + } +} +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + do { \ + tv_list_unref(tv->vval.v_list); \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + do { \ + assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ + dict_unref((dict_T *)dict); \ + *((dict_T **)&dict) = NULL; \ + if (tv != NULL) { \ + ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ + } \ + } while (0) + +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(tv != NULL); + tv->v_lock = VAR_UNLOCKED; + if (tv->vval.v_list->lv_refcount > 1) { + tv->vval.v_list->lv_refcount--; + tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ + do { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) + +static inline void _nothing_conv_list_end(typval_T *const tv) + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv == NULL) { + return; + } + assert(tv->v_type == VAR_LIST); + list_T *const list = tv->vval.v_list; + tv_list_unref(list); + tv->vval.v_list = NULL; +} +#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) + +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } + if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { + (*dictp)->dv_refcount--; + *dictp = NULL; + mpsv->data.d.todo = 0; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ + do { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +static inline void _nothing_conv_dict_end(typval_T *const tv, + dict_T **const dictp, + const void *const nodictvar) + FUNC_ATTR_ALWAYS_INLINE +{ + if ((const void *)dictp != nodictvar) { + dict_unref(*dictp); + *dictp = NULL; + } +} +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + _nothing_conv_dict_end(tv, (dict_T **)&dict, \ + (void *)&TYPVAL_ENCODE_NODICT_VAR) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME nothing +#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const +#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored +#include "nvim/eval/typval_encode.c.h" +#undef TYPVAL_ENCODE_SCOPE +#undef TYPVAL_ENCODE_NAME +#undef TYPVAL_ENCODE_FIRST_ARG_TYPE +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_RECURSE + +/// Free memory for a variable value and set the value to NULL or 0 +/// +/// @param[in,out] varp Value to free. +void tv_clear(typval_T *varp) +{ + if (varp != NULL && varp->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + (void)evn_ret; + assert(evn_ret == OK); + } +} + +//{{{2 Locks + +/// Lock or unlock an item +/// +/// @param[out] tv Item to (un)lock. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// @param[in] lock True if it is needed to lock an item, false to unlock. +void tv_item_lock(typval_T *const tv, const int deep, const bool lock) +{ + // TODO(ZyX-I): Make this not recursive + static int recurse = 0; + + if (recurse >= DICT_MAXNEST) { + emsgf(_("E743: variable nested too deep for (un)lock")); + return; + } + if (deep == 0) { + return; + } + recurse++; + + // lock/unlock the item itself +#define CHANGE_LOCK(lock, var) \ + do { \ + var = ((VarLockStatus[]) { \ + [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_FIXED] = VAR_FIXED, \ + })[var]; \ + } while (0) + CHANGE_LOCK(lock, tv->v_lock); + + switch (tv->v_type) { + case VAR_LIST: { + list_T *const l = tv->vval.v_list; + if (l != NULL) { + CHANGE_LOCK(lock, l->lv_lock); + if (deep < 0 || deep > 1) { + // Recursive: lock/unlock the items the List contains. + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + tv_item_lock(&li->li_tv, deep - 1, lock); + } + } + } + break; + } + case VAR_DICT: { + dict_T *const d = tv->vval.v_dict; + if (d != NULL) { + CHANGE_LOCK(lock, d->dv_lock); + if (deep < 0 || deep > 1) { + // recursive: lock/unlock the items the List contains + int todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); + } + } + } + } + break; + } + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_UNKNOWN: { + assert(false); + } + } +#undef CHANGE_LOCK + recurse--; +} + +/// Check whether VimL value is locked itself or refers to a locked container +/// +/// @param[in] tv Value to check. +/// +/// @return True if value is locked, false otherwise. +bool tv_islocked(const typval_T *const tv) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return ((tv->v_lock & VAR_LOCKED) + || (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + || (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); +} + +//{{{2 Type checks + +/// Check that given value is a number or string +/// +/// Error messages are compatible with get_tv_number() previously used for the +/// same purpose in buf*() functions. Special values are not accepted (previous +/// behaviour: silently fail to find buffer). +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str_or_nr(const typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; + } + case VAR_FLOAT: { + EMSG(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + EMSG(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + EMSG(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + EMSG(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h new file mode 100644 index 0000000000..cf83904ffc --- /dev/null +++ b/src/nvim/eval/typval.h @@ -0,0 +1,285 @@ +#ifndef NVIM_EVAL_TYPVAL_H +#define NVIM_EVAL_TYPVAL_H + +#include +#include +#include + +#include "nvim/hashtab.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" +#include "nvim/lib/queue.h" +#include "nvim/profile.h" // for proftime_T +#include "nvim/pos.h" // for linenr_T + +/// Type used for VimL VAR_NUMBER values +typedef int varnumber_T; + +/// Type used for VimL VAR_FLOAT values +typedef double float_T; + +/// Maximal possible value of varnumber_T variable +#define VARNUMBER_MAX INT_MAX + +/// Mimimal possible value of varnumber_T variable +#define VARNUMBER_MIN INT_MIN + +/// %d printf format specifier for varnumber_T +#define PRIdVARNUMBER "d" + +typedef struct listvar_S list_T; +typedef struct dictvar_S dict_T; +typedef struct partial_S partial_T; + +/// Special variable values +typedef enum { + kSpecialVarFalse, ///< v:false + kSpecialVarTrue, ///< v:true + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED = 1, ///< User lock, can be unlocked. + VAR_FIXED = 2, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function reference, .v_string is used as function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_SPECIAL, ///< Special value (true, false, null), .v_special + ///< is used. + VAR_PARTIAL, ///< Partial, .v_partial is used. +} VarType; + +/// Structure that holds an internal variable value +typedef struct { + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. + union typval_vval_union { + varnumber_T v_number; ///< Number, for VAR_NUMBER. + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + partial_T *v_partial; ///< Closure: function with args. + } vval; ///< Actual value. +} typval_T; + +/// Values for (struct dictvar_S).dv_scope +typedef enum { + VAR_NO_SCOPE = 0, ///< Not a scope dictionary. + VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). + VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix + ///< (l:, g:). +} ScopeType; + +/// Structure to hold an item of a list +typedef struct listitem_S listitem_T; + +struct listitem_S { + listitem_T *li_next; ///< Next item in list. + listitem_T *li_prev; ///< Previous item in list. + typval_T li_tv; ///< Item value. +}; + +/// Structure used by those that are using an item in a list +typedef struct listwatch_S listwatch_T; + +struct listwatch_S { + listitem_T *lw_item; ///< Item being watched. + listwatch_T *lw_next; ///< Next watcher. +}; + +/// Structure to hold info about a list +struct listvar_S { + listitem_T *lv_first; ///< First item, NULL if none. + listitem_T *lv_last; ///< Last item, NULL if none. + int lv_refcount; ///< Reference count. + int lv_len; ///< Number of items. + listwatch_T *lv_watch; ///< First watcher, NULL if none. + int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. + listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". + int lv_copyID; ///< ID used by deepcopy(). + list_T *lv_copylist; ///< Copied list used by deepcopy(). + VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. + list_T *lv_used_next; ///< next list in used lists list. + list_T *lv_used_prev; ///< Previous list in used lists list. +}; + +// Static list with 10 items. Use init_static_list() to initialize. +typedef struct { + list_T sl_list; // must be first + listitem_T sl_items[10]; +} staticList10_T; + +// Structure to hold an item of a Dictionary. +// Also used for a variable. +// The key is copied into "di_key" to avoid an extra alloc/free for it. +struct dictitem_S { + typval_T di_tv; ///< type and value of the variable + char_u di_flags; ///< flags (only used for variable) + char_u di_key[1]; ///< key (actually longer!) +}; + +#define TV_DICTITEM_STRUCT(KEY_LEN) \ + struct { \ + typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ + uint8_t di_flags; /* Flags. */ \ + char_u di_key[KEY_LEN]; /* NUL. */ \ + } + +/// Structure to hold a scope dictionary +/// +/// @warning Must be compatible with dictitem_T. +/// +/// For use in find_var_in_ht to pretend that it found dictionary item when it +/// finds scope dictionary. +typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; + +/// Structure to hold an item of a Dictionary +/// +/// @warning Must be compatible with ScopeDictDictItem. +/// +/// Also used for a variable. +typedef TV_DICTITEM_STRUCT() dictitem_T; + +/// Flags for dictitem_T.di_flags +typedef enum { + DI_FLAGS_RO = 1, ///< Read-only value + DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox + DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. + DI_FLAGS_LOCK = 8, ///< Locked value. + DI_FLAGS_ALLOC = 16, ///< Separately allocated. +} DictItemFlags; + +/// Structure representing a Dictionary +struct dictvar_S { + VarLockStatus dv_lock; ///< Whole dictionary lock status. + ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. +}; + +/// Type used for script ID +typedef int scid_T; + +// Structure to hold info for a function that is currently being executed. +typedef struct funccall_S funccall_T; + +// Structure to hold info for a user function. +typedef struct ufunc ufunc_T; + +struct ufunc { + int uf_varargs; ///< variable nr of arguments + int uf_flags; + int uf_calls; ///< nr of active calls + bool uf_cleared; ///< func_clear() was already called + garray_T uf_args; ///< arguments + garray_T uf_lines; ///< function lines + int uf_profiling; ///< true when func is being profiled + // Profiling the function as a whole. + int uf_tm_count; ///< nr of calls + proftime_T uf_tm_total; ///< time spent in function + children + proftime_T uf_tm_self; ///< time spent in function itself + proftime_T uf_tm_children; ///< time spent in children this call + // Profiling the function per line. + int *uf_tml_count; ///< nr of times line was executed + proftime_T *uf_tml_total; ///< time spent in a line + children + proftime_T *uf_tml_self; ///< time spent in a line itself + proftime_T uf_tml_start; ///< start time for current line + proftime_T uf_tml_children; ///< time spent in children for this line + proftime_T uf_tml_wait; ///< start wait time for current line + int uf_tml_idx; ///< index of line being timed; -1 if none + int uf_tml_execed; ///< line being timed was executed + scid_T uf_script_ID; ///< ID of script where function was defined, + // used for s: variables + int uf_refcount; ///< reference count, see func_name_refcount() + funccall_T *uf_scoped; ///< l: local variables for closure + char_u uf_name[1]; ///< name of function (actually longer); can + // start with 123_ ( is K_SPECIAL + // KS_EXTRA KE_SNR) +}; + +/// Maximum number of function arguments +#define MAX_FUNC_ARGS 20 + +struct partial_S { + int pt_refcount; ///< Reference count. + char_u *pt_name; ///< Function name; when NULL use pt_func->name. + ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with + ///< pt_name. + bool pt_auto; ///< When true the partial was created by using dict.member + ///< in handle_subscript(). + int pt_argc; ///< Number of arguments. + typval_T *pt_argv; ///< Arguments in allocated array. + dict_T *pt_dict; ///< Dict for "self". +}; + +/// Structure used for explicit stack while garbage collecting hash tables +typedef struct ht_stack_S { + hashtab_T *ht; + struct ht_stack_S *prev; +} ht_stack_T; + +/// Structure used for explicit stack while garbage collecting lists +typedef struct list_stack_S { + list_T *list; + struct list_stack_S *prev; +} list_stack_T; + +// In a hashtab item "hi_key" points to "di_key" in a dictitem. +// This avoids adding a pointer to the hashtab item. + +/// Convert a dictitem pointer to a hashitem key pointer +#define DI2HIKEY(di) ((di)->di_key) + +/// Convert a hashitem key pointer to a dictitem pointer +#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) + +/// Convert a hashitem value pointer to a dictitem pointer +#define HIVAL2DI(p) \ + ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) + +/// Convert a hashitem pointer to a dictitem pointer +#define HI2DI(hi) HIKEY2DI((hi)->hi_key) + +/// Get the number of items in a list +/// +/// @param[in] l List to check. +static inline long tv_list_len(list_T *const l) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l == NULL) { + return 0; + } + return l->lv_len; +} + +/// Empty string +/// +/// Needed for hack which allows not allocating empty string and still not +/// crashing when freeing it. +extern const char *const tv_empty_string; + +/// Specifies that free_unref_items() function has (not) been entered +extern bool tv_in_free_unref_items; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.h.generated.h" +#endif +#endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 4ff5589887..eb89a601ff 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -242,7 +242,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/encode.h" #include "nvim/func_attr.h" #include "nvim/eval/typval_encode.h" diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 46145c5d03..3475f6d8b3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -11,7 +11,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/func_attr.h" /// Type of the stack entry diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h deleted file mode 100644 index 8f5e1a897d..0000000000 --- a/src/nvim/eval_defs.h +++ /dev/null @@ -1,286 +0,0 @@ -#ifndef NVIM_EVAL_DEFS_H -#define NVIM_EVAL_DEFS_H - -#include -#include -#include - -#include "nvim/hashtab.h" -#include "nvim/lib/queue.h" -#include "nvim/garray.h" // for garray_T -#include "nvim/profile.h" // for proftime_T -#include "nvim/pos.h" // for linenr_T - -typedef int varnumber_T; -typedef double float_T; - -#define VARNUMBER_MAX INT_MAX -#define VARNUMBER_MIN INT_MIN - -typedef struct listvar_S list_T; -typedef struct dictvar_S dict_T; -typedef struct partial_S partial_T; - -/// Special variable values -typedef enum { - kSpecialVarFalse, ///< v:false - kSpecialVarTrue, ///< v:true - kSpecialVarNull, ///< v:null -} SpecialVarValue; - -/// Variable lock status for typval_T.v_lock -typedef enum { - VAR_UNLOCKED = 0, ///< Not locked. - VAR_LOCKED = 1, ///< User lock, can be unlocked. - VAR_FIXED = 2, ///< Locked forever. -} VarLockStatus; - -/// VimL variable types, for use in typval_T.v_type -typedef enum { - VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. - VAR_NUMBER, ///< Number, .v_number is used. - VAR_STRING, ///< String, .v_string is used. - VAR_FUNC, ///< Function reference, .v_string is used as function name. - VAR_LIST, ///< List, .v_list is used. - VAR_DICT, ///< Dictionary, .v_dict is used. - VAR_FLOAT, ///< Floating-point value, .v_float is used. - VAR_SPECIAL, ///< Special value (true, false, null), .v_special - ///< is used. - VAR_PARTIAL, ///< Partial, .v_partial is used. -} VarType; - -/// Structure that holds an internal variable value -typedef struct { - VarType v_type; ///< Variable type. - VarLockStatus v_lock; ///< Variable lock status. - union typval_vval_union { - varnumber_T v_number; ///< Number, for VAR_NUMBER. - SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. - float_T v_float; ///< Floating-point number, for VAR_FLOAT. - char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. - list_T *v_list; ///< List for VAR_LIST, can be NULL. - dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. - partial_T *v_partial; ///< Closure: function with args. - } vval; ///< Actual value. -} typval_T; - -/* Values for "dv_scope". */ -#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ -#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not - allowed to mask existing functions */ - -/* - * Structure to hold an item of a list: an internal variable without a name. - */ -typedef struct listitem_S listitem_T; - -struct listitem_S { - listitem_T *li_next; /* next item in list */ - listitem_T *li_prev; /* previous item in list */ - typval_T li_tv; /* type and value of the variable */ -}; - -/* - * Struct used by those that are using an item in a list. - */ -typedef struct listwatch_S listwatch_T; - -struct listwatch_S { - listitem_T *lw_item; /* item being watched */ - listwatch_T *lw_next; /* next watcher */ -}; - -/* - * Structure to hold info about a list. - */ -struct listvar_S { - listitem_T *lv_first; ///< First item, NULL if none. - listitem_T *lv_last; ///< Last item, NULL if none. - int lv_refcount; ///< Reference count. - int lv_len; ///< Number of items. - listwatch_T *lv_watch; ///< First watcher, NULL if none. - int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. - listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". - int lv_copyID; ///< ID used by deepcopy(). - list_T *lv_copylist; ///< Copied list used by deepcopy(). - VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. - list_T *lv_used_next; ///< next list in used lists list. - list_T *lv_used_prev; ///< Previous list in used lists list. -}; - -// Static list with 10 items. Use init_static_list() to initialize. -typedef struct { - list_T sl_list; // must be first - listitem_T sl_items[10]; -} staticList10_T; - -// Structure to hold an item of a Dictionary. -// Also used for a variable. -// The key is copied into "di_key" to avoid an extra alloc/free for it. -struct dictitem_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[1]; ///< key (actually longer!) -}; - -typedef struct dictitem_S dictitem_T; - -/// A dictitem with a 16 character key (plus NUL) -struct dictitem16_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[17]; ///< key -}; - -typedef struct dictitem16_S dictitem16_T; - - -#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable -#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox -#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() -#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable -#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated - -/// Structure representing a Dictionary -struct dictvar_S { - VarLockStatus dv_lock; ///< Whole dictionary lock status. - char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if - ///< dictionary represents a scope (i.e. g:, l: …). - int dv_refcount; ///< Reference count. - int dv_copyID; ///< ID used when recursivery traversing a value. - hashtab_T dv_hashtab; ///< Hashtab containing all items. - dict_T *dv_copydict; ///< Copied dict used by deepcopy(). - dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. - dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. - QUEUE watchers; ///< Dictionary key watchers set by user code. -}; - -typedef int scid_T; // script ID -typedef struct funccall_S funccall_T; - -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - -struct ufunc { - int uf_varargs; ///< variable nr of arguments - int uf_flags; - int uf_calls; ///< nr of active calls - bool uf_cleared; ///< func_clear() was already called - garray_T uf_args; ///< arguments - garray_T uf_lines; ///< function lines - int uf_profiling; ///< true when func is being profiled - // Profiling the function as a whole. - int uf_tm_count; ///< nr of calls - proftime_T uf_tm_total; ///< time spent in function + children - proftime_T uf_tm_self; ///< time spent in function itself - proftime_T uf_tm_children; ///< time spent in children this call - // Profiling the function per line. - int *uf_tml_count; ///< nr of times line was executed - proftime_T *uf_tml_total; ///< time spent in a line + children - proftime_T *uf_tml_self; ///< time spent in a line itself - proftime_T uf_tml_start; ///< start time for current line - proftime_T uf_tml_children; ///< time spent in children for this line - proftime_T uf_tml_wait; ///< start wait time for current line - int uf_tml_idx; ///< index of line being timed; -1 if none - int uf_tml_execed; ///< line being timed was executed - scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables - int uf_refcount; ///< reference count, see func_name_refcount() - funccall_T *uf_scoped; ///< l: local variables for closure - char_u uf_name[1]; ///< name of function (actually longer); can - // start with 123_ ( is K_SPECIAL - // KS_EXTRA KE_SNR) -}; - -/// Maximum number of function arguments -#define MAX_FUNC_ARGS 20 -#define VAR_SHORT_LEN 20 // short variable name length -#define FIXVAR_CNT 12 // number of fixed variables - -// structure to hold info for a function that is currently being executed. -struct funccall_S { - ufunc_T *func; ///< function being called - int linenr; ///< next line to be executed - int returned; ///< ":return" used - struct { ///< fixed variables for arguments - dictitem_T var; ///< variable (without room for name) - char_u room[VAR_SHORT_LEN]; ///< room for the name - } fixvar[FIXVAR_CNT]; - dict_T l_vars; ///< l: local function variables - dictitem_T l_vars_var; ///< variable for l: scope - dict_T l_avars; ///< a: argument variables - dictitem_T l_avars_var; ///< variable for a: scope - list_T l_varlist; ///< list for a:000 - listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000 - typval_T *rettv; ///< return value - linenr_T breakpoint; ///< next line with breakpoint or zero - int dbg_tick; ///< debug_tick when breakpoint was set - int level; ///< top nesting level of executed function - proftime_T prof_child; ///< time spent in a child - funccall_T *caller; ///< calling function or NULL - int fc_refcount; ///< number of user functions that reference - // this funccal - int fc_copyID; ///< for garbage collection - garray_T fc_funcs; ///< list of ufunc_T* which keep a reference - // to "func" -}; - -// structure used by trans_function_name() -typedef struct { - dict_T *fd_dict; ///< Dictionary used. - char_u *fd_newkey; ///< New key in "dict" in allocated memory. - dictitem_T *fd_di; ///< Dictionary item used. -} funcdict_T; - -struct partial_S { - int pt_refcount; ///< Reference count. - char_u *pt_name; ///< Function name; when NULL use pt_func->name. - ufunc_T *pt_func; ///< Function pointer; when NULL lookup function - ///< with pt_name. - bool pt_auto; ///< when true the partial was created for using - ///< dict.member in handle_subscript(). - int pt_argc; ///< Number of arguments. - typval_T *pt_argv; ///< Arguments in allocated array. - dict_T *pt_dict; ///< Dict for "self". -}; - -// structure used for explicit stack while garbage collecting hash tables -typedef struct ht_stack_S { - hashtab_T *ht; - struct ht_stack_S *prev; -} ht_stack_T; - -// structure used for explicit stack while garbage collecting lists -typedef struct list_stack_S { - list_T *list; - struct list_stack_S *prev; -} list_stack_T; - -// In a hashtab item "hi_key" points to "di_key" in a dictitem. -// This avoids adding a pointer to the hashtab item. - -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - -/// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) - -/// Type of assert_* check being performed -typedef enum -{ - ASSERT_EQUAL, - ASSERT_NOTEQUAL, - ASSERT_MATCH, - ASSERT_NOTMATCH, - ASSERT_INRANGE, - ASSERT_OTHER, -} assert_type_T; - -#endif // NVIM_EVAL_DEFS_H diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 678102daf6..151a4d375f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2958,7 +2958,7 @@ void sub_set_replacement(SubReplacementString sub) { xfree(old_sub.sub); if (sub.additional_elements != old_sub.additional_elements) { - list_unref(old_sub.additional_elements); + tv_list_unref(old_sub.additional_elements); } old_sub = sub; } diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 243b11255e..65bbd8a99e 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -4,8 +4,8 @@ #include #include "nvim/os/time.h" -#include "nvim/eval_defs.h" #include "nvim/pos.h" +#include "nvim/eval/typval.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9fc4ef2a02..e59a87b335 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3698,12 +3698,12 @@ static void script_host_execute(char *name, exarg_T *eap) uint8_t *script = script_get(eap, eap->arg); if (!eap->skip) { - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // script - list_append_string(args, script ? script : eap->arg, -1); + tv_list_append_string(args, (const char *)(script ? script : eap->arg), -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3715,21 +3715,21 @@ static void script_host_execute_file(char *name, exarg_T *eap) uint8_t buffer[MAXPATHL]; vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // filename - list_append_string(args, buffer, -1); + tv_list_append_string(args, (const char *)buffer, -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = list_alloc(); - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); - list_append_string(args, eap->arg, -1); + list_T *args = tv_list_alloc(); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + tv_list_append_string(args, (const char *)eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d1557f9c82..dc99c0771d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8416,8 +8416,8 @@ eval_vars ( *usedlen = 1; return NULL; } - result = list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), + (long)i); if (result == NULL) { *errormsg = (char_u *)""; return NULL; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 4bb6f97035..7a34a181e2 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,6 +1,8 @@ -/* - * ex_eval.c: functions for Ex command line for the +eval feature. - */ +// TODO(ZyX-I): move to eval/executor + +/// @file ex_eval.c +/// +/// Functions for Ex command line for the +eval feature. #include #include #include @@ -779,7 +781,6 @@ void report_discard_pending(int pending, void *value) */ void ex_if(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -800,6 +801,7 @@ void ex_if(exarg_T *eap) 1] & CSF_ACTIVE)); + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); if (!skip && !error) { @@ -844,7 +846,6 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -901,6 +902,7 @@ void ex_else(exarg_T *eap) } if (eap->cmdidx == CMD_elseif) { + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); /* When throwing error exceptions, we want to throw always the first * of several errors in a row. This is what actually happens when @@ -925,7 +927,7 @@ void ex_else(exarg_T *eap) */ void ex_while(exarg_T *eap) { - int error; + bool error; int skip; int result; struct condstack *cstack = eap->cstack; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8758a63bce..9851ed5396 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4249,7 +4249,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); } - list_unref(retlist); + tv_list_unref(retlist); *file = ga.ga_data; *num_file = ga.ga_len; @@ -4545,7 +4545,7 @@ static inline void hist_free_entry(histentry_T *hisptr) FUNC_ATTR_NONNULL_ALL { xfree(hisptr->hisstr); - list_unref(hisptr->additional_elements); + tv_list_unref(hisptr->additional_elements); clear_hist_entry(hisptr); } @@ -4601,7 +4601,7 @@ in_history ( history[type][last_i] = history[type][i]; last_i = i; } - list_unref(list); + tv_list_unref(list); history[type][i].hisnum = ++hisnum[type]; history[type][i].hisstr = str; history[type][i].timestamp = os_time(); diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 24eebdc303..5a1ca5213a 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -1,7 +1,7 @@ #ifndef NVIM_EX_GETLN_H #define NVIM_EX_GETLN_H -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index de79ee2469..d87407f099 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -12,6 +12,7 @@ #include "nvim/syntax_defs.h" #include "nvim/types.h" #include "nvim/event/loop.h" +#include "nvim/os/os_defs.h" #define IOSIZE (1024+1) // file I/O and sprintf buffer size @@ -21,16 +22,6 @@ # define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8 // takes 6 bytes for one cell) -// Maximum length of a file path. Make it a bit long, to stay -// on the safe side. But not too long to put on the stack. -#ifndef MAXPATHL -# ifdef MAXPATHLEN -# define MAXPATHL MAXPATHLEN -# else -# define MAXPATHL 256 -# endif -#endif - #ifdef WIN32 # define _PATHSEPSTR "\\" #else @@ -1226,11 +1217,6 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ EXTERN int ignored; EXTERN char *ignoredp; -EXTERN bool in_free_unref_items INIT(= false); - -// Used for checking if local variables or arguments used in a lambda. -EXTERN int *eval_lavars_used INIT(= NULL); - // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); diff --git a/src/nvim/main.c b/src/nvim/main.c index 7b1c912f4b..0c978dc47d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -391,9 +391,10 @@ int main(int argc, char **argv) shada_read_everything(NULL, false, true); TIME_MSG("reading ShaDa"); } - /* It's better to make v:oldfiles an empty list than NULL. */ - if (get_vim_var_list(VV_OLDFILES) == NULL) - set_vim_var_list(VV_OLDFILES, list_alloc()); + // It's better to make v:oldfiles an empty list than NULL. + if (get_vim_var_list(VV_OLDFILES) == NULL) { + set_vim_var_list(VV_OLDFILES, tv_list_alloc()); + } /* * "-q errorfile": Load the error file now. diff --git a/src/nvim/mark.c b/src/nvim/mark.c index de2fdd7f13..1d1e13e7e0 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1431,3 +1431,26 @@ void free_all_marks(void) memset(&namedfm[0], 0, sizeof(namedfm)); } #endif + +/// Adjust position to point to the first byte of a multi-byte character +/// +/// If it points to a tail byte it is move backwards to the head byte. +/// +/// @param[in] buf Buffer to adjust position in. +/// @param[out] lp Position to adjust. +void mark_mb_adjustpos(buf_T *buf, pos_T *lp) + FUNC_ATTR_NONNULL_ALL +{ + if (lp->col > 0 || lp->coladd > 1) { + const char_u *const p = ml_get_buf(buf, lp->lnum, false); + lp->col -= (*mb_head_off)(p, p + lp->col); + // Reset "coladd" when the cursor would be on the right half of a + // double-wide character. + if (lp->coladd == 1 + && p[lp->col] != TAB + && vim_isprintc((*mb_ptr2char)(p + lp->col)) + && ptr2cells(p + lp->col) > 1) { + lp->coladd = 0; + } + } +} diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 720b2475ed..2cb489501e 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -3,7 +3,7 @@ #include "nvim/pos.h" #include "nvim/os/time.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" /* * marks: positions in a file diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 0ab133a545..57518a9535 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -50,6 +50,7 @@ #include "nvim/strings.h" #include "nvim/os/os.h" #include "nvim/arabic.h" +#include "nvim/mark.h" typedef struct { int rangeStart; @@ -375,16 +376,18 @@ void remove_bom(char_u *s) */ int mb_get_class(const char_u *p) { - return mb_get_class_buf(p, curbuf); + return mb_get_class_tab(p, curbuf->b_chartab); } -int mb_get_class_buf(const char_u *p, buf_T *buf) +int mb_get_class_tab(const char_u *p, const uint64_t *const chartab) { if (MB_BYTE2LEN(p[0]) == 1) { - if (p[0] == NUL || ascii_iswhite(p[0])) + if (p[0] == NUL || ascii_iswhite(p[0])) { return 0; - if (vim_iswordc_buf(p[0], buf)) + } + if (vim_iswordc_tab(p[0], chartab)) { return 2; + } return 1; } return utf_class(utf_ptr2char(p)); @@ -1639,38 +1642,16 @@ theend: */ void mb_adjust_cursor(void) { - mb_adjustpos(curbuf, &curwin->w_cursor); -} - -/* - * Adjust position "*lp" to point to the first byte of a multi-byte character. - * If it points to a tail byte it's moved backwards to the head byte. - */ -void mb_adjustpos(buf_T *buf, pos_T *lp) -{ - char_u *p; - - if (lp->col > 0 - || lp->coladd > 1 - ) { - p = ml_get_buf(buf, lp->lnum, FALSE); - lp->col -= (*mb_head_off)(p, p + lp->col); - /* Reset "coladd" when the cursor would be on the right half of a - * double-wide character. */ - if (lp->coladd == 1 - && p[lp->col] != TAB - && vim_isprintc((*mb_ptr2char)(p + lp->col)) - && ptr2cells(p + lp->col) > 1) - lp->coladd = 0; - } + mark_mb_adjustpos(curbuf, &curwin->w_cursor); } /// Checks and adjusts cursor column. Not mode-dependent. /// @see check_cursor_col_win /// -/// @param win Places cursor on a valid column for this window. -void mb_check_adjust_col(win_T *win) +/// @param win_ Places cursor on a valid column for this window. +void mb_check_adjust_col(void *win_) { + win_T *win = (win_T *)win_; colnr_T oldcol = win->w_cursor.col; // Column 0 is always valid. diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 2c92a0fbb2..5f5bab9fcd 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -3,6 +3,8 @@ #include +#include "nvim/iconv.h" + /* * Return byte length of character that starts with byte "b". * Returns 1 for a single-byte character. @@ -40,6 +42,27 @@ #define mb_ptr2char utf_ptr2char #define mb_head_off utf_head_off +/// 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; + +/// Structure used for string conversions +typedef struct { + int vc_type; ///< Zero or more ConvFlags. + int vc_factor; ///< Maximal expansion factor. +# ifdef USE_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; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a..b4fdd86a6d 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -430,6 +430,19 @@ char *xstrdup(const char *str) return xmemdupz(str, strlen(str)); } +/// strdup() wrapper +/// +/// Unlike xstrdup() allocates a new empty string if it receives NULL. +char *xstrdupnul(const char *const str) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + if (str == NULL) { + return xmallocz(0); + } else { + return xstrdup(str); + } +} + /// A version of memchr that starts the search at `src + len`. /// /// Based on glibc's memrchr. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2ade9cb87d..4cca5ec948 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2465,7 +2465,7 @@ do_mouse ( &rettv, ARRAY_SIZE(argv), argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); - clear_tv(&rettv); + tv_clear(&rettv); break; } } @@ -7290,11 +7290,11 @@ static bool unadjust_for_sel(void) pp = &curwin->w_cursor; else pp = &VIsual; - if (pp->coladd > 0) - --pp->coladd; - else if (pp->col > 0) { - --pp->col; - mb_adjustpos(curbuf, pp); + if (pp->coladd > 0) { + pp->coladd--; + } else if (pp->col > 0) { + pp->col--; + mark_mb_adjustpos(curbuf, pp); } else if (pp->lnum > 1) { --pp->lnum; pp->col = (colnr_T)STRLEN(ml_get(pp->lnum)); @@ -7829,7 +7829,7 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { - mb_adjustpos(curwin->w_buffer, &oap->end); + mark_mb_adjustpos(curwin->w_buffer, &oap->end); } getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c13b6f736a..85cef59aec 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2555,9 +2555,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) dict_T *dict = get_vim_var_dict(VV_EVENT); // the yanked text - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; dict_add_list(dict, "regcontents", list); @@ -4844,8 +4844,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = list_alloc(); - list_append_string(list, NULL, -1); + list_T *list = tv_list_alloc(); + tv_list_append_string(list, NULL, 0); list->lv_first->li_tv.vval.v_string = s; return list; } @@ -4895,9 +4895,9 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } return list; @@ -5570,9 +5570,9 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *args = list_alloc(); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + list_T *const args = tv_list_alloc(); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5584,7 +5584,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) goto err; } - list_T *res = result.vval.v_list, *lines = NULL; + list_T *res = result.vval.v_list; + list_T *lines = NULL; if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { lines = res->lv_first->li_tv.vval.v_list; if (res->lv_last->li_tv.v_type != VAR_STRING) { @@ -5628,7 +5629,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (li->li_tv.v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string); + reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); } if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { @@ -5686,35 +5687,39 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = list_alloc(); + list_T *lines = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(lines, reg->y_array[i], -1); + tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = list_alloc(); - list_append_list(args, lines); + list_T *args = tv_list_alloc(); + tv_list_append_list(args, lines); - char_u regtype; + char regtype; switch (reg->y_type) { - case kMTLineWise: - regtype = 'V'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTCharWise: - regtype = 'v'; - break; - case kMTBlockWise: - regtype = 'b'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTUnknown: - assert(false); - } - list_append_string(args, ®type, 1); - - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + case kMTLineWise: { + regtype = 'V'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTCharWise: { + regtype = 'v'; + break; + } + case kMTBlockWise: { + regtype = 'b'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTUnknown: { + assert(false); + } + } + tv_list_append_string(args, ®type, 1); + + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); (void)eval_call_provider("clipboard", "set", args); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 44df2e9e0c..13d0142343 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -6,7 +6,7 @@ #include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/os/time.h" typedef int (*Indenter)(void); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 8d8c20c1d0..323503c4f5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4003,7 +4003,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) bufnum = 0; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1cd334abcd..9c6f02f778 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3650,9 +3650,11 @@ static long regtry(bt_regprog_T *prog, colnr_T col) */ static int reg_prev_class(void) { - if (reginput > regline) - return mb_get_class_buf(reginput - 1 - - (*mb_head_off)(regline, reginput - 1), reg_buf); + if (reginput > regline) { + return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline, + reginput - 1), + reg_buf->b_chartab); + } return -1; } @@ -3918,12 +3920,13 @@ regmatch ( else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - status = RA_NOMATCH; /* not on a word at all */ - else if (reg_prev_class() == this_class) - status = RA_NOMATCH; /* previous char is in same word */ + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + if (this_class <= 1) { + status = RA_NOMATCH; // Not on a word at all. + } else if (reg_prev_class() == this_class) { + status = RA_NOMATCH; // Previous char is in same word. + } } else { if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline && vim_iswordc_buf(reginput[-1 @@ -3938,8 +3941,8 @@ regmatch ( else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) @@ -6617,7 +6620,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } - clear_tv(&rettv); + tv_clear(&rettv); } else { eval_result = eval_to_string(source + 2, NULL, true); } @@ -6976,7 +6979,7 @@ list_T *reg_submatch_list(int no) linenr_T slnum; linenr_T elnum; list_T *list; - char_u *s; + const char *s; if (submatch_match == NULL) { slnum = submatch_mmatch->startpos[no].lnum; @@ -6988,27 +6991,27 @@ list_T *reg_submatch_list(int no) colnr_T scol = submatch_mmatch->startpos[no].col; colnr_T ecol = submatch_mmatch->endpos[no].col; - list = list_alloc(); + list = tv_list_alloc(); - s = reg_getline_submatch(slnum) + scol; + s = (const char *)reg_getline_submatch(slnum) + scol; if (slnum == elnum) { - list_append_string(list, s, ecol - scol); + tv_list_append_string(list, s, ecol - scol); } else { - list_append_string(list, s, -1); + tv_list_append_string(list, s, -1); for (int i = 1; i < elnum - slnum; i++) { - s = reg_getline_submatch(slnum + i); - list_append_string(list, s, -1); + s = (const char *)reg_getline_submatch(slnum + i); + tv_list_append_string(list, s, -1); } - s = reg_getline_submatch(elnum); - list_append_string(list, s, ecol); + s = (const char *)reg_getline_submatch(elnum); + tv_list_append_string(list, s, ecol); } } else { - s = submatch_match->startp[no]; + s = (const char *)submatch_match->startp[no]; if (s == NULL || submatch_match->endp[no] == NULL) { return NULL; } - list = list_alloc(); - list_append_string(list, s, (int)(submatch_match->endp[no] - s)); + list = tv_list_alloc(); + tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s); } return list; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 3f4e12af4a..5b49ab38f0 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -5410,7 +5410,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); if (this_class <= 1) { result = false; } else if (reg_prev_class() == this_class) { @@ -5435,7 +5435,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 197b029591..c550cb0888 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -30,7 +30,7 @@ #include "nvim/ex_getln.h" #include "nvim/search.h" #include "nvim/regexp.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" @@ -1223,7 +1223,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = list_alloc(); + oldfiles_list = tv_list_alloc(); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1435,8 +1435,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) fname = xstrdup(fname); } int kh_ret; - (void) kh_put(strset, &oldfiles_set, fname, &kh_ret); - list_append_allocated_string(oldfiles_list, fname); + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. cur_entry.data.filemark.fname = NULL; @@ -1573,7 +1573,9 @@ static char *shada_filename(const char *file) do { \ const String s_ = (s); \ msgpack_pack_str(spacker, s_.size); \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ } while (0) #define PACK_BIN(s) \ do { \ @@ -1965,7 +1967,7 @@ static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, typval_T tgttv; var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, true, 0); - clear_tv(&entry.data.data.global_var.value); + tv_clear(&entry.data.data.global_var.value); entry.data.data.global_var.value = tgttv; } ret = shada_pack_entry(packer, entry.data, max_kbyte); @@ -2573,13 +2575,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } }, max_kbyte)) == kSDWriteFailed) { - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); ret = kSDWriteFailed; goto shada_write_exit; } - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); @@ -3172,18 +3174,18 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemHistoryEntry: { - list_unref(entry->data.history_item.additional_elements); + tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; } case kSDItemVariable: { - list_unref(entry->data.global_var.additional_elements); + tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); break; } case kSDItemSubString: { - list_unref(entry->data.sub_string.additional_elements); + tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; } @@ -3451,7 +3453,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ - clear_tv(&adtv); \ + tv_clear(&adtv); \ goto shada_read_next_item_error; \ } \ tgt = adtv.vval.v_dict; \ @@ -3474,7 +3476,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, if (msgpack_to_vim(obj, &aetv) == FAIL) { \ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ - clear_tv(&aetv); \ + tv_clear(&aetv); \ goto shada_read_next_item_error; \ } \ assert(aetv.v_type == VAR_LIST); \ @@ -3866,7 +3868,7 @@ shada_read_next_item_hist_no_conv: &tgttv, true, 0); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); entry->data.global_var.value = tgttv; } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 3b891d998f..5ca5ab3339 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3248,7 +3248,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) add_suggestion(su, &su->su_ga, p, su->su_badlen, score, 0, true, su->su_sallang, false); } - list_unref(list); + tv_list_unref(list); } // Remove bogus suggestions, sort and truncate at "maxcount". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 267832ed2d..b964fed35a 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -571,7 +571,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) EMSG(_(e_printf)); } else { (*idxp)++; - int err = false; + bool err = false; n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); if (err) { n = 0; diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 8aea374b96..59b8701a3f 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -5,7 +5,7 @@ #include #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "strings.h.generated.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7bcaff662c..b0ffc12b5f 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -674,7 +674,7 @@ do_tag ( fname = xmalloc(MAXPATHL + 1); cmd = xmalloc(CMDBUFFSIZE + 1); - list = list_alloc(); + list = tv_list_alloc(); for (i = 0; i < num_matches; ++i) { int len, cmd_len; @@ -774,7 +774,7 @@ do_tag ( } dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); dict_add_nr_str(dict, "filename", 0L, fname); @@ -786,7 +786,7 @@ do_tag ( vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); set_errorlist(curwin, list, ' ', IObuff, NULL); - list_free(list); + tv_list_free(list); xfree(fname); xfree(cmd); @@ -2825,7 +2825,7 @@ int get_tags(list_T *list, char_u *pat) continue; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c95a795587..729cf03e15 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2952,14 +2952,14 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = list_alloc(); + list_T *alt_list = tv_list_alloc(); /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); dict_add_list(dict, "alt", alt_list); } - list_append_dict(list, dict); + tv_list_append_dict(list, dict); uhp = uhp->uh_prev.ptr; } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 4fac730f02..47a97da0cf 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5582,7 +5582,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int len = 1; list_T *subl; listitem_T *subli; - int error = false; + bool error = false; if (li->li_tv.v_type == VAR_LIST) { subl = li->li_tv.vval.v_list; @@ -5594,7 +5594,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, goto fail; } lnum = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } if (lnum == 0) { @@ -5605,12 +5605,13 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, subli = subli->li_next; if (subli != NULL) { col = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) + if (error) { goto fail; + } subli = subli->li_next; if (subli != NULL) { len = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } } @@ -5881,8 +5882,8 @@ void win_id2tabwin(typval_T *argvars, list_T *list) int id = get_tv_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); - list_append_number(list, tabnr); - list_append_number(list, winnr); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); } win_T * win_id2wp(typval_T *argvars) @@ -5918,7 +5919,7 @@ void win_findbuf(typval_T *argvars, list_T *list) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { - list_append_number(list, wp->handle); + tv_list_append_number(list, wp->handle); } } } -- cgit From e18a5783080f7c94f408ec5f53dedffdb69789e1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:24:34 +0300 Subject: *: Move some dictionary functions to typval.h and use char* Also fixes buffer reusage in setmatches() and complete(). --- src/.asan-blacklist | 2 +- src/nvim/api/private/helpers.c | 33 +- src/nvim/api/vim.c | 2 +- src/nvim/ascii.h | 8 +- src/nvim/buffer.c | 12 +- src/nvim/buffer.h | 5 +- src/nvim/charset.h | 10 + src/nvim/diff.c | 2 +- src/nvim/edit.c | 186 ++-- src/nvim/eval.c | 1780 ++++++++++++--------------------------- src/nvim/eval.h | 8 +- src/nvim/eval/decode.c | 26 +- src/nvim/eval/encode.c | 4 +- src/nvim/eval/typval.c | 788 ++++++++++++++++- src/nvim/eval/typval.h | 128 ++- src/nvim/eval/typval_encode.c.h | 10 +- src/nvim/event/process.h | 2 +- src/nvim/ex_cmds.c | 4 +- src/nvim/ex_cmds2.c | 65 +- src/nvim/ex_docmd.c | 28 +- src/nvim/ex_getln.c | 4 +- src/nvim/file_search.c | 2 +- src/nvim/getchar.c | 5 +- src/nvim/hashtab.c | 7 +- src/nvim/hashtab.h | 19 + src/nvim/macros.h | 9 + src/nvim/main.c | 45 +- src/nvim/mark.c | 2 +- src/nvim/mbyte.c | 36 +- src/nvim/mbyte.h | 15 + src/nvim/memline.c | 2 +- src/nvim/ops.c | 5 +- src/nvim/option.c | 364 ++++---- src/nvim/path.c | 73 +- src/nvim/popupmnu.c | 11 +- src/nvim/quickfix.c | 69 +- src/nvim/search.c | 9 +- src/nvim/shada.c | 14 +- src/nvim/spell.c | 9 +- src/nvim/spellfile.c | 2 +- src/nvim/syntax.c | 30 +- src/nvim/tag.c | 8 +- src/nvim/terminal.c | 10 +- src/nvim/undo.c | 4 +- src/nvim/vim.h | 38 +- src/nvim/window.c | 20 +- 46 files changed, 2068 insertions(+), 1847 deletions(-) (limited to 'src') diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 7636f8fa82..928d81bd5a 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ # multiqueue.h pointer arithmetic is not accepted by asan fun:multiqueue_node_data -fun:dictwatcher_node_data +fun:tv_dict_watcher_node_data diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff45cad8f5..6b17ba1a11 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -88,14 +88,13 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); + dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); - if (HASHITEM_EMPTY(hi)) { + if (di == NULL) { api_set_error(err, Validation, _("Key not found")); return (Object) OBJECT_INIT; } - dictitem_T *di = dict_lookup(hi); return vim_to_object(&di->di_tv); } @@ -130,7 +129,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, return rv; } - dictitem_T *di = dict_find(dict, (char_u *)key.data, (int)key.size); + dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) { @@ -156,9 +155,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, rv = vim_to_object(&di->di_tv); } // Delete the entry - hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(di); + tv_dict_item_remove(dict, di); } } else { // Update the key @@ -171,8 +168,8 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (di == NULL) { // Need to create an entry - di = dictitem_alloc((uint8_t *) key.data); - dict_add(dict, di); + di = tv_dict_item_alloc_len(key.data, key.size); + tv_dict_add(dict, di); } else { // Return the old value if (retval) { @@ -706,7 +703,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeDictionary: { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { KeyValuePair item = obj.data.dictionary.items[i]; @@ -716,20 +713,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) api_set_error(err, Validation, _("Empty dictionary keys aren't allowed")); // cleanup - dict_free(dict); + tv_dict_free(dict); return false; } - dictitem_T *di = dictitem_alloc((uint8_t *)key.data); + dictitem_T *const di = tv_dict_item_alloc(key.data); if (!object_to_vim(item.value, &di->di_tv, err)) { // cleanup - dictitem_free(di); - dict_free(dict); + tv_dict_item_free(di); + tv_dict_free(dict); return false; } - dict_add(dict, di); + tv_dict_add(dict, di); } dict->dv_refcount++; @@ -960,11 +957,7 @@ static void set_option_value_err(char *key, { char *errmsg; - if ((errmsg = (char *)set_option_value((uint8_t *)key, - numval, - (uint8_t *)stringval, - opt_flags))) - { + if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { if (try_end(err)) { return; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59c0200395..db2f25a2a6 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -185,7 +185,7 @@ Object nvim_eval(String expr, Error *err) typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); if (!expr_result) { - api_set_error(err, Exception, _("Failed to evaluate expression")); + api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 44ff540b40..31851a84e6 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -8,9 +8,11 @@ // Definitions of various common control characters. -#define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') -#define CharOrdLow(x) ((x) - 'a') -#define CharOrdUp(x) ((x) - 'A') +#define CharOrd(x) ((uint8_t)(x) < 'a' \ + ? (uint8_t)(x) - 'A'\ + : (uint8_t)(x) - 'a') +#define CharOrdLow(x) ((uint8_t)(x) - 'a') +#define CharOrdUp(x) ((uint8_t)(x) - 'A') #define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) #define NUL '\000' diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f7333fead4..0ce9ce073e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -661,7 +661,7 @@ static void free_buffer(buf_T *buf) free_buffer_stuff(buf, true); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); - dict_unref(buf->additional_data); + tv_dict_unref(buf->additional_data); clear_fmark(&buf->b_last_cursor); clear_fmark(&buf->b_last_insert); clear_fmark(&buf->b_last_change); @@ -1481,7 +1481,7 @@ static inline void buf_init_changedtick(buf_T *const buf) }, .di_key = "changedtick", }; - dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); + tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); } /// Add a file name to the buffer list. @@ -1573,7 +1573,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) if (buf != curbuf || curbuf == NULL) { buf = xcalloc(1, sizeof(buf_T)); // init b: variables - buf->b_vars = dict_alloc(); + buf->b_vars = tv_dict_alloc(); init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -5443,8 +5443,8 @@ void buf_open_scratch(handle_T bufnr, char *bufname) { (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); (void)setfname(curbuf, (char_u *)bufname, NULL, true); - set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); + set_option_value("bh", 0L, "hide", OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ed3e6ab6cc..c915d373aa 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -7,6 +7,7 @@ #include "nvim/screen.h" // for StlClickRecord #include "nvim/func_attr.h" #include "nvim/eval.h" +#include "nvim/macros.h" // Values for buflist_getfile() enum getf_values { @@ -91,8 +92,8 @@ static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) { #ifndef NDEBUG - dictitem_T *const changedtick_di = dict_find( - buf->b_vars, (char_u *)"changedtick", sizeof("changedtick") - 1); + dictitem_T *const changedtick_di = tv_dict_find( + buf->b_vars, S_LEN("changedtick")); assert(changedtick_di != NULL); assert(changedtick_di->di_tv.v_type == VAR_NUMBER); assert(changedtick_di->di_tv.v_lock == VAR_FIXED); diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 8d25b828e5..c69582c4c6 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -5,6 +5,16 @@ #include "nvim/pos.h" #include "nvim/buffer_defs.h" +/// Return the folded-case equivalent of the given character +/// +/// @param[in] c Character to transform. +/// +/// @return Folded variant. +#define CH_FOLD(c) \ + utf_fold((sizeof(c) == sizeof(char)) \ + ?((int)(uint8_t)(c)) \ + :((int)(c))) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 11ade20c1c..ff76abc01f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1586,7 +1586,7 @@ static int diff_cmp(char_u *s1, char_u *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return mb_stricmp(s1, s2); + return mb_stricmp((const char *)s1, (const char *)s2); } // Ignore white space changes and possibly ignore case. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 77d5b2c816..53b196c61f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2094,38 +2094,56 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); + return ins_compl_add(IObuff, len, icase, fname, NULL, true, dir, flags, + false); } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); } -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -static int -ins_compl_add ( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup /* accept duplicate match */ -) +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] icase Whether case is to be ignored. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, + const bool icase, char_u *const fname, + char_u *const *const cptext, + const bool cptext_allocated, + const Direction cdir, int flags, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); + int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); os_breakcheck(); - if (got_int) +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if (cptext_allocated) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree(cptext[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); return FAIL; - if (len < 0) + } + if (len < 0) { len = (int)STRLEN(str); + } /* * If the same match is already present, don't add it. @@ -2133,10 +2151,12 @@ ins_compl_add ( if (compl_first_match != NULL && !adup) { match = compl_first_match; do { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) + if (!(match->cp_flags & ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); return NOTDONE; + } match = match->cp_next; } while (match != NULL && match != compl_first_match); } @@ -2167,16 +2187,26 @@ ins_compl_add ( else if (fname != NULL) { match->cp_fname = vim_strsave(fname); flags |= FREE_FNAME; - } else + } else { match->cp_fname = NULL; + } match->cp_flags = flags; if (cptext != NULL) { int i; - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } } /* @@ -2299,9 +2329,10 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ + NULL, NULL, false, dir, 0, false)) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; + } FreeWild(num_matches, matches); } @@ -2365,8 +2396,8 @@ void set_completion(colnr_T startcol, list_T *list) /* compl_pattern doesn't need to be set */ compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { return; } @@ -2888,7 +2919,7 @@ static void ins_compl_clear(void) compl_orig_text = NULL; compl_enter_selects = FALSE; // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } /// Check that Insert completion is active. @@ -3486,7 +3517,7 @@ expand_by_function ( theend: if (matchdict != NULL) { - dict_unref(matchdict); + tv_dict_unref(matchdict); } if (matchlist != NULL) { tv_list_unref(matchlist); @@ -3519,53 +3550,60 @@ static void ins_compl_add_dict(dict_T *dict) dictitem_T *di_refresh; dictitem_T *di_words; - /* Check for optional "refresh" item. */ - compl_opt_refresh_always = FALSE; - di_refresh = dict_find(dict, (char_u *)"refresh", 7); + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - char_u *v = di_refresh->di_tv.vval.v_string; + const char *v = (const char *)di_refresh->di_tv.vval.v_string; - if (v != NULL && STRCMP(v, (char_u *)"always") == 0) - compl_opt_refresh_always = TRUE; + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } } - /* Add completions from a "words" list. */ - di_words = dict_find(dict, (char_u *)"words", 5); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { ins_compl_add_list(di_words->di_tv.vval.v_list); + } } -/* - * Add a match to the list of matches from a typeval_T. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -int ins_compl_add_tv(typval_T *tv, int dir) -{ - char_u *word; - int icase = FALSE; - int adup = FALSE; - int aempty = FALSE; - char_u *(cptext[CPT_COUNT]); +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +int ins_compl_add_tv(typval_T *const tv, const Direction dir) + FUNC_ATTR_NONNULL_ALL +{ + const char *word; + bool icase = false; + bool adup = false; + bool aempty = false; + char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = get_dict_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = get_dict_string(tv->vval.v_dict, "abbr", false); - cptext[CPT_MENU] = get_dict_string(tv->vval.v_dict, "menu", false); - cptext[CPT_KIND] = get_dict_string(tv->vval.v_dict, "kind", false); - cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, "info", false); - - icase = get_dict_number(tv->vval.v_dict, "icase"); - adup = get_dict_number(tv->vval.v_dict, "dup"); - aempty = get_dict_number(tv->vval.v_dict, "empty"); + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + + icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); + adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = get_tv_string_chk(tv); + word = (const char *)get_tv_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } - if (word == NULL || (!aempty && *word == NUL)) + if (word == NULL || (!aempty && *word == NUL)) { return FAIL; - return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); + } + return ins_compl_add((char_u *)word, -1, icase, NULL, + (char_u **)cptext, true, dir, 0, adup); } /* @@ -3980,7 +4018,7 @@ static void ins_compl_delete(void) // causes flicker, thus we can't do that. changed_cline_bef_curs(); // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } // Insert the new text being completed. @@ -3995,7 +4033,7 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict_add_nr_str(dict, "word", 0L, EMPTY_IF_NULL(compl_shown_match->cp_str)); dict_add_nr_str(dict, "abbr", 0L, @@ -4667,8 +4705,8 @@ static int ins_complete(int c, bool enable_pum) /* Always add completion for the original text. */ xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 03d6eef9e3..5015deead7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -97,6 +97,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/executor.h" #include "nvim/eval/gc.h" +#include "nvim/macros.h" // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -432,21 +433,6 @@ static ScopeDictDictItem vimvars_var; /// v: hashtab #define vimvarht vimvardict.dv_hashtab -typedef enum { - kCallbackNone, - kCallbackFuncref, - kCallbackPartial, -} CallbackType; - -typedef struct { - union { - char_u *funcref; - partial_T *partial; - } data; - CallbackType type; -} Callback; -#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) - typedef struct { union { LibuvProcess uv; @@ -464,13 +450,6 @@ typedef struct { MultiQueue *events; } TerminalJobData; -typedef struct dict_watcher { - Callback callback; - char *key_pattern; - QUEUE node; - bool busy; // prevent recursion if the dict is changed in the callback -} DictWatcher; - typedef struct { TerminalJobData *data; Callback *callback; @@ -596,19 +575,19 @@ void eval_init(void) } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - dict_T *const msgpack_types_dict = dict_alloc(); + dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; - dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; eval_msgpack_type_lists[i] = type_list; - if (dict_add(msgpack_types_dict, di) == FAIL) { + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); } @@ -616,9 +595,9 @@ void eval_init(void) msgpack_types_dict->dv_lock = VAR_FIXED; set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); - dict_T *v_event = dict_alloc(); + dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, tv_list_alloc()); @@ -745,7 +724,7 @@ void set_internal_string_var(char_u *name, char_u *value) tvp->v_type = VAR_STRING; tvp->vval.v_string = val; - set_var(name, tvp, FALSE); + set_var((const char *)name, tvp, false); free_tv(tvp); } @@ -1689,7 +1668,7 @@ static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (empty || di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string != NULL) { list_one_var(di, prefix, first); @@ -1951,7 +1930,7 @@ ex_let_one ( } } if (s != NULL) { - set_option_value(arg, n, s, opt_flags); + set_option_value((const char *)arg, n, (char *)s, opt_flags); arg_end = (char_u *)p; } *p = c1; @@ -2220,7 +2199,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = dict_find(lp->ll_dict, key, len); + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); /* When assigning to a scope dictionary check that a function and * variable name is valid (only variable name unless it is l: or @@ -2232,16 +2211,19 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len != -1) { prevval = key[len]; key[len] = NUL; - } else - prevval = 0; /* avoid compiler warning */ - wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC - && var_check_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key); - if (len != -1) + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && rettv->v_type == VAR_FUNC + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { key[len] = prevval; - if (wrong) + } + if (wrong) { return NULL; + } } if (lp->ll_di == NULL) { @@ -2391,12 +2373,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var(lp->ll_name, &tv, false); + set_var((const char *)lp->ll_name, &tv, false); } tv_clear(&tv); } } else { - set_var(lp->ll_name, rettv, copy); + set_var((const char *)lp->ll_name, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2450,26 +2432,20 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch : lp->ll_n1 != lp->ll_n2) EMSG(_("E711: List value has not enough items")); } else { - typval_T oldtv; + typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + bool watched = tv_dict_is_watched(dict); - /* - * Assign to a List or Dictionary item. - */ + // Assign to a List or Dictionary item. if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') { EMSG2(_(e_letwrong), op); return; } - /* Need to add an item to the Dictionary. */ - di = dictitem_alloc(lp->ll_newkey); - if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { xfree(di); return; } @@ -2493,16 +2469,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; - init_tv(rettv); + tv_init(rettv); } notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); + tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; - dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } } @@ -2806,7 +2782,7 @@ void ex_call(exarg_T *eap) } end: - dict_unref(fudi.fd_dict); + tv_dict_unref(fudi.fd_dict); xfree(tofree); } @@ -2944,7 +2920,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) // unlet a Dictionary item. dict_T *d = lp->ll_dict; dictitem_T *di = lp->ll_di; - bool watched = is_watched(d); + bool watched = tv_dict_is_watched(d); char *key = NULL; typval_T oldtv; @@ -2954,10 +2930,10 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) key = xstrdup((char *)di->di_key); } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); if (watched) { - dictwatcher_notify(d, key, NULL, &oldtv); + tv_dict_watcher_notify(d, key, NULL, &oldtv); tv_clear(&oldtv); xfree(key); } @@ -3005,7 +2981,7 @@ int do_unlet(char_u *name, int forceit) hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3018,7 +2994,7 @@ int do_unlet(char_u *name, int forceit) } typval_T oldtv; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); if (watched) { copy_tv(&di->di_tv, &oldtv); @@ -3027,7 +3003,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3102,18 +3078,12 @@ static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, */ void del_menutrans_vars(void) { - hashitem_T *hi; - int todo; - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); } - } + }); hash_unlock(&globvarht); } @@ -3659,11 +3629,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) tv_clear(&var2); return FAIL; } else { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict, + ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL @@ -3749,11 +3720,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { s1 = get_tv_string_buf(rettv, buf1); s2 = get_tv_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2); - else + if (type != TYPE_MATCH && type != TYPE_NOMATCH) { + i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + } else { i = 0; - n1 = FALSE; + } + n1 = false; switch (type) { case TYPE_EQUAL: n1 = (i == 0); break; case TYPE_NEQUAL: n1 = (i != 0); break; @@ -4355,11 +4327,11 @@ eval_index ( int verbose /* give error messages */ ) { - int empty1 = FALSE, empty2 = FALSE; - typval_T var1, var2; + bool empty1 = false; + bool empty2 = false; long n1, n2 = 0; long len = -1; - int range = FALSE; + int range = false; char_u *s; char_u *key = NULL; @@ -4397,8 +4369,8 @@ eval_index ( } } - init_tv(&var1); - init_tv(&var2); + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; if (**arg == '.') { /* * dict.name @@ -4576,7 +4548,7 @@ eval_index ( } } - item = dict_find(rettv->vval.v_dict, key, (int)len); + item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4868,7 +4840,7 @@ static void partial_free(partial_T *pt) tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); - dict_unref(pt->pt_dict); + tv_dict_unref(pt->pt_dict); if (pt->pt_name != NULL) { func_unref(pt->pt_name); xfree(pt->pt_name); @@ -4940,64 +4912,7 @@ failret: return OK; } - -// TODO(ZyX-I): move to eval/typval - -/* - * Return the dictitem that an entry in a hashtable points to. - */ -dictitem_T *dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -// TODO(ZyX-I): move to eval/typval - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ -static int -dict_equal ( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL && d2 == NULL) { - return true; - } - if (d1 == NULL || d2 == NULL) { - return false; - } - if (d1 == d2) { - return true; - } - if (dict_len(d1) != dict_len(d2)) { - return false; - } - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - -static int tv_equal_recurse_limit; - -static bool func_equal( +bool func_equal( typval_T *tv1, typval_T *tv2, bool ic // ignore case @@ -5032,7 +4947,7 @@ static bool func_equal( if (d1 != d2) { return false; } - } else if (!dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic, true)) { return false; } @@ -5051,90 +4966,6 @@ static bool func_equal( return true; } -/* - * Return TRUE if "tv1" and "tv2" have the same value. - * Compares the items just like "==" would compare them, but strings and - * numbers are different. Floats and numbers are also different. - */ -int tv_equal( - typval_T *tv1, - typval_T *tv2, - int ic, /* ignore case */ - int recursive /* TRUE when used recursively */ -) -{ - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *s1, *s2; - static int recursive_cnt = 0; /* catch recursive loops */ - int r; - - /* Catch lists and dicts that have an endless loop by limiting - * recursiveness to a limit. We guess they are equal then. - * A fixed limit has the problem of still taking an awful long time. - * Reduce the limit every time running into it. That should work fine for - * deeply linked structures that are not recursively linked and catch - * recursiveness quickly. */ - if (!recursive) - tv_equal_recurse_limit = 1000; - if (recursive_cnt >= tv_equal_recurse_limit) { - --tv_equal_recurse_limit; - return TRUE; - } - - // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and - // arguments. - if ((tv1->v_type == VAR_FUNC - || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL)) - && (tv2->v_type == VAR_FUNC - || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) { - recursive_cnt++; - r = func_equal(tv1, tv2, ic); - recursive_cnt--; - return r; - } - if (tv1->v_type != tv2->v_type) { - return false; - } - - switch (tv1->v_type) { - case VAR_LIST: - recursive_cnt++; - r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); - recursive_cnt--; - return r; - - case VAR_DICT: - ++recursive_cnt; - r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - /// Get next (unique) copy ID /// /// Used for traversing nested structures e.g. when serializing them or garbage @@ -5400,7 +5231,7 @@ static int free_unref_items(int copyID) // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list // of dicts or list of lists. - dict_free_contents(dd); + tv_dict_free_contents(dd); did_free = true; } } @@ -5423,7 +5254,7 @@ static int free_unref_items(int copyID) for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - dict_free_dict(dd); + tv_dict_free_dict(dd); } } @@ -5460,14 +5291,10 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) // Mark each item in the hashtab. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. - int todo = (int)cur_ht->ht_used; - for (hashitem_T *hi = cur_ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, &ht_stack, - list_stack); - } - } + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item( + &TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); } if (ht_stack == NULL) { @@ -5559,7 +5386,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, QUEUE *w = NULL; DictWatcher *watcher = NULL; QUEUE_FOREACH(w, &dd->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); } } @@ -5704,265 +5531,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/// Allocate an empty header for a dictionary. -dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET -{ - dict_T *d = xmalloc(sizeof(dict_T)); - - // Add the dict to the list of dicts for garbage collection. - if (gc_first_dict != NULL) { - gc_first_dict->dv_used_prev = d; - } - d->dv_used_next = gc_first_dict; - d->dv_used_prev = NULL; - gc_first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = VAR_UNLOCKED; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - QUEUE_INIT(&d->watchers); - - return d; -} - -/* - * Allocate an empty dict for a return value. - */ -static void rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - rettv->v_lock = VAR_UNLOCKED; - d->dv_refcount++; -} - -/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -/// -/// @param d The Dictionary to clear -void dict_clear(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - - size_t todo = d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - dictitem_free(HI2DI(hi)); - hash_remove(&d->dv_hashtab, hi); - todo--; - } - } - - hash_unlock(&d->dv_hashtab); -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ -void dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) { - dict_free(d); - } -} - -/// Free a Dictionary, including all items it contains. -/// Ignores the reference count. -static void dict_free_contents(dict_T *const d) - FUNC_ATTR_NONNULL_ALL -{ - // Lock the hashtab, we don't want it to resize while freeing items. - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - size_t todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - // Remove the item before deleting it, just in case there is - // something recursive causing trouble. - dictitem_T *const di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - dictitem_free(di); - todo--; - } - } - - while (!QUEUE_EMPTY(&d->watchers)) { - QUEUE *w = QUEUE_HEAD(&d->watchers); - DictWatcher *watcher = dictwatcher_node_data(w); - QUEUE_REMOVE(w); - dictwatcher_free(watcher); - } - - hash_clear(&d->dv_hashtab); -} - -static void dict_free_dict(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - // Remove the dict from the list of dicts for garbage collection. - if (d->dv_used_prev == NULL) { - gc_first_dict = d->dv_used_next; - } else { - d->dv_used_prev->dv_used_next = d->dv_used_next; - } - if (d->dv_used_next != NULL) { - d->dv_used_next->dv_used_prev = d->dv_used_prev; - } - - xfree(d); -} - -void dict_free(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - if (!tv_in_free_unref_items) { - dict_free_contents(d); - dict_free_dict(d); - } -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - */ -dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); -#ifndef __clang_analyzer__ - STRCPY(di->di_key, key); -#endif - di->di_flags = DI_FLAGS_ALLOC; - return di; -} - -/* - * Make a copy of a Dictionary item. - */ -static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); - - STRCPY(di->di_key, org->di_key); - di->di_flags = DI_FLAGS_ALLOC; - copy_tv(&org->di_tv, &di->di_tv); - - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ -static void dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) { - EMSG2(_(e_intern2), "dictitem_remove()"); - } else { - hash_remove(&dict->dv_hashtab, hi); - } - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ -void dictitem_free(dictitem_T *item) -{ - tv_clear(&item->di_tv); - if (item->di_flags & DI_FLAGS_ALLOC) { - xfree(item); - } -} - -/// Make a copy of dictionary -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original dictionary to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied dictionary. May be NULL in case original dictionary is NULL -/// or some failure happens. The refcount of the new dictionary is set -/// to 1. -static dict_T *dict_copy(const vimconv_T *const conv, - dict_T *const orig, - const bool deep, - const int copyID) -{ - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - dict_T *copy = dict_alloc(); - { - if (copyID != 0) { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - if (conv == NULL || conv->vc_type == CONV_NONE) { - di = dictitem_alloc(hi->hi_key); - } else { - char *const key = (char *) string_convert((vimconv_T *) conv, - hi->hi_key, NULL); - if (key == NULL) { - di = dictitem_alloc(hi->hi_key); - } else { - di = dictitem_alloc((char_u *) key); - xfree(key); - } - } - if (deep) { - if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) { - xfree(di); - break; - } - } else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - /* * Add a number or string entry to dictionary "d". * When "str" is NULL use number "nr", otherwise use "str". @@ -5972,7 +5540,7 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) { dictitem_T *item; - item = dictitem_alloc((char_u *)key); + item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; if (str == NULL) { item->di_tv.v_type = VAR_NUMBER; @@ -5981,8 +5549,8 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) item->di_tv.v_type = VAR_STRING; item->di_tv.vval.v_string = vim_strsave(str); } - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } return OK; @@ -5994,13 +5562,13 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) */ int dict_add_list(dict_T *d, char *key, list_T *list) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } ++list->lv_refcount; @@ -6011,13 +5579,13 @@ int dict_add_list(dict_T *d, char *key, list_T *list) /// Returns FAIL when out of memory and when key already exists. int dict_add_dict(dict_T *d, char *key, dict_T *dict) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *const item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } dict->dv_refcount++; @@ -6029,60 +5597,12 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) /// This does not protect against adding new keys to the Dictionary. /// /// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *dict) +void dict_set_keys_readonly(dict_T *const dict) FUNC_ATTR_NONNULL_ALL { - size_t todo = dict->dv_hashtab.ht_used; - for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - todo--; - HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - } -} - -/* - * Get the number of items in a Dictionary. - */ -static long dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ -dictitem_T *dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (d == NULL) { - return NULL; - } - if (len < 0) { - akey = key; - } else if (len >= AKEYLEN) { - tofree = akey = vim_strnsave(key, len); - } else { - /* Avoid a malloc/free by using buf[]. */ - STRLCPY(buf, key, len + 1); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - xfree(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); } /// Get a function from a dictionary @@ -6091,7 +5611,7 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len) /// @return true/false on success/failure. static bool get_dict_callback(dict_T *d, char *key, Callback *result) { - dictitem_T *di = dict_find(d, (uint8_t *)key, -1); + dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { result->type = kCallbackNone; @@ -6113,40 +5633,6 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) return res; } -/// Get a string item from a dictionary. -/// -/// @param save whether memory should be allocated for the return value -/// when false a shared buffer is used, can only be used once! -/// -/// @return the entry or NULL if the entry doesn't exist. -char_u *get_dict_string(dict_T *d, char *key, bool save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return NULL; - } - s = get_tv_string(&di->di_tv); - if (save) { - s = vim_strsave(s); - } - return s; -} - -/// Get a number item from a dictionary. -/// -/// @return the entry or 0 if the entry doesn't exist. -long get_dict_number(dict_T *d, char *key) -{ - dictitem_T *di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return 0; - } - return get_tv_number(&di->di_tv); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -6176,7 +5662,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) } if (evaluate) { - d = dict_alloc(); + d = tv_dict_alloc(); } tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; @@ -6207,19 +5693,19 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - item = dict_find(d, key, -1); + item = tv_dict_find(d, (const char *)key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); tv_clear(&tvkey); tv_clear(&tv); goto failret; } - item = dictitem_alloc(key); + item = tv_dict_item_alloc((const char *)key); tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); } } @@ -6236,7 +5722,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); failret: if (evaluate) { - dict_free(d); + tv_dict_free(d); } return FAIL; } @@ -8152,9 +7638,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) todo = error ? 0 : (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; + todo--; + if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) { + n++; + } } } } @@ -8381,7 +7868,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) DictWatcher *watcher = NULL; bool matched = false; QUEUE_FOREACH(w, &dict->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); if (callback_equal(&watcher->callback, &callback) && !strcmp(watcher->key_pattern, key_pattern)) { matched = true; @@ -8397,7 +7884,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } QUEUE_REMOVE(w); - dictwatcher_free(watcher); + tv_dict_watcher_free(watcher); } /* @@ -8750,92 +8237,24 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN && get_tv_number_chk(&argvars[1], &error)) options |= WILD_KEEP_ALL; - if (!error) { - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) - options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); - } else { - tv_list_alloc_ret(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); - for (int i = 0; i < xpc.xp_numfiles; i++) { - tv_list_append_string(rettv->vval.v_list, - (const char *)xpc.xp_files[i], -1); - } - ExpandCleanup(&xpc); - } - } else - rettv->vval.v_string = NULL; - } -} - -/* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ -void dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - bool watched = is_watched(d1); - const char *const arg_errmsg = _("extend() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { - if (!HASHITEM_EMPTY(hi2)) { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) { - di1 = dictitem_copy(HI2DI(hi2)); - if (dict_add(d1, di1) == FAIL) { - dictitem_free(di1); - } - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, NULL); - } - } else if (*action == 'e') { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } else if (*action == 'f' && HI2DI(hi2) != di1) { - typval_T oldtv; - - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) - || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { - break; - } - - if (watched) { - copy_tv(&di1->di_tv, &oldtv); - } - - tv_clear(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - tv_clear(&oldtv); + if (!error) { + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + if (p_wic) + options += WILD_ICASE; + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); + ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + for (int i = 0; i < xpc.xp_numfiles; i++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } + ExpandCleanup(&xpc); } - } + } else + rettv->vval.v_string = NULL; } } @@ -8880,37 +8299,41 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1, *d2; - char_u *action; - int i; + dict_T *d1; + dict_T *d2; d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) && d2 != NULL) { - /* Check the third argument. */ + const char *action = "force"; + // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { - static char *(av[]) = {"keep", "force", "error"}; + const char *const av[] = { "keep", "force", "error" }; - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) + action = (const char *)get_tv_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { break; + } + } if (i == 3) { EMSG2(_(e_invarg2), action); return; } - } else - action = (char_u *)"force"; + } - dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); copy_tv(&argvars[0], rettv); } - } else + } else { EMSG2(_(e_listdictarg), "extend()"); + } } /* @@ -9076,7 +8499,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (!HASHITEM_EMPTY(hi)) { --todo; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (map && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) { @@ -9094,7 +8517,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { break; } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); } } } @@ -9639,9 +9062,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = dict_find(d, get_tv_string(&argvars[1]), -1); - if (di != NULL) + di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + if (di != NULL) { tv = &di->di_tv; + } } } else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) { @@ -9704,7 +9128,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *const d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); @@ -9717,7 +9141,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) /// Returns buffer options, variables and other attributes in a dictionary. static dict_T *get_buffer_info(buf_T *buf) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); dict_add_nr_str(dict, "name", 0L, @@ -9772,12 +9196,12 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; - di = dict_find(sel_d, (char_u *)"buflisted", -1); + di = tv_dict_find(sel_d, S_LEN("buflisted")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_buflisted = true; } - di = dict_find(sel_d, (char_u *)"bufloaded", -1); + di = tv_dict_find(sel_d, S_LEN("bufloaded")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_bufloaded = true; } @@ -10047,7 +9471,7 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; @@ -10467,7 +9891,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, (void)get_errorlist(wp, -1, rettv->vval.v_list); } } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (is_qf || wp != NULL) { if (what_arg->v_type == VAR_DICT) { dict_T *d = what_arg->vval.v_dict; @@ -10499,7 +9923,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); while (cur != NULL) { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { @@ -10678,7 +10102,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// as a dictionary. static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tp_idx, NULL); @@ -10776,7 +10200,7 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Returns information about a window as a dictionary. static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tpnr, NULL); dict_add_nr_str(dict, "winnr", winnr, NULL); @@ -11286,8 +10710,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].vval.v_dict == NULL) return; - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + (const char *)get_tv_string(&argvars[1]), + -1) != NULL; } /// `haslocaldir([{win}[, {tab}]])` function @@ -11912,60 +11337,48 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void dict_list(typval_T *argvars, typval_T *rettv, int what) { - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } - if ((d = argvars[0].vval.v_dict) == NULL) + dict_T *const d = argvars[0].vval.v_dict; + if (d == NULL) { return; + } tv_list_alloc_ret(rettv); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - - li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + TV_DICT_ITER(d, di, { + listitem_T *const li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - /* keys() */ - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - ++l2->lv_refcount; + if (what == 0) { + // keys() + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + } else if (what == 1) { + // values() + copy_tv(&di->di_tv, &li->li_tv); + } else { + // items() + list_T *const l2 = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_list = l2; + l2->lv_refcount++; - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = 0; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); - } + sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + copy_tv(&di->di_tv, &sub_li->li_tv); } - } + }); } /// "id()" function @@ -12230,23 +11643,26 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *job_opts = NULL; - bool detach = false, rpc = false, pty = false; - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + bool detach = false; + bool rpc = false; + bool pty = false; + Callback on_stdout = CALLBACK_NONE; + Callback on_stderr = CALLBACK_NONE; + Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - detach = get_dict_number(job_opts, "detach") != 0; - rpc = get_dict_number(job_opts, "rpc") != 0; - pty = get_dict_number(job_opts, "pty") != 0; + detach = tv_dict_get_number(job_opts, "detach") != 0; + rpc = tv_dict_get_number(job_opts, "rpc") != 0; + pty = tv_dict_get_number(job_opts, "pty") != 0; if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); return; } - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); + char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; // The new cwd must be a directory. @@ -12268,15 +11684,15 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Process *proc = (Process *)&data->proc; if (pty) { - uint16_t width = get_dict_number(job_opts, "width"); + uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { data->proc.pty.width = width; } - uint16_t height = get_dict_number(job_opts, "height"); + uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { data->proc.pty.height = height; } - char *term = (char *)get_dict_string(job_opts, "TERM", true); + char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { data->proc.pty.term_name = term; } @@ -12537,7 +11953,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: - rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); break; case VAR_UNKNOWN: case VAR_SPECIAL: @@ -12714,7 +12130,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, FALSE); } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (rhs != NULL) { // Return a dictionary. char_u *lhs = str2special_save(mp->m_keys, true); @@ -12980,10 +12396,10 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -12996,8 +12412,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)grp, + (const char *)pat, prio, id, + NULL, (const char *)conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -13005,8 +12422,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; char_u buf[NUMBUFLEN]; - char_u *group; - group = get_tv_string_buf_chk(&argvars[0], buf); + const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -13036,10 +12452,10 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -13054,8 +12470,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, + l, (const char *)conceal_char); } /* @@ -13156,8 +12572,8 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); + todo--; + i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -13845,7 +13261,6 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *li; long idx; long end; - char_u *key; dict_T *d; dictitem_T *di; const char *const arg_errmsg = _("remove() argument"); @@ -13856,18 +13271,18 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - key = get_tv_string_chk(&argvars[1]); + const char *key = (const char *)get_tv_string_chk(&argvars[1]); if (key != NULL) { - di = dict_find(d, key, -1); + di = tv_dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - if (is_watched(d)) { - dictwatcher_notify(d, (char *)key, NULL, rettv); + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); } } } @@ -14967,7 +14382,6 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buf != NULL && varname != NULL && varp != NULL) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; aco_save_T aco; @@ -14976,9 +14390,9 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { - set_option_value((char_u *)varname, numval, strval, OPT_LOCAL); + set_option_value(varname, numval, strval, OPT_LOCAL); } // reset notion of buffer @@ -14987,7 +14401,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *save_curbuf = curbuf; const size_t varname_len = STRLEN(varname); - char_u *const bufvarname = xmalloc(STRLEN(varname) + 3); + char *const bufvarname = xmalloc(varname_len + 3); curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); @@ -15002,7 +14416,6 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; dictitem_T *di; - char_u *csearch; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -15010,7 +14423,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if ((d = argvars[0].vval.v_dict) != NULL) { - csearch = get_dict_string(d, "char", false); + char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false); if (csearch != NULL) { if (enc_utf8) { int pcc[MAX_MCO]; @@ -15022,13 +14435,15 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) csearch, MB_PTR2LEN(csearch)); } - di = dict_find(d, (char_u *)"forward", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("forward")); + if (di != NULL) { set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + } - di = dict_find(d, (char_u *)"until", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("until")); + if (di != NULL) { set_csearch_until(!!get_tv_number(&di->di_tv)); + } } } @@ -15252,11 +14667,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && (dict_find(d, (char_u *)"pattern", -1) != NULL - || dict_find(d, (char_u *)"pos1", -1) != NULL) - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) { + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { EMSG(_(e_invarg)); return; } @@ -15267,11 +14682,10 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; while (li != NULL) { int i = 0; - char_u buf[5]; - dictitem_T *di; d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { if (s == NULL) { s = tv_list_alloc(); if (s == NULL) { @@ -15280,14 +14694,16 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // match from matchaddpos() - for (i = 1; i < 9; ++i) { - snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { - if (di->di_tv.v_type != VAR_LIST) { + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { return; } - tv_list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &pos_di->di_tv); s->lv_refcount++; } else { break; @@ -15295,23 +14711,31 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", true); - int priority = get_dict_number(d, "priority"); - int id = get_dict_number(d, "id"); - char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", true) - : NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in get_tv_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? (const char *)get_tv_string( + &conceal_di->di_tv) + : NULL); if (i == 0) { match_add(curwin, group, - get_dict_string(d, "pattern", false), + tv_dict_get_string(d, "pattern", false), priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); tv_list_unref(s); s = NULL; } - xfree(group); - xfree(conceal); li = li->li_next; } rettv->vval.v_number = 0; @@ -15471,35 +14895,31 @@ free_lstval: */ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *save_curtab; - tabpage_T *tp; - char_u *varname, *tabvarname; - typval_T *varp; - rettv->vval.v_number = 0; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; - - if (varname != NULL && varp != NULL - && tp != NULL - ) { - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); - - tabvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); + tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; + + if (varname != NULL && varp != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varp, true); xfree(tabvarname); - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } } } @@ -15525,44 +14945,42 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win; - win_T *save_curwin; - tabpage_T *save_curtab; - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - if (off == 1) + tabpage_T *tp = NULL; + if (off == 1) { tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + } else { tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { + win_T *save_curwin; + tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; if (!need_switch_win || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; - ++varname; + varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char_u nbuf[NUMBUFLEN]; + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); set_var(winvarname, varp, true); xfree(winvarname); } @@ -17086,15 +16504,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; - char *cwd = "."; + const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false); + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((char_u *)cwd)) { + if (!os_isdir((const char_u *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -17131,7 +16549,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) // at this point the buffer has no terminal instance associated yet, so unset // the 'swapfile' option to ensure no swap file will be created curbuf->b_p_swf = false; - (void)setfname(curbuf, (uint8_t *)buf, NULL, true); + (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), @@ -17177,23 +16595,25 @@ static bool callback_from_typval(Callback *callback, typval_T *arg) /// Unref/free callback -static void callback_free(Callback *callback) +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL { switch (callback->type) { - case kCallbackFuncref: + case kCallbackFuncref: { func_unref(callback->data.funcref); xfree(callback->data.funcref); break; - - case kCallbackPartial: + } + case kCallbackPartial: { partial_unref(callback->data.partial); break; - - case kCallbackNone: + } + case kCallbackNone: { break; - - default: + } + default: { abort(); + } } callback->type = kCallbackNone; } @@ -17220,8 +16640,9 @@ static bool callback_equal(Callback *cb1, Callback *cb2) } } -static bool callback_call(Callback *callback, int argcount_in, - typval_T *argvars_in, typval_T *rettv) +bool callback_call(Callback *const callback, const int argcount_in, + typval_T *const argvars_in, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL { partial_T *partial; char_u *name; @@ -17290,8 +16711,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); return; } - if (dict_find(dict, (char_u *)"repeat", -1) != NULL) { - repeat = get_dict_number(dict, "repeat"); + dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); + if (di != NULL) { + repeat = get_tv_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -17353,13 +16775,11 @@ static void timer_due_cb(TimeWatcher *tw, void *data) timer_stop(timer); } - typval_T argv[2]; - init_tv(argv); + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; - typval_T rettv; + typval_T rettv = TV_INITIAL_VALUE; - init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); tv_clear(&rettv); @@ -17614,7 +17034,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; list_T *list; @@ -17802,36 +17222,37 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; + dict_T *dict; if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else { - if (dict_find(dict, (char_u *)"lnum", -1) != NULL) { - curwin->w_cursor.lnum = get_dict_number(dict, "lnum"); + || (dict = argvars[0].vval.v_dict) == NULL) { + emsgf(_(e_invarg)); + } else { + dictitem_T *di; + if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { + curwin->w_cursor.lnum = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"col", -1) != NULL) { - curwin->w_cursor.col = get_dict_number(dict, "col"); + if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { + curwin->w_cursor.col = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"coladd", -1) != NULL) { - curwin->w_cursor.coladd = get_dict_number(dict, "coladd"); + if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { + curwin->w_cursor.coladd = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"curswant", -1) != NULL) { - curwin->w_curswant = get_dict_number(dict, "curswant"); + if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { + curwin->w_curswant = get_tv_number(&di->di_tv); curwin->w_set_curswant = false; } - if (dict_find(dict, (char_u *)"topline", -1) != NULL) { - set_topline(curwin, get_dict_number(dict, "topline")); + if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { + set_topline(curwin, get_tv_number(&di->di_tv)); } - if (dict_find(dict, (char_u *)"topfill", -1) != NULL) { - curwin->w_topfill = get_dict_number(dict, "topfill"); + if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { + curwin->w_topfill = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) { - curwin->w_leftcol = get_dict_number(dict, "leftcol"); + if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { + curwin->w_leftcol = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) { - curwin->w_skipcol = get_dict_number(dict, "skipcol"); + if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { + curwin->w_skipcol = get_tv_number(&di->di_tv); } check_cursor(); @@ -17854,7 +17275,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); @@ -18027,7 +17448,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); cursor_pos_info(rettv->vval.v_dict); } @@ -18895,10 +18316,10 @@ handle_subscript( } ret = FAIL; } - dict_unref(selfdict); + tv_dict_unref(selfdict); selfdict = NULL; - } else { /* **arg == '[' || **arg == '.' */ - dict_unref(selfdict); + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { selfdict = rettv->vval.v_dict; if (selfdict != NULL) @@ -18919,7 +18340,7 @@ handle_subscript( set_selfdict(rettv, selfdict); } - dict_unref(selfdict); + tv_dict_unref(selfdict); return ret; } @@ -19016,7 +18437,7 @@ void free_tv(typval_T *varp) tv_list_unref(varp->vval.v_list); break; case VAR_DICT: - dict_unref(varp->vval.v_dict); + tv_dict_unref(varp->vval.v_dict); break; case VAR_SPECIAL: case VAR_NUMBER: @@ -19028,15 +18449,6 @@ void free_tv(typval_T *varp) } } -/* - * Set the value of a variable to NULL without freeing items. - */ -static void init_tv(typval_T *varp) -{ - if (varp != NULL) - memset(varp, 0, sizeof(typval_T)); -} - // TODO(ZyX-I): move to eval/typval /// Get the number value of a variable @@ -19057,44 +18469,53 @@ varnumber_T get_tv_number(const typval_T *const varp) varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { - long n = 0L; + varnumber_T n = 0; switch (varp->v_type) { - case VAR_NUMBER: - return (long)(varp->vval.v_number); - case VAR_FLOAT: - EMSG(_("E805: Using a Float as a Number")); - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E703: Using a Funcref as a Number")); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) { - vim_str2nr(varp->vval.v_string, NULL, NULL, - STR2NR_ALL, &n, NULL, 0); + case VAR_NUMBER: { + return varp->vval.v_number; } - return n; - case VAR_LIST: - EMSG(_("E745: Using a List as a Number")); - break; - case VAR_DICT: - EMSG(_("E728: Using a Dictionary as a Number")); - break; - case VAR_SPECIAL: - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; + case VAR_FLOAT: { + EMSG(_("E805: Using a Float as a Number")); + break; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Using a Funcref as a Number")); + break; + } + case VAR_STRING: { + if (varp->vval.v_string != NULL) { + long nr; + vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; + return n; + } + case VAR_LIST: { + EMSG(_("E745: Using a List as a Number")); + break; + } + case VAR_DICT: { + EMSG(_("E728: Using a Dictionary as a Number")); + break; + } + case VAR_SPECIAL: { + switch (varp->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); + break; } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; } if (denote == NULL) { // useful for values that must be unsigned @@ -19254,16 +18675,17 @@ static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { const char *varname; - hashtab_T *ht = find_var_ht(name, name_len, &varname); + hashtab_T *const ht = find_var_ht(name, name_len, &varname); if (htp != NULL) { *htp = ht; } if (ht == NULL) { return NULL; } - dictitem_T *ret = find_var_in_ht(ht, *name, - varname, name_len - (size_t)(varname - name), - no_autoload || htp != NULL); + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); if (ret != NULL) { return ret; } @@ -19327,7 +18749,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, return NULL; } } - return HI2DI(hi); + return TV_DICT_HI2DI(hi); } // Get function call environment based on backtrace debug level @@ -19528,7 +18950,7 @@ void unref_var_dict(dict_T *dict) /* Now the dict needs to be freed if no one else is using it, go back to * normal reference counting. */ dict->dv_refcount -= DO_NOT_FREE_CNT - 1; - dict_unref(dict); + tv_dict_unref(dict); } /* @@ -19559,7 +18981,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // Free the variable. Don't remove it from the hashtab, // ht_array might change then. hash_clear() takes care of it // later. - v = HI2DI(hi); + v = TV_DICT_HI2DI(hi); if (free_val) { tv_clear(&v->di_tv); } @@ -19578,7 +19000,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) */ static void delete_var(hashtab_T *ht, hashitem_T *hi) { - dictitem_T *di = HI2DI(hi); + dictitem_T *di = TV_DICT_HI2DI(hi); hash_remove(ht, hi); tv_clear(&di->di_tv); @@ -19637,40 +19059,31 @@ static void list_one_var_a(const char *prefix, const char *name, } } -/* - * Set variable "name" to value in "tv". - * If the variable already exists, the value is updated. - * Otherwise the variable is created. - */ -static void -set_var ( - char_u *name, - typval_T *tv, - int copy /* make copy of value in "tv" */ -) +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +static void set_var(const char *name, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; - typval_T oldtv; dict_T *dict; - const size_t name_len = STRLEN(name); - char_u *varname; - ht = find_var_ht_dict((const char *)name, name_len, (const char **)&varname, - &dict); - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + const size_t name_len = strlen(name); + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); if (ht == NULL || *varname == NUL) { EMSG2(_(e_illvar), name); return; } - v = find_var_in_ht(ht, 0, - (const char *)varname, name_len - (size_t)(varname - name), - true); + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); // Search in parent scope which is possible to reference from lambda if (v == NULL) { @@ -19678,10 +19091,11 @@ set_var ( } if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && var_check_func_name(name, v == NULL)) { + && !var_check_func_name(name, v == NULL)) { return; } + typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value if (var_check_ro(v->di_flags, (const char *)name, name_len) @@ -19704,9 +19118,9 @@ set_var ( return; } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); - if (STRCMP(varname, "searchforward") == 0) + if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - else if (STRCMP(varname, "hlsearch") == 0) { + } else if (strcmp(varname, "hlsearch") == 0) { no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } @@ -19727,13 +19141,14 @@ set_var ( return; } - /* Make sure the variable name is valid. */ - if (!valid_varname(varname)) + // Make sure the variable name is valid. + if (!valid_varname(varname)) { return; + } - v = xmalloc(sizeof(dictitem_T) + STRLEN(varname)); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); - if (hash_add(ht, DI2HIKEY(v)) == FAIL) { + if (tv_dict_add(dict, v) == FAIL) { xfree(v); return; } @@ -19745,14 +19160,14 @@ set_var ( } else { v->di_tv = *tv; v->di_tv.v_lock = 0; - init_tv(tv); + tv_init(tv); } if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); tv_clear(&oldtv); } } @@ -19768,8 +19183,8 @@ set_var ( /// /// @return True if variable is read-only: either always or in sandbox when /// sandbox is enabled, false otherwise. -static bool var_check_ro(const int flags, const char *const name, - const size_t name_len) +bool var_check_ro(const int flags, const char *const name, + const size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (flags & DI_FLAGS_RO) { @@ -19804,22 +19219,24 @@ static bool var_check_fixed(const int flags, const char *const name, return false; } -/* - * Check if a funcref is assigned to a valid variable name. - * Return TRUE and give an error if not. - */ -static int -var_check_func_name ( - char_u *name, /* points to start of variable name */ - int new_var /* TRUE when creating the variable */ -) +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])) { EMSG2(_("E704: Funcref variable name must start with a capital: %s"), name); - return TRUE; + return false; } // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked @@ -19827,63 +19244,30 @@ var_check_func_name ( if (new_var && function_exists((const char *)name, false)) { EMSG2(_("E705: Variable name conflicts with existing function: %s"), name); - return true; + return false; } - return false; + return true; } -/* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ -static int valid_varname(char_u *varname) -{ - char_u *p; - - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !ascii_isdigit(*p)) - && *p != AUTOLOAD_CHAR) { - EMSG2(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} +// TODO(ZyX-I): move to eval/expressions -/// Check whether typval is locked -/// -/// Also gives an error message. +/// Check if a variable name is valid /// -/// @param[in] lock Lock status. -/// @param[in] name Value name, for use in error message. -/// @param[in] name_len Value name length. +/// @param[in] varname Variable name to check. /// -/// @return True if value is locked. -static bool tv_check_lock(const VarLockStatus lock, - const char *const name, - const size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - const char *error_message = NULL; - switch (lock) { - case VAR_UNLOCKED: { + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + emsgf(_(e_illvar), varname); return false; } - case VAR_LOCKED: { - error_message = N_("E741: Value is locked: %.*s"); - break; - } - case VAR_FIXED: { - error_message = N_("E742: Cannot change value of %.*s"); - break; - } } - assert(error_message != NULL); - - const char *const unknown_name = _("Unknown"); - - emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), - (name != NULL ? name : unknown_name)); - return true; } @@ -20016,7 +19400,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_dict = from->vval.v_dict->dv_copydict; ++to->vval.v_dict->dv_refcount; } else { - to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID); + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } if (to->vval.v_dict == NULL) ret = FAIL; @@ -20730,9 +20114,9 @@ void ex_function(exarg_T *eap) if (fudi.fd_dict != NULL) { if (fudi.fd_di == NULL) { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { + // Add new dict entry + fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey); + if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { xfree(fudi.fd_di); xfree(fp); goto erret; @@ -21002,7 +20386,7 @@ theend: */ static int eval_fname_script(const char *const p) { - // Use mb_stricmp() because in Turkish comparing the "I" may not work with + // Use mb_strnicmp() because in Turkish comparing the "I" may not work with // the standard library function. if (p[0] == '<' && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0 @@ -21558,9 +20942,9 @@ void ex_delfunction(exarg_T *eap) } if (fudi.fd_dict != NULL) { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); + // Delete the dict item that refers to the function, it will + // invoke func_unref() and possibly delete the function. + tv_dict_item_remove(fudi.fd_dict, fudi.fd_di); } else { // A normal function (not a numbered function or lambda) has a // refcount of 1 for the entry in the hashtable. When deleting @@ -21675,6 +21059,11 @@ void func_unref(char_u *name) /// Unreference a Function: decrement the reference count and free it when it /// becomes zero. +/// Unreference user function, freeing it if needed +/// +/// Decrements the reference count and frees when it becomes zero. +/// +/// @param fp Function to unreference. void func_ptr_unref(ufunc_T *fp) { if (fp != NULL && --fp->uf_refcount <= 0) { @@ -21712,17 +21101,19 @@ void func_ptr_ref(ufunc_T *fp) } } -/// Call a user function. -static void -call_user_func( - ufunc_T *fp, // pointer to function - int argcount, // nr of args - typval_T *argvars, // arguments - typval_T *rettv, // return value - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - dict_T *selfdict // Dictionary for "self" -) +/// Call a user function +/// +/// @param fp Function to call. +/// @param[in] argcount Number of arguments. +/// @param argvars Arguments. +/// @param[out] rettv Return value. +/// @param[in] firstline First line of range. +/// @param[in] lastline Last line of range. +/// @param selfdict Dictionary for "self" for dictionary functions. +void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, + typval_T *rettv, linenr_T firstline, linenr_T lastline, + dict_T *selfdict) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) { char_u *save_sourcing_name; linenr_T save_sourcing_lnum; @@ -21796,7 +21187,7 @@ call_user_func( STRCPY(name, "self"); #endif v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; @@ -21819,7 +21210,7 @@ call_user_func( STRCPY(name, "000"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; @@ -21867,9 +21258,9 @@ call_user_func( // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. copy_tv(&v->di_tv, &v->di_tv); - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); } else { - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { @@ -22065,29 +21456,22 @@ call_user_func( && fc->fc_refcount <= 0) { free_funccal(fc, false); } else { - hashitem_T *hi; - listitem_T *li; - int todo; - // "fc" is still in use. This can happen when returning "a:000", // assigning "l:" to a global variable or defining a closure. // Link "fc" in the list for garbage collection later. fc->caller = previous_funccal; previous_funccal = fc; - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } + // Make a copy of the a: variables, since we didn't do that above. + TV_DICT_ITER(&fc->l_avars, di, { + copy_tv(&di->di_tv, &di->di_tv); + }); - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + // Make a copy of the a:000 items, since we didn't do that above. + for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; + li = li->li_next) { copy_tv(&li->li_tv, &li->li_tv); + } } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -22194,7 +21578,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) STRCPY(v->di_key, name); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); + tv_dict_add(dp, v); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -22590,7 +21974,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = globvarht.ht_array; while ((size_t) (hi - hifirst) < hinum && (HASHITEM_EMPTY(hi) - || var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) { + || var_flavour(hi->hi_key) != VAR_FLAVOUR_SHADA)) { hi++; } if ((size_t) (hi - hifirst) == hinum) { @@ -22599,11 +21983,10 @@ const void *var_shada_iter(const void *const iter, const char **const name, } else { hi = (const hashitem_T *) iter; } - *name = (char *) HI2DI(hi)->di_key; - copy_tv(&(HI2DI(hi)->di_tv), rettv); - while ((size_t) (++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) - && var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) { + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; } } @@ -22614,62 +21997,54 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var((char_u *) name, &vartv, false); + set_var(name, &vartv, false); current_funccal = saved_current_funccal; } int store_session_globals(FILE *fd) { - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char_u *p, *t; - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - /* Escape special characters with a backslash. Turn a LF and - * CR into \n and \r. */ - p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); - for (t = p; *t != NUL; ++t) - if (*t == '\n') - *t = 'n'; - else if (*t == '\r') - *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ', - p, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ') < 0) - || put_eol(fd) == FAIL) { - xfree(p); - return FAIL; + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), + (char_u *)"\\\"\n\r"); + for (char_u *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { xfree(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; - - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", - this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) - return FAIL; + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; } } - } + }); return OK; } @@ -23094,7 +22469,7 @@ static inline TerminalJobData *common_job_init(char **argv, bool pty, bool rpc, bool detach, - char *cwd) + const char *cwd) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -23384,8 +22759,7 @@ static void on_job_event(JobEvent *ev) argv[2].v_lock = 0; argv[2].vval.v_string = (uint8_t *)ev->type; - typval_T rettv; - init_tv(&rettv); + typval_T rettv = TV_INITIAL_VALUE; callback_call(ev->callback, 3, argv, &rettv); tv_clear(&rettv); } @@ -23499,91 +22873,3 @@ bool eval_has_provider(char *name) return false; } - -// Compute the `DictWatcher` address from a QUEUE node. This only exists for -// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic). -static DictWatcher *dictwatcher_node_data(QUEUE *q) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - return QUEUE_DATA(q, DictWatcher, node); -} - -// Send a change notification to all `dict` watchers that match `key`. -static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, - typval_T *oldtv) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) -{ - typval_T argv[4]; - for (size_t i = 0; i < ARRAY_SIZE(argv); i++) { - init_tv(argv + i); - } - - argv[0].v_type = VAR_DICT; - argv[0].vval.v_dict = dict; - argv[1].v_type = VAR_STRING; - argv[1].vval.v_string = (char_u *)xstrdup(key); - argv[2].v_type = VAR_DICT; - argv[2].vval.v_dict = dict_alloc(); - argv[2].vval.v_dict->dv_refcount++; - - if (newtv) { - dictitem_T *v = dictitem_alloc((char_u *)"new"); - copy_tv(newtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - if (oldtv) { - dictitem_T *v = dictitem_alloc((char_u *)"old"); - copy_tv(oldtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - typval_T rettv; - - QUEUE *w; - QUEUE_FOREACH(w, &dict->watchers) { - DictWatcher *watcher = dictwatcher_node_data(w); - if (!watcher->busy && dictwatcher_matches(watcher, key)) { - init_tv(&rettv); - watcher->busy = true; - callback_call(&watcher->callback, 3, argv, &rettv); - watcher->busy = false; - tv_clear(&rettv); - } - } - - for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - tv_clear(argv + i); - } -} - -// Test if `key` matches with with `watcher->key_pattern` -static bool dictwatcher_matches(DictWatcher *watcher, const char *key) - FUNC_ATTR_NONNULL_ALL -{ - // For now only allow very simple globbing in key patterns: a '*' at the end - // of the string means it should match everything up to the '*' instead of the - // whole string. - char *nul = strchr(watcher->key_pattern, NUL); - size_t len = nul - watcher->key_pattern; - if (*(nul - 1) == '*') { - return !strncmp(key, watcher->key_pattern, len - 1); - } else { - return !strcmp(key, watcher->key_pattern); - } -} - -// Perform all necessary cleanup for a `DictWatcher` instance. -static void dictwatcher_free(DictWatcher *watcher) - FUNC_ATTR_NONNULL_ALL -{ - callback_free(&watcher->callback); - xfree(watcher->key_pattern); - xfree(watcher); -} - -// Check if `d` has at least one watcher. -static bool is_watched(dict_T *d) -{ - return d && !QUEUE_EMPTY(&d->watchers); -} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 57fee5c5a2..7f6fd76c46 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,21 +1,23 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include "nvim/profile.h" #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/garray.h" // For garray_T #include "nvim/buffer_defs.h" // For scid_T #include "nvim/ex_cmds_defs.h" // For exarg_T +#include "nvim/eval/typval.h" +#include "nvim/profile.h" +#include "nvim/garray.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. extern hashtab_T func_hashtab; + // From user function to hashitem and back. EXTERN ufunc_T dumuf; #define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) +#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) /// Defines for Vim variables diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 1d30f51f55..3cb68e093b 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -6,6 +6,7 @@ #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" +#include "nvim/macros.h" #include "nvim/message.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" @@ -51,16 +52,16 @@ static inline void create_special_dict(typval_T *const rettv, typval_T val) FUNC_ATTR_NONNULL_ALL { - dict_T *const dict = dict_alloc(); - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + dict_T *const dict = tv_dict_alloc(); + dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; type_di->di_tv.vval.v_list->lv_refcount++; - dict_add(dict, type_di); - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + tv_dict_add(dict, type_di); + dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; - dict_add(dict, val_di); + tv_dict_add(dict, val_di); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -138,9 +139,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, assert(!(key.is_special_string || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); - dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); + dictitem_T *const obj_di = tv_dict_item_alloc( + (const char *)key.val.vval.v_string); tv_clear(&key.val); - if (dict_add(last_container.container.vval.v_dict, obj_di) + if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } @@ -173,8 +175,8 @@ static inline int json_decoder_pop(ValuesStackItem obj, && (obj.is_special_string || obj.val.vval.v_string == NULL || *obj.val.vval.v_string == NUL - || dict_find(last_container.container.vval.v_dict, - obj.val.vval.v_string, -1))) { + || tv_dict_find(last_container.container.vval.v_dict, + (const char *)obj.val.vval.v_string, -1))) { tv_clear(&obj.val); // Restart @@ -835,7 +837,7 @@ json_decode_string_cycle_start: .vval = { .v_list = val_list }, })); } else { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; tv = (typval_T) { .v_type = VAR_DICT, @@ -1042,7 +1044,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) goto msgpack_to_vim_generic_map; } } - dict_T *const dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -1055,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, mobj.via.map.ptr[i].key.via.str.size); di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { + if (tv_dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map tv_clear(rettv); xfree(di); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1416806ca6..26f9aaa27d 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -744,11 +744,11 @@ bool encode_check_json_key(const typval_T *const tv) } const dictitem_T *type_di; const dictitem_T *val_di; - if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL || type_di->di_tv.v_type != VAR_LIST || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) - || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL || val_di->di_tv.v_type != VAR_LIST) { return false; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 7726e106a1..bb7baed7d2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2,6 +2,7 @@ #include #include +#include "nvim/lib/queue.h" #include "nvim/eval/typval.h" #include "nvim/eval/gc.h" #include "nvim/eval/executor.h" @@ -12,6 +13,9 @@ #include "nvim/assert.h" #include "nvim/memory.h" #include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/vim.h" +#include "nvim/ascii.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -115,8 +119,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) } } -//{{{2 Lists -//{{{3 Alloc/free +//{{{2 Alloc/free /// Allocate an empty list /// @@ -205,7 +208,7 @@ void tv_list_unref(list_T *const l) } } -//{{{3 Add/remove +//{{{2 Add/remove /// Remove items "item" to "item2" from list "l". /// @@ -406,7 +409,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) tv_list_append(l, li); } -//{{{3 Operations on the whole list +//{{{2 Operations on the whole list /// Make a copy of list /// @@ -596,6 +599,8 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) /// @param[in] l2 Second list to compare. /// @param[in] ic True if case is to be ignored. /// @param[in] recursive True when used recursively. +/// +/// @return True if lists are equal, false otherwise. bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT @@ -623,7 +628,7 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } -//{{{3 Indexing/searching +//{{{2 Indexing/searching /// Locate item with a given index in a list and return it /// @@ -761,6 +766,552 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) } return idx; } + +//{{{1 Dictionaries +//{{{2 Dictionary watchers + +/// Perform all necessary cleanup for a `DictWatcher` instance +/// +/// @param watcher Watcher to free. +void tv_dict_watcher_free(DictWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + callback_free(&watcher->callback); + xfree(watcher->key_pattern); + xfree(watcher); +} + +/// Test if `key` matches with with `watcher->key_pattern` +/// +/// @param[in] watcher Watcher to check key pattern from. +/// @param[in] key Key to check. +/// +/// @return true if key matches, false otherwise. +static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + // For now only allow very simple globbing in key patterns: a '*' at the end + // of the string means it should match everything up to the '*' instead of the + // whole string. + const size_t len = strlen(watcher->key_pattern); + if (watcher->key_pattern[len - 1] == '*') { + return strncmp(key, watcher->key_pattern, len - 1) == 0; + } else { + return strcmp(key, watcher->key_pattern) == 0; + } +} + +/// Send a change notification to all dictionary watchers that match given key +/// +/// @param[in] dict Dictionary which was modified. +/// @param[in] key Key which was modified. +/// @param[in] newtv New key value. +/// @param[in] oldtv Old key value. +void tv_dict_watcher_notify(dict_T *const dict, const char *const key, + typval_T *const newtv, typval_T *const oldtv) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv[3]; + + argv[0].v_type = VAR_DICT; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_dict = dict; + argv[1].v_type = VAR_STRING; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_string = (char_u *)xstrdup(key); + argv[2].v_type = VAR_DICT; + argv[2].v_lock = VAR_UNLOCKED; + argv[2].vval.v_dict = tv_dict_alloc(); + argv[2].vval.v_dict->dv_refcount++; + + if (newtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); + copy_tv(newtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + if (oldtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); + copy_tv(oldtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + typval_T rettv; + + QUEUE *w; + QUEUE_FOREACH(w, &dict->watchers) { + DictWatcher *watcher = tv_dict_watcher_node_data(w); + if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) { + rettv = TV_INITIAL_VALUE; + watcher->busy = true; + callback_call(&watcher->callback, 3, argv, &rettv); + watcher->busy = false; + tv_clear(&rettv); + } + } + + for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { + tv_clear(argv + i); + } +} + +//{{{2 Dictionary item + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// @param[in] key_len Key length. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); + memcpy(di->di_key, key, key_len); + di->di_key[key_len] = NUL; + di->di_flags = DI_FLAGS_ALLOC; + return di; +} + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc(const char *const key) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + return tv_dict_item_alloc_len(key, strlen(key)); +} + +/// Free a dictionary item, also clearing the value +/// +/// @param item Item to free. +void tv_dict_item_free(dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->di_tv); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } +} + +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +int tv_dict_add(dict_T *const d, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + +/// Make a copy of a dictionary item +/// +/// @param[in] di Item to copy. +/// +/// @return [allocated] new dictionary item. +static dictitem_T *tv_dict_item_copy(dictitem_T *const di) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); + copy_tv(&di->di_tv, &new_di->di_tv); + return new_di; +} + +/// Remove item from dictionary and free it +/// +/// @param dict Dictionary to remove item from. +/// @param item Item to remove. +void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key); + if (HASHITEM_EMPTY(hi)) { + emsgf(_(e_intern2), "tv_dict_item_remove()"); + } else { + hash_remove(&dict->dv_hashtab, hi); + } + tv_dict_item_free(item); +} + +//{{{2 Alloc/free + +/// Allocate an empty dictionary +/// +/// @return [allocated] new dictionary. +dict_T *tv_dict_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + dict_T *const d = xmalloc(sizeof(dict_T)); + + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; + d->dv_used_prev = NULL; + gc_first_dict = d; + + hash_init(&d->dv_hashtab); + d->dv_lock = VAR_UNLOCKED; + d->dv_scope = VAR_NO_SCOPE; + d->dv_refcount = 0; + d->dv_copyID = 0; + QUEUE_INIT(&d->watchers); + + return d; +} + +/// Free items contained in a dictionary +/// +/// @param[in,out] d Dictionary to clear. +void tv_dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + HASHTAB_ITER(&d->dv_hashtab, hi, { + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = TV_DICT_HI2DI(hi); + hash_remove(&d->dv_hashtab, hi); + tv_dict_item_free(di); + }); + + while (!QUEUE_EMPTY(&d->watchers)) { + QUEUE *w = QUEUE_HEAD(&d->watchers); + QUEUE_REMOVE(w); + DictWatcher *watcher = tv_dict_watcher_node_data(w); + tv_dict_watcher_free(watcher); + } + + hash_clear(&d->dv_hashtab); + d->dv_hashtab.ht_locked--; + hash_init(&d->dv_hashtab); +} + +/// Free a dictionary itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] d Dictionary to free. +void tv_dict_free_dict(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the dict from the list of dicts for garbage collection. + if (d->dv_used_prev == NULL) { + gc_first_dict = d->dv_used_next; + } else { + d->dv_used_prev->dv_used_next = d->dv_used_next; + } + if (d->dv_used_next != NULL) { + d->dv_used_next->dv_used_prev = d->dv_used_prev; + } + + xfree(d); +} + +/// Free a dictionary, including all items it contains +/// +/// Ignores the reference count. +/// +/// @param d Dictionary to free. +void tv_dict_free(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_dict_free_contents(d); + tv_dict_free_dict(d); + } +} + + +/// Unreference a dictionary +/// +/// Decrements the reference count and frees dictionary when it becomes zero. +/// +/// @param[in] d Dictionary to operate on. +void tv_dict_unref(dict_T *const d) +{ + if (d != NULL && --d->dv_refcount <= 0) { + tv_dict_free(d); + } +} + +//{{{2 Indexing/searching + +/// Find item in dictionary +/// +/// @param[in] d Dictionary to check. +/// @param[in] key Dictionary key. +/// @param[in] len Key length. If negative, then strlen(key) is used. +/// +/// @return found item or NULL if nothing was found. +dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + hashitem_T *const hi = (len < 0 + ? hash_find(&d->dv_hashtab, (const char_u *)key) + : hash_find_len(&d->dv_hashtab, key, (size_t)len)); + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + return TV_DICT_HI2DI(hi); +} + +/// Get a number item from a dictionary +/// +/// Returns 0 if the entry does not exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return 0; + } + return get_tv_number(&di->di_tv); +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] save If true, returned string will be placed in the allocated +/// memory. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. If returned value is not NULL, it may +/// be allocated depending on `save` argument. +char *tv_dict_get_string(dict_T *const d, const char *const key, + const bool save) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char numbuf[NUMBUFLEN]; + const char *const s = tv_dict_get_string_buf(d, key, numbuf); + if (save && s != NULL) { + return xstrdup(s); + } + return (char *)s; +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] numbuf Numbuf for. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. +const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, + char *const numbuf) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return NULL; + } + return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); +} + +//{{{2 Operations on the whole dict + +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void tv_dict_clear(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + HASHTAB_ITER(&d->dv_hashtab, hi, { + tv_dict_item_free(TV_DICT_HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + }); + + hash_unlock(&d->dv_hashtab); +} + +/// Extend dictionary with items from another dictionary +/// +/// @param d1 Dictionary to extend. +/// @param[in] d2 Dictionary to extend with. +/// @param[in] action "error", "force", "keep": +/// +/// e*, including "error": duplicate key gives an error. +/// f*, including "force": duplicate d2 keys override d1. +/// other, including "keep": duplicate d2 keys ignored. +void tv_dict_extend(dict_T *const d1, dict_T *const d2, + const char *const action) + FUNC_ATTR_NONNULL_ALL +{ + const bool watched = tv_dict_is_watched(d1); + const char *const arg_errmsg = _("extend() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + + TV_DICT_ITER(d2, di2, { + dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); + if (d1->dv_scope != VAR_NO_SCOPE) { + // Disallow replacing a builtin function in l: and g:. + // Check the key to be valid when adding to any scope. + if (d1->dv_scope == VAR_DEF_SCOPE + && di2->di_tv.v_type == VAR_FUNC + && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { + break; + } + if (!valid_varname((const char *)di2->di_key)) { + break; + } + } + if (di1 == NULL) { + dictitem_T *const new_di = tv_dict_item_copy(di2); + if (tv_dict_add(d1, new_di) == FAIL) { + tv_dict_item_free(new_di); + } else if (watched) { + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, + NULL); + } + } else if (*action == 'e') { + emsgf(_("E737: Key already exists: %s"), di2->di_key); + break; + } else if (*action == 'f' && di2 != di1) { + typval_T oldtv; + + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { + break; + } + + if (watched) { + copy_tv(&di1->di_tv, &oldtv); + } + + tv_clear(&di1->di_tv); + copy_tv(&di2->di_tv, &di1->di_tv); + + if (watched) { + tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, + &oldtv); + tv_clear(&oldtv); + } + } + }); +} + +/// Compare two dictionaries +/// +/// @param[in] d1 First dictionary. +/// @param[in] d2 Second dictionary. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_dict_equal(dict_T *const d1, dict_T *const d2, + const bool ic, const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (d1 == d2) { + return true; + } + if (d1 == NULL || d2 == NULL) { + return false; + } + if (tv_dict_len(d1) != tv_dict_len(d2)) { + return false; + } + + TV_DICT_ITER(d1, di1, { + dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1); + if (di2 == NULL) { + return false; + } + if (!tv_equal(&di1->di_tv, &di2->di_tv, ic, recursive)) { + return false; + } + }); + return true; +} + +/// Make a copy of dictionary +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original dictionary to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied dictionary. May be NULL in case original dictionary is NULL +/// or some failure happens. The refcount of the new dictionary is set +/// to 1. +dict_T *tv_dict_copy(const vimconv_T *const conv, + dict_T *const orig, + const bool deep, + const int copyID) +{ + if (orig == NULL) { + return NULL; + } + + dict_T *copy = tv_dict_alloc(); + if (copyID != 0) { + orig->dv_copyID = copyID; + orig->dv_copydict = copy; + } + TV_DICT_ITER(orig, di, { + if (got_int) { + break; + } + dictitem_T *new_di; + if (conv == NULL || conv->vc_type == CONV_NONE) { + new_di = tv_dict_item_alloc((const char *)di->di_key); + } else { + size_t len = STRLEN(di->di_key); + char *const key = (char *)string_convert(conv, di->di_key, &len); + if (key == NULL) { + new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); + } else { + new_di = tv_dict_item_alloc_len(key, len); + xfree(key); + } + } + if (deep) { + if (var_item_copy(conv, &di->di_tv, &new_di->di_tv, deep, + copyID) == FAIL) { + xfree(new_di); + break; + } + } else { + copy_tv(&di->di_tv, &new_di->di_tv); + } + if (tv_dict_add(copy, new_di) == FAIL) { + tv_dict_item_free(new_di); + break; + } + }); + + copy->dv_refcount++; + if (got_int) { + tv_dict_unref(copy); + copy = NULL; + } + + return copy; +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -783,6 +1334,21 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv) return l; } +/// Allocate an empty dictionary for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where dictionary is saved. +void tv_dict_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const d = tv_dict_alloc(); + ret_tv->vval.v_dict = d; + ret_tv->v_type = VAR_DICT; + ret_tv->v_lock = VAR_UNLOCKED; + d->dv_refcount++; +} + //{{{3 Clear #define TYPVAL_ENCODE_ALLOW_SPECIALS false @@ -884,7 +1450,7 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ do { \ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ + tv_dict_unref((dict_T *)dict); \ *((dict_T **)&dict) = NULL; \ if (tv != NULL) { \ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ @@ -966,7 +1532,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, FUNC_ATTR_ALWAYS_INLINE { if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); + tv_dict_unref(*dictp); *dictp = NULL; } } @@ -1077,13 +1643,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, d->dv_lock); if (deep < 0 || deep > 1) { // recursive: lock/unlock the items the List contains - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } + TV_DICT_ITER(d, di, { + tv_item_lock(&di->di_tv, deep - 1, lock); + }); } } break; @@ -1121,6 +1683,140 @@ bool tv_islocked(const typval_T *const tv) && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); } +/// Return true if typval is locked +/// +/// Also gives an error message when typval is locked. +/// +/// @param[in] lock Lock status. +/// @param[in] name Variable name, used in the error message. +/// @param[in] use_gettext True if variable name also is to be translated. +/// +/// @return true if variable is locked, false otherwise. +bool tv_check_lock(const VarLockStatus lock, const char *const name, + const size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *error_message = NULL; + switch (lock) { + case VAR_UNLOCKED: { + return false; + } + case VAR_LOCKED: { + error_message = N_("E741: Value is locked: %.*s"); + break; + } + case VAR_FIXED: { + error_message = N_("E742: Cannot change value of %.*s"); + break; + } + } + assert(error_message != NULL); + + const char *const unknown_name = _("Unknown"); + + emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), + (name != NULL ? name : unknown_name)); + + return true; +} + +//{{{2 Comparison + +static int tv_equal_recurse_limit; + +/// Compare two VimL values +/// +/// Like "==", but strings and numbers are different, as well as floats and +/// numbers. +/// +/// @warning Too nested structures may be considered equal even if they are not. +/// +/// @param[in] tv1 First value to compare. +/// @param[in] tv2 Second value to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +/// +/// @return true if values are equal. +bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + // TODO(ZyX-I): Make this not recursive + static int recursive_cnt = 0; // Catch recursive loops. + + if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) + && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) + && tv1->v_type != tv2->v_type) { + return false; + } + + // Catch lists and dicts that have an endless loop by limiting + // recursiveness to a limit. We guess they are equal then. + // A fixed limit has the problem of still taking an awful long time. + // Reduce the limit every time running into it. That should work fine for + // deeply linked structures that are not recursively linked and catch + // recursiveness quickly. + if (!recursive) { + tv_equal_recurse_limit = 1000; + } + if (recursive_cnt >= tv_equal_recurse_limit) { + tv_equal_recurse_limit--; + return true; + } + + switch (tv1->v_type) { + case VAR_LIST: { + recursive_cnt++; + const bool r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, + true); + recursive_cnt--; + return r; + } + case VAR_DICT: { + recursive_cnt++; + const bool r = tv_dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, + true); + recursive_cnt--; + return r; + } + case VAR_PARTIAL: + case VAR_FUNC: { + if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL) + || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL)) { + return false; + } + recursive_cnt++; + const bool r = func_equal(tv1, tv2, ic); + recursive_cnt--; + return r; + } + case VAR_NUMBER: { + return tv1->vval.v_number == tv2->vval.v_number; + } + case VAR_FLOAT: { + return tv1->vval.v_float == tv2->vval.v_float; + } + case VAR_STRING: { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); + const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + return mb_strcmp_ic((bool)ic, s1, s2) == 0; + } + case VAR_SPECIAL: { + return tv1->vval.v_special == tv2->vval.v_special; + } + case VAR_UNKNOWN: { + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it + // does not equal anything, not even self. + return false; + } + } + + assert(false); + return false; +} + //{{{2 Type checks /// Check that given value is a number or string @@ -1135,37 +1831,37 @@ bool tv_islocked(const typval_T *const tv) bool tv_check_str_or_nr(const typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; } - assert(false); - return false; + case VAR_FLOAT: { + emsgf(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + emsgf(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + emsgf(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + emsgf(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + emsgf(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index cf83904ffc..6183397d12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -8,6 +8,7 @@ #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" +#include "nvim/func_attr.h" #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T @@ -31,6 +32,31 @@ typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; +typedef struct ufunc ufunc_T; + +typedef enum { + kCallbackNone, + kCallbackFuncref, + kCallbackPartial, +} CallbackType; + +typedef struct { + union { + char_u *funcref; + partial_T *partial; + } data; + CallbackType type; +} Callback; +#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) + +/// Structure holding dictionary watcher +typedef struct dict_watcher { + Callback callback; + char *key_pattern; + QUEUE node; + bool busy; // prevent recursion if the dict is changed in the callback +} DictWatcher; + /// Special variable values typedef enum { kSpecialVarFalse, ///< v:false @@ -134,7 +160,7 @@ struct dictitem_S { struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ - char_u di_key[KEY_LEN]; /* NUL. */ \ + char_u di_key[KEY_LEN]; /* Key value. */ \ } /// Structure to hold a scope dictionary @@ -181,9 +207,7 @@ typedef int scid_T; // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - +/// Structure to hold info for a user function. struct ufunc { int uf_varargs; ///< variable nr of arguments int uf_flags; @@ -207,12 +231,12 @@ struct ufunc { int uf_tml_idx; ///< index of line being timed; -1 if none int uf_tml_execed; ///< line being timed was executed scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables + ///< used for s: variables int uf_refcount; ///< reference count, see func_name_refcount() funccall_T *uf_scoped; ///< l: local variables for closure char_u uf_name[1]; ///< name of function (actually longer); can - // start with 123_ ( is K_SPECIAL - // KS_EXTRA KE_SNR) + ///< start with 123_ ( is K_SPECIAL + ///< KS_EXTRA KE_SNR) }; /// Maximum number of function arguments @@ -245,24 +269,17 @@ typedef struct list_stack_S { // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - /// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) +#define TV_DICT_HI2DI(hi) \ + ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) + +static inline long tv_list_len(list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. static inline long tv_list_len(list_T *const l) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (l == NULL) { return 0; @@ -270,6 +287,64 @@ static inline long tv_list_len(list_T *const l) return l->lv_len; } +static inline long tv_dict_len(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the number of items in a Dictionary +/// +/// @param[in] d Dictionary to check. +static inline long tv_dict_len(const dict_T *const d) +{ + if (d == NULL) { + return 0L; + } + return (long)d->dv_hashtab.ht_used; +} + +static inline bool tv_dict_is_watched(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check if dictionary is watched +/// +/// @param[in] d Dictionary to check. +/// +/// @return true if there is at least one watcher. +static inline bool tv_dict_is_watched(const dict_T *const d) +{ + return d && !QUEUE_EMPTY(&d->watchers); +} + +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + +/// Initialize VimL object +/// +/// Initializes to unlocked VAR_UNKNOWN object. +/// +/// @param[out] tv Object to initialize. +static inline void tv_init(typval_T *const tv) +{ + if (tv != NULL) { + memset(tv, 0, sizeof(*tv)); + } +} + +#define TV_INITIAL_VALUE \ + ((typval_T) { \ + .v_type = VAR_UNKNOWN, \ + .v_lock = VAR_UNLOCKED, \ + }) + /// Empty string /// /// Needed for hack which allows not allocating empty string and still not @@ -279,6 +354,21 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a dictionary +/// +/// @param[in] d Dictionary to iterate over. +/// @param di Name of the variable with current dictitem_T entry. +/// @param code Cycle body. +#define TV_DICT_ITER(d, di, code) \ + HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \ + { \ + dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \ + { \ + code \ + } \ + } \ + }) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index eb89a601ff..ad54eef4a0 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -406,11 +406,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const dictitem_T *val_di; if (TYPVAL_ENCODE_ALLOW_SPECIALS && tv->vval.v_dict->dv_hashtab.ht_used == 2 - && (type_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_TYPE", -1)) != NULL + && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_TYPE"))) != NULL && type_di->di_tv.v_type == VAR_LIST - && (val_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_VAL", -1)) != NULL) { + && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_VAL"))) != NULL) { size_t i; for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { @@ -662,7 +662,7 @@ typval_encode_stop_converting_one_item: while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { cur_mpsv->data.d.hi++; } - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); + dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi); cur_mpsv->data.d.todo--; cur_mpsv->data.d.hi++; TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0], diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 5cbf7f9ce7..26d70a5e6d 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,7 +21,7 @@ struct process { int pid, status, refcount; // set to the hrtime of when process_stop was called for the process. uint64_t stopped_time; - char *cwd; + const char *cwd; char **argv; Stream *in, *out, *err; process_exit_cb cb; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 151a4d375f..9681527fee 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4764,8 +4764,8 @@ void fix_help_buffer(void) char_u *p; char_u *rt; - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + // Set filetype to "help". + set_option_value("ft", 0L, "help", OPT_LOCAL); if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e59a87b335..b84834d351 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2073,9 +2073,9 @@ void ex_listdo(exarg_T *eap) // Clear 'shm' to avoid that the file message overwrites // any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); do_argfile(eap, i); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); } if (curwin->w_arg_idx != i) { @@ -2138,9 +2138,9 @@ void ex_listdo(exarg_T *eap) // Go to the next buffer. Clear 'shm' to avoid that the file // message overwrites any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); // If autocommands took us elsewhere, quit here. @@ -2496,16 +2496,16 @@ static int APP_BOTH; static void add_pack_plugin(char_u *fname, void *cookie) { char_u *p4, *p3, *p2, *p1, *p; - char_u *new_rtp; - char_u *ffname = (char_u *)fix_fname((char *)fname); + + char *const ffname = fix_fname((char *)fname); if (ffname == NULL) { return; } - if (cookie != &APP_LOAD && strstr((char *)p_rtp, (char *)ffname) == NULL) { + if (cookie != &APP_LOAD && strstr((char *)p_rtp, ffname) == NULL) { // directory is not yet in 'runtimepath', add it - p4 = p3 = p2 = p1 = get_past_head(ffname); + p4 = p3 = p2 = p1 = get_past_head((char_u *)ffname); for (p = p1; *p; mb_ptr_adv(p)) { if (vim_ispathsep_nocolon(*p)) { p4 = p3; p3 = p2; p2 = p1; p1 = p; @@ -2521,13 +2521,13 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = NUL; // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences - size_t fname_len = STRLEN(ffname); - char_u *insp = p_rtp; + size_t fname_len = strlen(ffname); + const char *insp = (const char *)p_rtp; for (;;) { - if (vim_fnamencmp(insp, ffname, fname_len) == 0) { + if (path_fnamencmp(insp, ffname, fname_len) == 0) { break; } - insp = vim_strchr(insp, ','); + insp = strchr(insp, ','); if (insp == NULL) { break; } @@ -2536,10 +2536,10 @@ static void add_pack_plugin(char_u *fname, void *cookie) if (insp == NULL) { // not found, append at the end - insp = p_rtp + STRLEN(p_rtp); + insp = (const char *)p_rtp + STRLEN(p_rtp); } else { // append after the matching directory. - insp += STRLEN(ffname); + insp += strlen(ffname); while (*insp != NUL && *insp != ',') { insp++; } @@ -2547,31 +2547,40 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = c; // check if rtp/pack/name/start/name/after exists - char *afterdir = concat_fnames((char *)ffname, "after", true); + char *afterdir = concat_fnames(ffname, "after", true); size_t afterlen = 0; if (os_isdir((char_u *)afterdir)) { - afterlen = STRLEN(afterdir) + 1; // add one for comma + afterlen = strlen(afterdir) + 1; // add one for comma } - size_t oldlen = STRLEN(p_rtp); - size_t addlen = STRLEN(ffname) + 1; // add one for comma - new_rtp = try_malloc(oldlen + addlen + afterlen + 1); // add one for NUL + const size_t oldlen = STRLEN(p_rtp); + const size_t addlen = strlen(ffname) + 1; // add one for comma + const size_t new_rtp_len = oldlen + addlen + afterlen + 1; + // add one for NUL -------------------------------------^ + char *const new_rtp = try_malloc(new_rtp_len); if (new_rtp == NULL) { goto theend; } - uintptr_t keep = (uintptr_t)(insp - p_rtp); + const size_t keep = (size_t)(insp - (const char *)p_rtp); + size_t new_rtp_fill = 0; memmove(new_rtp, p_rtp, keep); - new_rtp[keep] = ','; - memmove(new_rtp + keep + 1, ffname, addlen); + new_rtp_fill += keep; + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, ffname, addlen); + new_rtp_fill += addlen - 1; + assert(new_rtp[new_rtp_fill] == NUL || new_rtp[new_rtp_fill] == ','); if (p_rtp[keep] != NUL) { - memmove(new_rtp + keep + addlen, p_rtp + keep, - oldlen - keep + 1); + memmove(new_rtp + new_rtp_fill, p_rtp + keep, oldlen - keep + 1); + new_rtp_fill += oldlen - keep; } if (afterlen > 0) { - STRCAT(new_rtp, ","); - STRCAT(new_rtp, afterdir); + assert(new_rtp[new_rtp_fill] == NUL); + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, afterdir, afterlen - 1); + new_rtp_fill += afterlen - 1; } - set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + new_rtp[new_rtp_fill] = NUL; + set_option_value("rtp", 0L, new_rtp, 0); xfree(new_rtp); xfree(afterdir); } @@ -2580,7 +2589,7 @@ static void add_pack_plugin(char_u *fname, void *cookie) static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT - size_t len = STRLEN(ffname) + STRLEN(ftpat); + size_t len = strlen(ffname) + STRLEN(ftpat); char_u *pat = try_malloc(len + 1); if (pat == NULL) { goto theend; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index dc99c0771d..87fe52c119 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9483,18 +9483,18 @@ void dialog_msg(char_u *buff, char *format, char_u *fname) static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0); - set_option_value((char_u *)"keymodel", 0L, - (char_u *)"startsel,stopsel", 0); + set_option_value("selection", 0L, "exclusive", 0); + set_option_value("selectmode", 0L, "mouse,key", 0); + set_option_value("mousemodel", 0L, "popup", 0); + set_option_value("keymodel", 0L, "startsel,stopsel", 0); } else if (STRCMP(eap->arg, "xterm") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0); - set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0); - } else + set_option_value("selection", 0L, "inclusive", 0); + set_option_value("selectmode", 0L, "", 0); + set_option_value("mousemodel", 0L, "extend", 0); + set_option_value("keymodel", 0L, "", 0); + } else { EMSG2(_(e_invarg2), eap->arg); + } } /* @@ -9608,8 +9608,9 @@ void filetype_maybe_enable(void) */ static void ex_setfiletype(exarg_T *eap) { - if (!did_filetype) - set_option_value((char_u *)"filetype", 0L, eap->arg, OPT_LOCAL); + if (!did_filetype) { + set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL); + } } static void ex_digraphs(exarg_T *eap) @@ -9695,7 +9696,8 @@ static void ex_match(exarg_T *eap) c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL, NULL); + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); xfree(g); *end = c; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9851ed5396..872b7fe365 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5173,7 +5173,7 @@ static int ex_window(void) // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); curwin->w_p_rl = cmdmsg_rl; cmdmsg_rl = false; curbuf->b_p_ma = true; @@ -5191,7 +5191,7 @@ static int ex_window(void) add_map((char_u *)" ", INSERT); add_map((char_u *)" a", NORMAL); } - set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL); + set_option_value("ft", 0L, "vim", OPT_LOCAL); } /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b73d9944ce..8ab6955042 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1556,7 +1556,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0c131d7b33..076ee13a80 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1881,9 +1881,8 @@ static int vgetorpeek(int advance) (size_t)(mlen - typebuf.tb_maplen)); } - del_typebuf(mlen, 0); /* remove the chars */ - set_option_value((char_u *)"paste", - (long)!p_paste, NULL, 0); + del_typebuf(mlen, 0); // Remove the chars. + set_option_value("paste", !p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; msg_row = (int)Rows - 1; diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index b14bb01c0e..354c9718ba 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -82,7 +82,7 @@ void hash_clear_all(hashtab_T *ht, unsigned int off) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find(hashtab_T *ht, const char_u *key) +hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key) { return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key)); } @@ -99,7 +99,8 @@ hashitem_T *hash_find(hashtab_T *ht, const char_u *key) /// /// @warning Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) +hashitem_T *hash_find_len(const hashtab_T *const ht, const char *const key, + const size_t len) { return hash_lookup(ht, key, len, hash_hash_len(key, len)); } @@ -115,7 +116,7 @@ hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_lookup(hashtab_T *const ht, +hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const size_t key_len, const hash_T hash) { diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 0da2b13f2e..973b97d476 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -70,6 +70,25 @@ typedef struct hashtable_S { hashitem_T ht_smallarray[HT_INIT_SIZE]; /// initial array } hashtab_T; +/// Iterate over a hashtab +/// +/// @param[in] ht Hashtab to iterate over. +/// @param hi Name of the variable with current hashtab entry. +/// @param code Cycle body. +#define HASHTAB_ITER(ht, hi, code) \ + do { \ + hashtab_T *const hi##ht_ = (ht); \ + size_t hi##todo_ = hi##ht_->ht_used; \ + for (hashitem_T *hi = hi##ht_->ht_array; hi##todo_; hi++) { \ + if (!HASHITEM_EMPTY(hi)) { \ + { \ + code \ + } \ + hi##todo_--; \ + } \ + } \ + } while (0) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "hashtab.h.generated.h" #endif diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..5042663041 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ diff --git a/src/nvim/main.c b/src/nvim/main.c index 0c978dc47d..33e1551351 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -803,17 +803,18 @@ static void command_line_scan(mparm_T *parmp) argv_idx = -1; /* skip to next argument */ break; - case 'A': /* "-A" start in Arabic mode */ - set_option_value((char_u *)"arabic", 1L, NULL, 0); + case 'A': { // "-A" start in Arabic mode. + set_option_value("arabic", 1L, NULL, 0); break; - - case 'b': /* "-b" binary mode */ - /* Needs to be effective before expanding file names, because - * for Win32 this makes us edit a shortcut file itself, - * instead of the file it links to. */ + } + case 'b': { // "-b" binary mode. + // Needs to be effective before expanding file names, because + // for Win32 this makes us edit a shortcut file itself, + // instead of the file it links to. set_options_bin(curbuf->b_p_bin, 1, 0); - curbuf->b_p_bin = 1; /* binary file I/O */ + curbuf->b_p_bin = 1; // Binary file I/O. break; + } case 'e': /* "-e" Ex mode */ exmode_active = EXMODE_NORMAL; @@ -830,24 +831,27 @@ static void command_line_scan(mparm_T *parmp) main_start_gui(); break; - case 'F': /* "-F" start in Farsi mode: rl + fkmap set */ - p_fkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'F': { // "-F" start in Farsi mode: rl + fkmap set. + p_fkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } case 'h': /* "-h" give help message */ usage(); mch_exit(0); - case 'H': /* "-H" start in Hebrew mode: rl + hkmap set */ - p_hkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'H': { // "-H" start in Hebrew mode: rl + hkmap set. + p_hkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } - case 'l': /* "-l" lisp mode, 'lisp' and 'showmatch' on */ - set_option_value((char_u *)"lisp", 1L, NULL, 0); - p_sm = TRUE; + case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on. + set_option_value("lisp", 1L, NULL, 0); + p_sm = true; break; + } case 'M': /* "-M" no changes or writing of files */ reset_modifiable(); @@ -946,8 +950,7 @@ static void command_line_scan(mparm_T *parmp) /* default is 10: a little bit verbose */ p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value((char_u *)"verbosefile", 0L, - (char_u *)argv[0] + argv_idx, 0); + set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0); argv_idx = (int)STRLEN(argv[0]); } break; @@ -956,7 +959,7 @@ static void command_line_scan(mparm_T *parmp) /* "-w {scriptout}" write to script */ if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); break; } want_argument = TRUE; @@ -1088,7 +1091,7 @@ scripterror: if (ascii_isdigit(*((char_u *)argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); argv_idx = -1; break; } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 1d1e13e7e0..ae94ec7ecd 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -62,7 +62,7 @@ int setmark(int c) /// Free fmark_T item void free_fmark(fmark_T fm) { - dict_unref(fm.additional_data); + tv_dict_unref(fm.additional_data); } /// Free xfmark_T item diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 57518a9535..621fa6fefc 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -583,7 +583,7 @@ int utf_ptr2char(const char_u *p) * If byte sequence is illegal or incomplete, returns -1 and does not advance * "s". */ -static int utf_safe_read_char_adv(char_u **s, size_t *n) +static int utf_safe_read_char_adv(const char_u **s, size_t *n) { int c; @@ -1233,7 +1233,8 @@ bool utf_isupper(int a) return utf_tolower(a) != a; } -static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2) +static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, + size_t n2) { int c1, c2, cdiff; char_u buffer[6]; @@ -1392,19 +1393,26 @@ int utf16_to_utf8(const WCHAR *strw, char **str) * Returns zero if s1 and s2 are equal (ignoring case), the difference between * two characters otherwise. */ -int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) +int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn) { return utf_strnicmp(s1, s2, nn, nn); } -/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte - * encoding because mb_stricmp() takes care of all ascii and non-ascii - * encodings, including characters with umlauts in latin1, etc., while - * STRICMP() only handles the system locale version, which often does not - * handle non-ascii properly. */ -int mb_stricmp(char_u *s1, char_u *s2) +/// Compare strings case-insensitively +/// +/// @note We need to call mb_stricmp() even when we aren't dealing with +/// a multi-byte encoding because mb_stricmp() takes care of all ASCII and +/// non-ascii encodings, including characters with umlauts in latin1, +/// etc., while STRICMP() only handles the system locale version, which +/// often does not handle non-ascii properly. +/// +/// @param[in] s1 First string to compare, not more then #MAXCOL characters. +/// @param[in] s2 Second string to compare, not more then #MAXCOL characters. +/// +/// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2. +int mb_stricmp(const char *s1, const char *s2) { - return mb_strnicmp(s1, s2, MAXCOL); + return mb_strnicmp((const char_u *)s1, (const char_u *)s2, MAXCOL); } /* @@ -2020,8 +2028,8 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, - size_t *unconvlenp, size_t *resultlenp) +static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, + size_t slen, size_t *unconvlenp, size_t *resultlenp) { const char *from; size_t fromlen; @@ -2306,7 +2314,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, * Illegal chars are often changed to "?", unless vcp->vc_fail is set. * When something goes wrong, NULL is returned and "*lenp" is unchanged. */ -char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) +char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp) { return string_convert_ext(vcp, ptr, lenp, NULL); } @@ -2316,7 +2324,7 @@ char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, +char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp, size_t *unconvlenp) { char_u *retval = NULL; diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 5f5bab9fcd..c20e6d47ff 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -2,8 +2,10 @@ #define NVIM_MBYTE_H #include +#include #include "nvim/iconv.h" +#include "nvim/func_attr.h" /* * Return byte length of character that starts with byte "b". @@ -66,4 +68,17 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif + +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compare strings +/// +/// @param[in] ic True if case is to be ignored. +/// +/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) +{ + return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); +} #endif // NVIM_MBYTE_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f9d3751390..5ea2397db3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -992,7 +992,7 @@ void ml_recover(void) if (b0_ff != 0) set_fileformat(b0_ff - 1, OPT_LOCAL); if (b0_fenc != NULL) { - set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, TRUE); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 85cef59aec..d634a8c393 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -16,6 +16,7 @@ #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -888,7 +889,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) if (reg->additional_data == additional_data) { return; } - dict_unref(reg->additional_data); + tv_dict_unref(reg->additional_data); reg->additional_data = additional_data; } @@ -2581,7 +2582,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/option.c b/src/nvim/option.c index b037b0ae35..4c8606a999 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -25,6 +25,7 @@ #include #include "nvim/vim.h" +#include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/edit.h" #include "nvim/option.h" @@ -34,6 +35,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -604,7 +606,7 @@ void set_init_1(void) /* * 'maxmemtot' and 'maxmem' may have to be adjusted for available memory */ - opt_idx = findoption((char_u *)"maxmemtot"); + opt_idx = findoption("maxmemtot"); if (opt_idx >= 0) { { /* Use half of amount of memory available to Vim. */ @@ -614,7 +616,7 @@ void set_init_1(void) ? UINTPTR_MAX : (uintptr_t)(available_kib /2); options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; - opt_idx = findoption((char_u *)"maxmem"); + opt_idx = findoption("maxmem"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; } @@ -645,7 +647,7 @@ void set_init_1(void) } } buf[j] = NUL; - opt_idx = findoption((char_u *)"cdpath"); + opt_idx = findoption("cdpath"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = buf; options[opt_idx].flags |= P_DEF_ALLOCED; @@ -764,8 +766,9 @@ void set_init_1(void) * NOTE: mlterm's author is being asked to 'set' a variable * instead of an environment variable due to inheritance. */ - if (os_env_exists("MLTERM")) - set_option_value((char_u *)"tbidi", 1L, NULL, 0); + if (os_env_exists("MLTERM")) { + set_option_value("tbidi", 1L, NULL, 0); + } didset_options2(); @@ -775,7 +778,7 @@ void set_init_1(void) char_u *p = enc_locale(); if (p == NULL) { // use utf-8 as 'default' if locale encoding can't be detected. - p = vim_strsave((char_u *)"utf-8"); + p = (char_u *)xmemdupz(S_LEN("utf-8")); } fenc_default = p; @@ -882,7 +885,7 @@ set_options_default ( static void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - int opt_idx = findoption((char_u *)name); + int opt_idx = findoption(name); if (opt_idx >= 0) { if (options[opt_idx].flags & P_DEF_ALLOCED) { xfree(options[opt_idx].def_val[VI_DEFAULT]); @@ -904,9 +907,10 @@ void set_number_default(char *name, long val) { int opt_idx; - opt_idx = findoption((char_u *)name); - if (opt_idx >= 0) + opt_idx = findoption(name); + if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val; + } } #if defined(EXITFREE) @@ -947,17 +951,19 @@ void set_init_2(void) * wrong when the window height changes. */ set_number_default("scroll", Rows / 2); - idx = findoption((char_u *)"scroll"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) + idx = findoption("scroll"); + if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL, p_cp); + } comp_col(); /* * 'window' is only for backwards compatibility with Vi. * Default is Rows - 1. */ - if (!option_was_set((char_u *)"window")) + if (!option_was_set("window")) { p_window = Rows - 1; + } set_number_default("window", Rows - 1); parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ (void)parse_printoptions(); /* parse 'printoptions' default value */ @@ -976,16 +982,18 @@ void set_init_3(void) int idx_sp; int do_sp; - idx_srr = findoption((char_u *)"srr"); - if (idx_srr < 0) - do_srr = FALSE; - else + idx_srr = findoption("srr"); + if (idx_srr < 0) { + do_srr = false; + } else { do_srr = !(options[idx_srr].flags & P_WAS_SET); - idx_sp = findoption((char_u *)"sp"); - if (idx_sp < 0) - do_sp = FALSE; - else + } + idx_sp = findoption("sp"); + if (idx_sp < 0) { + do_sp = false; + } else { do_sp = !(options[idx_sp].flags & P_WAS_SET); + } size_t len = 0; char_u *p = (char_u *)invocation_path_tail(p_sh, &len); @@ -1029,7 +1037,7 @@ void set_init_3(void) } if (bufempty()) { - int idx_ffs = findoption((char_u *)"ffs"); + int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { @@ -1048,14 +1056,16 @@ void set_helplang_default(const char *lang) { int idx; - if (lang == NULL || STRLEN(lang) < 2) /* safety check */ + const size_t lang_len = strlen(lang); + if (lang == NULL || lang_len < 2) { // safety check return; - idx = findoption((char_u *)"hlg"); + } + idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); - p_hlg = (char_u *)xstrdup(lang); - /* zh_CN becomes "cn", zh_TW becomes "tw". */ + p_hlg = (char_u *)xmemdupz(lang, lang_len); + // zh_CN becomes "cn", zh_TW becomes "tw". if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); @@ -1082,12 +1092,12 @@ void set_title_defaults(void) * icon name. Saves a bit of time, because the X11 display server does * not need to be contacted. */ - idx1 = findoption((char_u *)"title"); + idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_title = 0; } - idx1 = findoption((char_u *)"icon"); + idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_icon = 0; @@ -1193,7 +1203,7 @@ do_set ( goto skip; } if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); } len++; if (opt_idx == -1) { @@ -1209,7 +1219,7 @@ do_set ( len++; } } - opt_idx = findoption_len(arg, (size_t) len); + opt_idx = findoption_len((const char *)arg, (size_t)len); if (opt_idx == -1) { key = find_key_option(arg); } @@ -1391,11 +1401,10 @@ do_set ( value = prefix; } - errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); - } else { /* numeric or string */ - if (vim_strchr((char_u *)"=:&<", nextchar) == NULL - || prefix != 1) { + errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value, + opt_flags); + } else { // Numeric or string. + if (strchr("=:&<", nextchar) == NULL || prefix != 1) { errmsg = e_invarg; goto skip; } @@ -1448,15 +1457,19 @@ do_set ( goto skip; } - if (adding) + if (adding) { value = *(long *)varp + value; - if (prepending) + } + if (prepending) { value = *(long *)varp * value; - if (removing) + } + if (removing) { value = *(long *)varp - value; - errmsg = set_num_option(opt_idx, varp, value, - errbuf, sizeof(errbuf), opt_flags); - } else if (opt_idx >= 0) { /* string */ + } + errmsg = (char_u *)set_num_option(opt_idx, varp, value, + errbuf, sizeof(errbuf), + opt_flags); + } else if (opt_idx >= 0) { // String. char_u *save_arg = NULL; char_u *s = NULL; char_u *oldval = NULL; // previous value if *varp @@ -2221,7 +2234,7 @@ static void check_string_option(char_u **pp) */ int was_set_insecurely(char_u *opt, int opt_flags) { - int idx = findoption(opt); + int idx = findoption((const char *)opt); if (idx >= 0) { uint32_t *flagp = insecure_flag(idx, opt_flags); @@ -2283,9 +2296,9 @@ set_string_option_direct ( int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int idx = opt_idx; - if (idx == -1) { /* use name */ - idx = findoption(name); - if (idx < 0) { /* not found (should not happen) */ + if (idx == -1) { // Use name. + idx = findoption((const char *)name); + if (idx < 0) { // Not found (should not happen). EMSG2(_(e_intern2), "set_string_option_direct()"); EMSG2(_("For option %s"), name); return; @@ -2765,7 +2778,7 @@ did_set_string_option ( // option. opt_idx = ((options[opt_idx].fullname[0] == 'v') ? (shada_idx == -1 - ? ((shada_idx = findoption((char_u *) "shada"))) + ? ((shada_idx = findoption("shada"))) : shada_idx) : opt_idx); // Update free_oldval now that we have the opt_idx for 'shada', otherwise @@ -3575,24 +3588,24 @@ static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id) } } -/* - * Set the value of a boolean option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_bool_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - int value, /* new value */ - int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */ -) +/// Set the value of a boolean option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static char *set_bool_option(const int opt_idx, char_u *const varp, + const int value, + const int opt_flags) { int old_value = *(int *)varp; /* Disallow changing some options from secure mode */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *(int *)varp = value; /* set the new value */ @@ -3605,20 +3618,18 @@ set_bool_option ( *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; // Ensure that options set to p_force_on cannot be disabled. - if ((int *)varp == &p_force_on && p_force_on == FALSE) { - p_force_on = TRUE; - return e_unsupportedoption; - } + if ((int *)varp == &p_force_on && p_force_on == false) { + p_force_on = true; + return (char *)e_unsupportedoption; // Ensure that options set to p_force_off cannot be enabled. - else if ((int *)varp == &p_force_off && p_force_off == TRUE) { - p_force_off = FALSE; - return e_unsupportedoption; - } - /* 'undofile' */ - else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { - /* Only take action when the option was set. When reset we do not - * delete the undo file, the option may be set again without making - * any changes in between. */ + } else if ((int *)varp == &p_force_off && p_force_off == true) { + p_force_off = false; + return (char *)e_unsupportedoption; + // 'undofile' + } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { + // Only take action when the option was set. When reset we do not + // delete the undo file, the option may be set again without making + // any changes in between. if (curbuf->b_p_udf || p_udf) { char_u hash[UNDO_HASH_SIZE]; buf_T *save_curbuf = curbuf; @@ -3740,8 +3751,8 @@ set_bool_option ( if (curwin->w_p_pvw) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (win->w_p_pvw && win != curwin) { - curwin->w_p_pvw = FALSE; - return (char_u *)N_("E590: A preview window already exists"); + curwin->w_p_pvw = false; + return N_("E590: A preview window already exists"); } } } @@ -3889,9 +3900,8 @@ set_bool_option ( /* set 'delcombine' */ p_deco = TRUE; - /* Force-set the necessary keymap for arabic */ - set_option_value((char_u *)"keymap", 0L, (char_u *)"arabic", - OPT_LOCAL); + // Force-set the necessary keymap for arabic. + set_option_value("keymap", 0L, "arabic", OPT_LOCAL); p_altkeymap = 0; p_hkmap = 0; p_fkmap = 0; @@ -3957,20 +3967,18 @@ set_bool_option ( return NULL; } -/* - * Set the value of a number option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_num_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - long value, /* new value */ - char_u *errbuf, /* buffer for error messages */ - size_t errbuflen, /* length of "errbuf" */ - int opt_flags /* OPT_LOCAL, OPT_GLOBAL and - OPT_MODELINE */ -) +/// Set the value of a number option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param errbuf Buffer for error messages. +/// @param[in] errbuflen Length of `errbuf`. +/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. +/// +/// @return NULL on success, error message on error. +static char *set_num_option(int opt_idx, char_u *varp, long value, + char_u *errbuf, size_t errbuflen, int opt_flags) { char_u *errmsg = NULL; long old_value = *(long *)varp; @@ -3981,7 +3989,7 @@ set_num_option ( /* Disallow changing some options from secure mode. */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *pp = value; @@ -4251,8 +4259,9 @@ set_num_option ( cmdline_row = (int)(Rows - p_ch); } } - if (p_window >= Rows || !option_was_set((char_u *)"window")) + if (p_window >= Rows || !option_was_set("window")) { p_window = Rows - 1; + } } if (curbuf->b_p_ts <= 0) { @@ -4357,7 +4366,7 @@ set_num_option ( curwin->w_set_curswant = TRUE; check_redraw(options[opt_idx].flags); - return errmsg; + return (char *)errmsg; } /* @@ -4395,39 +4404,36 @@ static void check_redraw(uint32_t flags) /// @param[in] len Length of the option. /// /// @return Index of the option or -1 if option was not found. -int findoption_len(const char_u *const arg, const size_t len) +int findoption_len(const char *const arg, const size_t len) { - char *s, *p; + const char *s; + const char *p; static int quick_tab[27] = { 0, 0 }; // quick access table - int is_term_opt; - /* - * For first call: Initialize the quick-access table. - * It contains the index for the first option that starts with a certain - * letter. There are 26 letters, plus the first "t_" option. - */ + // For first call: Initialize the quick-access table. + // It contains the index for the first option that starts with a certain + // letter. There are 26 letters, plus the first "t_" option. if (quick_tab[1] == 0) { p = options[0].fullname; for (short int i = 1; (s = options[i].fullname) != NULL; i++) { if (s[0] != p[0]) { - if (s[0] == 't' && s[1] == '_') + if (s[0] == 't' && s[1] == '_') { quick_tab[26] = i; - else + } else { quick_tab[CharOrdLow(s[0])] = i; + } } p = s; } } - /* - * Check for name starting with an illegal character. - */ + // Check for name starting with an illegal character. if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; } int opt_idx; - is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); if (is_term_opt) { opt_idx = quick_tab[26]; } else { @@ -4435,7 +4441,7 @@ int findoption_len(const char_u *const arg, const size_t len) } // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } } @@ -4444,20 +4450,22 @@ int findoption_len(const char_u *const arg, const size_t len) // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } s = NULL; } } - if (s == NULL) + if (s == NULL) { opt_idx = -1; + } return opt_idx; } -bool is_tty_option(char *name) +bool is_tty_option(const char *name) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || !strcmp((char *)name, "term"); + return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; } #define TCO_BUFFER_SIZE 8 @@ -4493,7 +4501,7 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(char *name, char *value) +bool set_tty_option(const char *name, const char *value) { if (!strcmp(name, "t_Co")) { int colors = atoi(value); @@ -4504,23 +4512,24 @@ bool set_tty_option(char *name, char *value) if (colors != t_colors) { t_colors = colors; // We now have a different color setup, initialize it again. - init_highlight(TRUE, FALSE); + init_highlight(true, false); } return true; } - return is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype"); + return (is_tty_option(name) || !strcmp(name, "term") + || !strcmp(name, "ttytype")); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for an option +/// +/// @param[in] arg Option name. +/// +/// @return Option index or -1 if option was not found. +static int findoption(const char *const arg) { - return findoption_len(arg, STRLEN(arg)); + return findoption_len(arg, strlen(arg)); } /* @@ -4548,9 +4557,10 @@ get_option_value ( int opt_idx; char_u *varp; - opt_idx = findoption(name); - if (opt_idx < 0) /* unknown option */ + opt_idx = findoption((const char *)name); + if (opt_idx < 0) { // Unknown option. return -3; + } varp = get_varp_scope(&(options[opt_idx]), opt_flags); @@ -4608,7 +4618,7 @@ int get_option_value_strict(char *name, char_u *varp = NULL; vimoption_T *p; int rv = 0; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } @@ -4702,21 +4712,19 @@ int get_option_value_strict(char *name, return rv; } -/* - * Set the value of option "name". - * Use "string" for string options, use "number" for other options. - * - * Returns NULL on success or error message on error. - */ -char_u * -set_option_value ( - char_u *name, - long number, - char_u *string, - int opt_flags /* OPT_LOCAL or 0 (both) */ -) +/// Set the value of an option +/// +/// @param[in] name Option name. +/// @param[in] number New value for the number or boolean option. +/// @param[in] string New value for string option. +/// @param[in] opt_flags Flags: OPT_LOCAL or 0 (both). +/// +/// @return NULL on success, error message on error. +char *set_option_value(const char *const name, const long number, + const char *const string, const int opt_flags) + FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option((char *)name, (char *)string)) { + if (set_tty_option(name, string)) { return NULL; } @@ -4724,9 +4732,9 @@ set_option_value ( char_u *varp; opt_idx = findoption(name); - if (opt_idx < 0) + if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); - else { + } else { uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { @@ -4734,11 +4742,11 @@ set_option_value ( return NULL; } if (flags & P_STRING) { - const char *s = (const char *)string; + const char *s = string; if (s == NULL) { s = ""; } - return (char_u *)set_string_option(opt_idx, s, opt_flags); + return set_string_option(opt_idx, s, opt_flags); } else { varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp != NULL) { /* hidden option is not changed */ @@ -4757,12 +4765,11 @@ set_option_value ( return NULL; // do nothing as we hit an error } } - if (flags & P_NUM) - return set_num_option(opt_idx, varp, number, - NULL, 0, opt_flags); - else - return set_bool_option(opt_idx, varp, (int)number, - opt_flags); + if (flags & P_NUM) { + return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags); + } else { + return set_bool_option(opt_idx, varp, (int)number, opt_flags); + } } } } @@ -4773,9 +4780,10 @@ char_u *get_highlight_default(void) { int i; - i = findoption((char_u *)"hl"); - if (i >= 0) + i = findoption("hl"); + if (i >= 0) { return options[i].def_val[VI_DEFAULT]; + } return (char_u *)NULL; } @@ -5212,7 +5220,7 @@ void unset_global_local_option(char *name, void *from) vimoption_T *p; buf_T *buf = (buf_T *)from; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); return; @@ -5775,11 +5783,12 @@ void reset_modifiable(void) { int opt_idx; - curbuf->b_p_ma = FALSE; - p_ma = FALSE; - opt_idx = findoption((char_u *)"ma"); - if (opt_idx >= 0) - options[opt_idx].def_val[VI_DEFAULT] = FALSE; + curbuf->b_p_ma = false; + p_ma = false; + opt_idx = findoption("ma"); + if (opt_idx >= 0) { + options[opt_idx].def_val[VI_DEFAULT] = false; + } } /* @@ -5877,15 +5886,15 @@ set_context_in_set_cmd ( expand_option_name[2] = p[-2]; expand_option_name[3] = p[-1]; } else { - /* Allow * wildcard */ - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') + // Allow * wildcard. + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') { p++; - if (*p == NUL) + } + if (*p == NUL) { return; + } nextchar = *p; - *p = NUL; - opt_idx = findoption(arg); - *p = nextchar; + opt_idx = findoption_len((const char *)arg, (size_t)(p - arg)); if (opt_idx == -1 || options[opt_idx].var == NULL) { xp->xp_context = EXPAND_NOTHING; return; @@ -6047,7 +6056,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) * For a terminal key code expand_option_idx is < 0. */ if (expand_option_idx < 0) { - expand_option_idx = findoption(expand_option_name); + expand_option_idx = findoption((const char *)expand_option_name); } if (expand_option_idx >= 0) { @@ -6447,20 +6456,22 @@ void vimrc_found(char_u *fname, char_u *envname) } } -/* - * Return TRUE when option "name" has been set. - * Only works correctly for global options. - */ -int option_was_set(char_u *name) +/// Check whether global option has been set +/// +/// @param[in] name Option name. +/// +/// @return True if it was set. +static bool option_was_set(const char *name) { int idx; idx = findoption(name); - if (idx < 0) /* unknown option */ - return FALSE; - if (options[idx].flags & P_WAS_SET) - return TRUE; - return FALSE; + if (idx < 0) { // Unknown option. + return false; + } else if (options[idx].flags & P_WAS_SET) { + return true; + } + return false; } /* @@ -6945,10 +6956,11 @@ bool signcolumn_on(win_T *wp) return wp->w_buffer->b_signlist != NULL; } -/// Get window or buffer local options. -dict_T * get_winbuf_options(int bufopt) +/// Get window or buffer local options +dict_T *get_winbuf_options(const int bufopt) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - dict_T *d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { struct vimoption *opt = &options[opt_idx]; diff --git a/src/nvim/path.c b/src/nvim/path.c index dfcafc85de..2bd87b608e 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -282,48 +282,63 @@ bool dir_of_file_exists(char_u *fname) return retval; } -/* - * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally - * and deal with 'fileignorecase'. - */ -int vim_fnamecmp(char_u *x, char_u *y) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamecmp(const char *fname1, const char *fname2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - return vim_fnamencmp(x, y, MAXPATHL); + const size_t len1 = strlen(fname1); + const size_t len2 = strlen(fname2); + return path_fnamencmp(fname1, fname2, MAX(len1, len2)); #else - if (p_fic) - return mb_stricmp(x, y); - return STRCMP(x, y); + return mb_strcmp_ic((bool)p_fic, fname1, fname2); #endif } -int vim_fnamencmp(char_u *x, char_u *y, size_t len) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// @param[in] len Compare at most len bytes. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamencmp(const char *const fname1, const char *const fname2, + const size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - char_u *px = x; - char_u *py = y; - int cx = NUL; - int cy = NUL; + int c1 = NUL; + int c2 = NUL; + const char *p1 = fname1; + const char *p2 = fname2; while (len > 0) { - cx = PTR2CHAR(px); - cy = PTR2CHAR(py); - if (cx == NUL || cy == NUL - || ((p_fic ? vim_tolower(cx) != vim_tolower(cy) : cx != cy) - && !(cx == '/' && cy == '\\') - && !(cx == '\\' && cy == '/'))) + c1 = PTR2CHAR(p1); + c2 = PTR2CHAR(p2); + if (c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) + || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; - len -= MB_PTR2LEN(px); - px += MB_PTR2LEN(px); - py += MB_PTR2LEN(py); + } + len -= MB_PTR2LEN(p1); + p1 += MB_PTR2LEN(p1); + p2 += MB_PTR2LEN(p2); } - if (len == 0) - return 0; - return cx - cy; + return c1 - c2; #else - if (p_fic) - return mb_strnicmp(x, y, len); - return STRNCMP(x, y, len); + if (p_fic) { + return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len); + } + return strncmp(fname1, fname2, len); #endif } diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ea00afbd86..6346951c05 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -610,13 +610,10 @@ static int pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, - (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, - (char_u *)"wipe", OPT_LOCAL); - set_option_value((char_u *)"diff", 0L, - NULL, OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); + set_option_value("diff", 0L, NULL, OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 323503c4f5..d23059ff22 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1236,7 +1236,7 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_nr = nr; if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */ type = 0; - qfp->qf_type = type; + qfp->qf_type = (char_u)type; qfp->qf_valid = valid; lastp = &qi->qf_lists[qi->qf_curlist].qf_last; @@ -2581,15 +2581,13 @@ void ex_copen(exarg_T *eap) else { /* Create a new quickfix buffer */ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); - /* switch off 'swapfile' */ - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix", - OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + // Switch off 'swapfile'. + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "quickfix", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); RESET_BINDING(curwin); - curwin->w_p_diff = FALSE; - set_option_value((char_u *)"fdm", 0L, (char_u *)"manual", - OPT_LOCAL); + curwin->w_p_diff = false; + set_option_value("fdm", 0L, "manual", OPT_LOCAL); } /* Only set the height when still in the same tab page and there is no @@ -2901,14 +2899,14 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) } } - /* correct cursor position */ - check_lnums(TRUE); + // Correct cursor position. + check_lnums(true); if (old_last == NULL) { // Set the 'filetype' to "qf" each time after filling the buffer. This // resembles reading a file into a buffer, it's more logical when using // autocommands. - set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); + set_option_value("ft", 0L, "qf", OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -4002,7 +4000,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; @@ -4057,7 +4055,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) int flags = QF_GETLIST_NONE; int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4070,15 +4068,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } - if (dict_find(what, (char_u *)"all", -1) != NULL) { + if (tv_dict_find(what, S_LEN("all")) != NULL) { flags |= QF_GETLIST_ALL; } - if (dict_find(what, (char_u *)"title", -1) != NULL) { + if (tv_dict_find(what, S_LEN("title")) != NULL) { flags |= QF_GETLIST_TITLE; } - if (dict_find(what, (char_u *)"winid", -1) != NULL) { + if (tv_dict_find(what, S_LEN("winid")) != NULL) { flags |= QF_GETLIST_WINID; } @@ -4132,17 +4130,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, if (d == NULL) continue; - char_u *filename = get_dict_string(d, "filename", true); - int bufnum = (int)get_dict_number(d, "bufnr"); - long lnum = get_dict_number(d, "lnum"); - int col = (int)get_dict_number(d, "col"); - char_u vcol = (char_u)get_dict_number(d, "vcol"); - int nr = (int)get_dict_number(d, "nr"); - char_u *type = get_dict_string(d, "type", true); - char_u *pattern = get_dict_string(d, "pattern", true); - char_u *text = get_dict_string(d, "text", true); + char *const filename = tv_dict_get_string(d, "filename", true); + int bufnum = (int)tv_dict_get_number(d, "bufnr"); + long lnum = tv_dict_get_number(d, "lnum"); + int col = (int)tv_dict_get_number(d, "col"); + char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); + int nr = (int)tv_dict_get_number(d, "nr"); + const char *type_str = tv_dict_get_string(d, "type", false); + const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); + char *const pattern = tv_dict_get_string(d, "pattern", true); + char *text = tv_dict_get_string(d, "text", true); if (text == NULL) { - text = vim_strsave((char_u *)""); + text = xcalloc(1, 1); } bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -4162,21 +4161,20 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, int status = qf_add_entry(qi, NULL, // dir - filename, + (char_u *)filename, bufnum, - text, + (char_u *)text, lnum, col, vcol, // vis_col - pattern, // search pattern + (char_u *)pattern, // search pattern nr, - (char_u)(type == NULL ? NUL : *type), + type, valid); xfree(filename); xfree(pattern); xfree(text); - xfree(type); if (status == FAIL) { retval = FAIL; @@ -4213,7 +4211,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) newlist = true; } int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4231,10 +4229,11 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) qf_idx = qi->qf_curlist; } - if ((di = dict_find(what, (char_u *)"title", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { if (di->di_tv.v_type == VAR_STRING) { xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = get_dict_string(what, "title", true); + qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( + what, "title", true); if (qf_idx == qi->qf_curlist) { qf_update_win_titlevar(qi); } diff --git a/src/nvim/search.c b/src/nvim/search.c index 8c56eda7cf..fb9e820928 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -282,7 +282,7 @@ void restore_search_patterns(void) static inline void free_spat(struct spat *const spat) { xfree(spat->pat); - dict_unref(spat->additional_data); + tv_dict_unref(spat->additional_data); } #if defined(EXITFREE) @@ -1290,10 +1290,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * ignored because we are interested in the next line -- Acevedo */ if ((compl_cont_status & CONT_ADDING) && !(compl_cont_status & CONT_SOL)) { - if ((p_ic ? mb_stricmp(p, pat) : STRCMP(p, pat)) == 0) + if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) { return OK; - } else if (*p != NUL) { /* ignore empty lines */ - /* expanding lines or words */ + } + } else if (*p != NUL) { // Ignore empty lines. + // Expanding lines or words. assert(compl_length >= 0); if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) : STRNCMP(p, pat, compl_length)) == 0) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c550cb0888..2fe042cda8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1623,10 +1623,10 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, for (const hashitem_T *hi= d->dv_hashtab.ht_array; todo; hi++) { \ if (!HASHITEM_EMPTY(hi)) { \ todo--; \ - dictitem_T *const di = HI2DI(hi); \ - const size_t key_len = strlen((const char *) hi->hi_key); \ + dictitem_T *const di = TV_DICT_HI2DI(hi); \ + const size_t key_len = strlen((const char *)hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ - msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ + msgpack_pack_str_body(spacker, (const char *)hi->hi_key, key_len); \ if (encode_vim_to_msgpack(spacker, &di->di_tv, \ _("additional data of ShaDa " what)) \ == FAIL) { \ @@ -3156,17 +3156,17 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - dict_unref(entry->data.filemark.additional_data); + tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; } case kSDItemSearchPattern: { - dict_unref(entry->data.search_pattern.additional_data); + tv_dict_unref(entry->data.search_pattern.additional_data); xfree(entry->data.search_pattern.pat); break; } case kSDItemRegister: { - dict_unref(entry->data.reg.additional_data); + tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } @@ -3192,7 +3192,7 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemBufferList: { for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - dict_unref(entry->data.buffer_list.buffers[i].additional_data); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 5ca5ab3339..56f0350aef 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7134,16 +7134,17 @@ void ex_spelldump(exarg_T *eap) char_u *spl; long dummy; - if (no_spell_checking(curwin)) + if (no_spell_checking(curwin)) { return; - get_option_value((char_u*)"spl", &dummy, &spl, OPT_LOCAL); + } + get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value("spell", true, "", OPT_LOCAL); + set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); if (!bufempty()) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index c108ae4a2c..6ba2801203 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5439,7 +5439,7 @@ static void init_spellfile(void) fname != NULL && strstr((char *)path_tail(fname), ".ascii.") != NULL ? (char_u *)"ascii" : spell_enc()); - set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); + set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } aspath = false; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 0a27d9dd92..632a5bf2a5 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -1406,14 +1406,14 @@ static int syn_stack_equal(synstate_T *sp) /* If the pointer is different it can still be the * same text. Compare the strings, ignore case when * the start item has the sp_ic flag set. */ - if (bsx->matches[j] == NULL - || six->matches[j] == NULL) + if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; - if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic - ? mb_stricmp(bsx->matches[j], - six->matches[j]) != 0 - : STRCMP(bsx->matches[j], six->matches[j]) != 0) + } + if (mb_strcmp_ic((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic, + (const char *)bsx->matches[j], + (const char *)six->matches[j]) != 0) { break; + } } } if (j != NSUBEXP) @@ -6510,16 +6510,16 @@ do_highlight ( if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { - if (t_colors < 16) + if (t_colors < 16) { i = (color == 0 || color == 4); - else + } else { i = (color < 7 || color == 8); - /* Set the 'background' option if the value is - * wrong. */ - if (i != (*p_bg == 'd')) - set_option_value((char_u *)"bg", 0L, - i ? (char_u *)"dark" - : (char_u *)"light", 0); + } + // Set the 'background' option if the value is + // wrong. + if (i != (*p_bg == 'd')) { + set_option_value("bg", 0L, (i ? "dark" : "light"), 0); + } } } } @@ -7113,7 +7113,7 @@ set_hl_attr ( * Lookup a highlight group name and return it's ID. * If it is not found, 0 is returned. */ -int syn_name2id(char_u *name) +int syn_name2id(const char_u *name) { int i; char_u name_u[200]; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index b0ffc12b5f..af19a1bfc4 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -773,7 +773,7 @@ do_tag ( cmd[len] = NUL; } - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); @@ -2768,8 +2768,8 @@ add_tag_field ( int len = 0; int retval; - /* check that the field name doesn't exist yet */ - if (dict_find(dict, (char_u *)field_name, -1) != NULL) { + // Check that the field name doesn't exist yet. + if (tv_dict_find(dict, field_name, -1) != NULL) { if (p_verbose > 0) { verbose_enter(); smsg(_("Duplicate field name: %s"), field_name); @@ -2824,7 +2824,7 @@ int get_tags(list_T *list, char_u *pat) if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) continue; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c81e883fb9..85c4950b42 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -226,17 +226,17 @@ Terminal *terminal_open(TerminalOptions opts) rv->invalid_start = 0; rv->invalid_end = opts.height; refresh_screen(rv, curbuf); - set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); + set_option_value("buftype", 0, "terminal", OPT_LOCAL); // Default settings for terminal buffers curbuf->b_p_ma = false; // 'nomodifiable' curbuf->b_p_ul = -1; // 'undolevels' curbuf->b_p_scbk = p_scbk; // 'scrollback' curbuf->b_p_tw = 0; // 'textwidth' - set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"list", false, NULL, OPT_LOCAL); + set_option_value("wrap", false, NULL, OPT_LOCAL); + set_option_value("number", false, NULL, OPT_LOCAL); + set_option_value("relativenumber", false, NULL, OPT_LOCAL); + set_option_value("list", false, NULL, OPT_LOCAL); buf_set_term_title(curbuf, (char *)curbuf->b_ffname); RESET_BINDING(curwin); // Reset cursor in current window. diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 729cf03e15..6e21dccebb 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -317,7 +317,7 @@ static long get_undolevel(void) static inline void zero_fmark_additional_data(fmark_T *fmarks) { for (size_t i = 0; i < NMARKS; i++) { - dict_unref(fmarks[i].additional_data); + tv_dict_unref(fmarks[i].additional_data); fmarks[i].additional_data = NULL; } } @@ -2941,7 +2941,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_T *dict; while (uhp != NULL) { - dict = dict_alloc(); + dict = tv_dict_alloc(); dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); if (uhp == curbuf->b_u_newhead) diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 87e9889465..ae654251f9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -109,11 +109,14 @@ Error: configure did not run properly.Check auto/config.log. // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) -/* directions */ -#define FORWARD 1 -#define BACKWARD (-1) -#define FORWARD_FILE 3 -#define BACKWARD_FILE (-3) +/// Directions. +typedef enum { + kDirectionNotSet = 0, + FORWARD = 1, + BACKWARD = (-1), + FORWARD_FILE = 3, + BACKWARD_FILE = (-3), +} Direction; /* return values for functions */ #if !(defined(OK) && (OK == 1)) @@ -282,15 +285,22 @@ enum { #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of % in statusline */ -/* - * fnamecmp() is used to compare file names. - * On some systems case in a file name does not matter, on others it does. - * (this does not account for maximum name lengths and things like "../dir", - * thus it is not 100% accurate!) - */ -#define fnamecmp(x, y) vim_fnamecmp((char_u *)(x), (char_u *)(y)) -#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \ - (size_t)(n)) +/// Compare file names +/// +/// On some systems case in a file name does not matter, on others it does. +/// +/// @note Does not account for maximum name lengths and things like "../dir", +/// thus it is not 100% accurate. OS may also use different algorythm for +/// case-insensitive comparison. +/// +/// @param[in] x First file name to compare. +/// @param[in] y Second file name to compare. +/// +/// @return 0 for equal file names, non-zero otherwise. +#define fnamecmp(x, y) path_fnamecmp((const char *)(x), (const char *)(y)) +#define fnamencmp(x, y, n) path_fnamencmp((const char *)(x), \ + (const char *)(y), \ + (size_t)(n)) /* * Enums need a typecast to be used as array index (for Ultrix). diff --git a/src/nvim/window.c b/src/nvim/window.c index 47a97da0cf..a3b0e6fc2d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2983,8 +2983,8 @@ static tabpage_T *alloc_tabpage(void) tp->handle = ++last_tp_handle; handle_register_tabpage(tp); - /* init t: variables */ - tp->tp_vars = dict_alloc(); + // Init t: variables. + tp->tp_vars = tv_dict_alloc(); init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); tp->tp_diff_invalid = TRUE; tp->tp_ch_used = p_ch; @@ -3811,8 +3811,8 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->handle = ++last_win_id; handle_register_window(new_wp); - /* init w: variables */ - new_wp->w_vars = dict_alloc(); + // Init w: variables. + new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); /* Don't execute autocommands while the window is not properly @@ -5504,9 +5504,9 @@ void restore_buffer(bufref_T *save_curbuf) // Optionally, a desired ID 'id' can be specified (greater than or equal to 1). // If no particular ID is desired, -1 must be specified for 'id'. // Return ID of added match, -1 on failure. -int match_add(win_T *wp, char_u *grp, char_u *pat, +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, list_T *pos_list, - char_u *conceal_char) + const char *const conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5534,11 +5534,11 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, cur = cur->next; } } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) { + if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { EMSG2(_(e_nogroup), grp); return -1; } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5557,14 +5557,14 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = pat == NULL ? NULL: vim_strsave(pat); + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); m->hlg_id = hlg_id; m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; m->conceal_char = 0; if (conceal_char != NULL) { - m->conceal_char = (*mb_ptr2char)(conceal_char); + m->conceal_char = (*mb_ptr2char)((const char_u *)conceal_char); } // Set up position matches -- cgit From 54bd2e8b731376b29c150f909c6b50103bfbb91a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:25:57 +0300 Subject: eval: Make setmatches() return -1 in case of some failures --- src/nvim/eval.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5015deead7..7ec1fda0a8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14680,6 +14680,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_matches(curwin); li = l->lv_first; + bool match_add_failed = false; while (li != NULL) { int i = 0; @@ -14728,17 +14729,23 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) &conceal_di->di_tv) : NULL); if (i == 0) { - match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal); + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } } else { - match_add(curwin, group, NULL, priority, id, s, conceal); + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } tv_list_unref(s); s = NULL; } li = li->li_next; } - rettv->vval.v_number = 0; + if (!match_add_failed) { + rettv->vval.v_number = 0; + } } } -- cgit From ecff8387f465bbf5d57de09498059ff1de671131 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:12:15 +0300 Subject: eval: Move get_dict_callback to typval.c --- src/nvim/eval.c | 265 ++++++++++++++++++++++++------------------------- src/nvim/eval/typval.c | 36 +++++++ 2 files changed, 164 insertions(+), 137 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7ec1fda0a8..8d6b6d2dc1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -729,8 +729,8 @@ void set_internal_string_var(char_u *name, char_u *value) } static lval_T *redir_lval = NULL; -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /* @@ -761,9 +761,9 @@ var_redir_start ( /* The output is stored in growarray "redir_ga" until redirection ends. */ ga_init(&redir_ga, (int)sizeof(char), 500); - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); + // Parse the variable name (can be a dict or list entry). + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) { clear_lval(redir_lval); @@ -839,12 +839,13 @@ void var_redir_stop(void) ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ tv.v_type = VAR_STRING; tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)"."); + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)"."); + } clear_lval(redir_lval); } @@ -1457,11 +1458,13 @@ void ex_let(exarg_T *eap) char_u *argend; int first = TRUE; - argend = skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) + argend = (char_u *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ - --argend; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } expr = skipwhite(argend); if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL && expr[1] == '=')) { @@ -1527,7 +1530,7 @@ ex_let_vars ( char_u *nextchars ) { - char_u *arg = arg_start; + char_u *arg = arg_start; list_T *l; int i; listitem_T *item; @@ -1606,9 +1609,11 @@ ex_let_vars ( * for "[var, var; var]" set "semicolon". * Return NULL for an error. */ -static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) +static const char_u *skip_var_list(const char_u *arg, int *var_count, + int *semicolon) { - char_u *p, *s; + const char_u *p; + const char_u *s; if (*arg == '[') { /* "[var, var]": find the matching ']'. */ @@ -1645,7 +1650,7 @@ static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, * l[idx]. */ -static char_u *skip_var_one(char_u *arg) +static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) return arg + 2; @@ -1825,34 +1830,36 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. - * Returns a pointer to the char just after the var name. - * Returns NULL if there is an error. - */ -static char_u * -ex_let_one ( - char_u *arg, /* points to variable name */ - typval_T *tv, /* value to assign to variable */ - int copy, /* copy value from "tv" */ - char_u *endchars, /* valid chars after variable name or NULL */ - char_u *op /* "+", "-", "." or NULL*/ -) +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char_u *ex_let_one(char_u *arg, typval_T *const tv, + const bool copy, const char_u *const endchars, + const char_u *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; - char_u *arg_end = NULL; + char_u *name; + char_u *arg_end = NULL; int len; int opt_flags; - char_u *tofree = NULL; + char_u *tofree = NULL; /* * ":let $VAR = expr": Set environment variable. */ if (*arg == '$') { - /* Find the end of the name. */ - ++arg; + // Find the end of the name. + arg++; name = arg; - len = get_env_len(&arg); + len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); } else { @@ -1975,9 +1982,9 @@ ex_let_one ( char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); - else { + } else { set_var_lval(&lv, p, tv, copy, op); arg_end = p; } @@ -2020,7 +2027,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, FUNC_ATTR_NONNULL_ARG(1, 3) { char_u *p; - char_u *expr_start, *expr_end; int cc; dictitem_T *v; typval_T var1; @@ -2036,13 +2042,17 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, memset(lp, 0, sizeof(lval_T)); if (skip) { - /* When skipping just find the end of the name. */ - lp->ll_name = name; - return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + // When skipping just find the end of the name. + lp->ll_name = (char_u *)name; + return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); } - /* Find the end of the name. */ - p = find_name_end(name, &expr_start, &expr_end, fne_flags); + // Find the end of the name. + char_u *expr_start; + char_u *expr_end; + p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2063,8 +2073,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; - } else - lp->ll_name = name; + } else { + lp->ll_name = (char_u *)name; + } /* Without [idx] or .key we are done. */ if ((*p != '[' && *p != '.') || lp->ll_name == NULL) @@ -2351,7 +2362,8 @@ static void clear_lval(lval_T *lp) * "endp" points to just after the parsed name. * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". */ -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op) +static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, + int copy, const char_u *op) { int cc; listitem_T *ri; @@ -2493,10 +2505,10 @@ notify: * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) +void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - char_u *expr; + const char_u *expr; typval_T tv; list_T *l; @@ -2824,16 +2836,17 @@ void ex_lockvar(exarg_T *eap) static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) { char_u *arg = argstart; - char_u *name_end; - int error = FALSE; + bool error = false; lval_T lv; do { - /* Parse the name and find the end. */ - name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, - FNE_CHECK_START); - if (lv.ll_name == NULL) - error = TRUE; /* error but continue parsing */ + // Parse the name and find the end. + char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true, + eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } if (name_end == NULL || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { if (name_end != NULL) { @@ -2868,7 +2881,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) // TODO(ZyX-I): move to eval/ex_cmds -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) +static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) { int ret = OK; int cc; @@ -3022,7 +3035,7 @@ int do_unlet(char_u *name, int forceit) * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, +static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, const bool lock) { int ret = OK; @@ -5605,34 +5618,6 @@ void dict_set_keys_readonly(dict_T *const dict) }); } -/// Get a function from a dictionary -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. -/// @return true/false on success/failure. -static bool get_dict_callback(dict_T *d, char *key, Callback *result) -{ - dictitem_T *const di = tv_dict_find(d, key, -1); - - if (di == NULL) { - result->type = kCallbackNone; - return true; - } - - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); - result->type = kCallbackNone; - return false; - } - - typval_T tv; - copy_tv(&di->di_tv, &tv); - set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); - tv_clear(&tv); - return res; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -5997,7 +5982,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) ++*arg; name = *arg; - len = get_env_len(arg); + len = get_env_len((const char_u **)arg); if (evaluate) { if (len == 0) { @@ -11291,16 +11276,16 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; - char_u *end; dictitem_T *di; rettv->vval.v_number = -1; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, false, false, - GLV_NO_AUTOLOAD|GLV_READ_ONLY, FNE_CHECK_START); + const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + &lv, false, false, + GLV_NO_AUTOLOAD, FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { - if (*end != NUL) + if (*end != NUL) { EMSG(_(e_trailing)); - else { + } else { if (lv.ll_tv == NULL) { di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); if (di != NULL) { @@ -16580,7 +16565,8 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } -static bool callback_from_typval(Callback *callback, typval_T *arg) +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { callback->data.partial = arg->vval.v_partial; @@ -17698,15 +17684,16 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) * Advance "arg" to the first character after the name. * Return 0 for error. */ -static int get_env_len(char_u **arg) +static int get_env_len(const char_u **arg) { - char_u *p; int len; - for (p = *arg; vim_isIDc(*p); ++p) - ; - if (p == *arg) /* no name found */ + const char_u *p; + for (p = *arg; vim_isIDc(*p); p++) { + } + if (p == *arg) { // No name found. return 0; + } len = (int)(p - *arg); *arg = p; @@ -17758,8 +17745,6 @@ static int get_name_len(const char **const arg, int verbose) { int len; - char_u *expr_start; - char_u *expr_end; *alias = NULL; /* default to no alias */ @@ -17775,12 +17760,12 @@ static int get_name_len(const char **const arg, *arg += len; } - /* - * Find the end of the name; check for {} construction. - */ + // Find the end of the name; check for {} construction. + char_u *expr_start; + char_u *expr_end; const char *p = (const char *)find_name_end((char_u *)(*arg), - &expr_start, - &expr_end, + (const char_u **)&expr_start, + (const char_u **)&expr_end, len > 0 ? 0 : FNE_CHECK_START); if (expr_start != NULL) { if (!evaluate) { @@ -17816,12 +17801,11 @@ static int get_name_len(const char **const arg, // "flags" can have FNE_INCL_BR and FNE_CHECK_START. // Return a pointer to just after the name. Equal to "arg" if there is no // valid name. -static char_u *find_name_end(char_u *arg, char_u **expr_start, - char_u **expr_end, int flags) +static const char_u *find_name_end(const char_u *arg, const char_u **expr_start, + const char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; int len; if (expr_start != NULL) { @@ -17834,6 +17818,7 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, return arg; } + const char_u *p; for (p = arg; *p != NUL && (eval_isnamec(*p) || *p == '{' @@ -17905,7 +17890,8 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, * Returns a new allocated string, which the caller must free. * Returns NULL for failure. */ -static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) +static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, + char_u *expr_end, char_u *in_end) { char_u c1; char_u *retval = NULL; @@ -17934,7 +17920,9 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u * *expr_end = '}'; if (retval != NULL) { - temp_result = find_name_end(retval, &expr_start, &expr_end, 0); + temp_result = (char_u *)find_name_end(retval, + (const char_u **)&expr_start, + (const char_u **)&expr_end, 0); if (expr_start != NULL) { /* Further expansion! */ temp_result = make_expanded_name(retval, expr_start, @@ -18351,7 +18339,7 @@ handle_subscript( return ret; } -static void set_selfdict(typval_T *rettv, dict_T *selfdict) +void set_selfdict(typval_T *rettv, dict_T *selfdict) { // Don't do this when "dict.Func" is already a partial that was bound // explicitly (pt_auto is false). @@ -20195,15 +20183,15 @@ ret_free: static char_u * trans_function_name( char_u **pp, - int skip, /* only find the end, don't evaluate */ + int skip, // only find the end, don't evaluate int flags, funcdict_T *fdp, // return: info about dictionary used partial_T **partial // return: partial of a FuncRef ) { char_u *name = NULL; - char_u *start; - char_u *end; + const char_u *start; + const char_u *end; int lead; char_u sid_buf[20]; int len; @@ -20229,9 +20217,9 @@ trans_function_name( start += lead; } - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); + // Note that TFN_ flags use the same values as GLV_ flags. + end = get_lval((char_u *)start, NULL, &lv, false, skip, flags, + lead > 2 ? 0 : FNE_CHECK_START); if (end == start) { if (!skip) EMSG(_("E129: Function name required")); @@ -20244,10 +20232,12 @@ trans_function_name( * interrupt, or an exception. */ if (!aborting()) { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); + if (end != NULL) { + emsgf(_(e_invarg2), start); + } + } else { + *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR); + } goto theend; } @@ -20260,11 +20250,11 @@ trans_function_name( } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; + *pp = (char_u *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); - *pp = end; + *pp = (char_u *)end; if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; } @@ -20274,7 +20264,7 @@ trans_function_name( || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); } else { - *pp = end; + *pp = (char_u *)end; } name = NULL; } @@ -20282,8 +20272,8 @@ trans_function_name( } if (lv.ll_name == NULL) { - /* Error found, but continue after the function name. */ - *pp = end; + // Error found, but continue after the function name. + *pp = (char_u *)end; goto theend; } @@ -20305,7 +20295,7 @@ trans_function_name( } if (name != NULL) { name = vim_strsave(name); - *pp = end; + *pp = (char_u *)end; if (strncmp((char *)name, "", 5) == 0) { // Change "" to the byte sequence. name[0] = K_SPECIAL; @@ -20379,7 +20369,7 @@ trans_function_name( } memmove(name + lead, lv.ll_name, (size_t)len); name[lead + len] = NUL; - *pp = end; + *pp = (char_u *)end; theend: clear_lval(&lv); @@ -20408,8 +20398,8 @@ static int eval_fname_script(const char *const p) /// Check whether function name starts with or s: /// -/// Only works for names previously checked by eval_fname_script(), if it -/// returned non-zero. +/// @warning Only works for names previously checked by eval_fname_script(), if +/// it returned non-zero. /// /// @param[in] name Name to check. /// @@ -20558,14 +20548,15 @@ bool translated_function_exists(const char *name) /// @return True if it exists, false otherwise. static bool function_exists(const char *const name, bool no_deref) { - char_u *nm = (char_u *)name; + const char_u *nm = (const char_u *)name; bool n = false; int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD; if (no_deref) { flag |= TFN_NO_DEREF; } - char *const p = (char *)trans_function_name(&nm, false, flag, NULL, NULL); + char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL, + NULL); nm = skipwhite(nm); /* Only accept "funcname", "funcname ", "funcname (..." and @@ -22510,9 +22501,9 @@ static inline TerminalJobData *common_job_init(char **argv, static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, Callback *on_stderr, Callback *on_exit) { - if (get_dict_callback(vopts, "on_stdout", on_stdout) - && get_dict_callback(vopts, "on_stderr", on_stderr) - && get_dict_callback(vopts, "on_exit", on_exit)) { + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { vopts->dv_refcount++; return true; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index bb7baed7d2..f4f34e0d92 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1130,6 +1130,42 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); } +/// Get a function from a dictionary +/// +/// @param[in] d Dictionary to get callback from. +/// @param[in] key Dictionary key. +/// @param[in] key_len Key length, may be -1 to use strlen(). +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. +/// +/// @return true/false on success/failure. +bool tv_dict_get_callback(dict_T *const d, + const char *const key, const ptrdiff_t key_len, + Callback *const result) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + result->type = kCallbackNone; + + dictitem_T *const di = tv_dict_find(d, key, key_len); + + if (di == NULL) { + return true; + } + + if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING + && di->di_tv.v_type != VAR_PARTIAL) { + EMSG(_("Argument is not a function or function name")); + return false; + } + + typval_T tv; + copy_tv(&di->di_tv, &tv); + set_selfdict(&tv, d); + bool res = callback_from_typval(result, &tv); + tv_clear(&tv); + return res; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -- cgit From 983a5532ca17060508ea308a9b7069dae4facb59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:14:50 +0300 Subject: eval: Move dict_set_keys_readonly to typval.c --- src/nvim/eval.c | 15 +-------------- src/nvim/eval/typval.c | 17 +++++++++++++++-- src/nvim/file_search.c | 2 +- src/nvim/ops.c | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8d6b6d2dc1..afbb93ef92 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5605,19 +5605,6 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) return OK; } -/// Set all existing keys in "dict" as read-only. -/// -/// This does not protect against adding new keys to the Dictionary. -/// -/// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *const dict) - FUNC_ATTR_NONNULL_ALL -{ - TV_DICT_ITER(dict, di, { - di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - }); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -18083,7 +18070,7 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) if (val != NULL) { val->dv_refcount++; // Set readonly - dict_set_keys_readonly(val); + tv_dict_set_keys_readonly(val); } } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f4f34e0d92..0df818040b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1135,8 +1135,8 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, /// @param[in] d Dictionary to get callback from. /// @param[in] key Dictionary key. /// @param[in] key_len Key length, may be -1 to use strlen(). -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. /// /// @return true/false on success/failure. bool tv_dict_get_callback(dict_T *const d, @@ -1348,6 +1348,19 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, return copy; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen. +void tv_dict_set_keys_readonly(dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 8ab6955042..d7120a7309 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1551,7 +1551,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d634a8c393..4bca2f0889 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2578,7 +2578,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) buf[1] = NUL; dict_add_nr_str(dict, "operator", 0, (char_u *)buf); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; -- cgit From 210342d795a13e88579d3e55ae931463d16f241d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 11 Jan 2017 23:20:03 +0300 Subject: eval: Move dict_add_list and dict_add_dict to typval.c --- src/nvim/eval.c | 57 ++++++++++---------------------------------------- src/nvim/eval/typval.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/nvim/ops.c | 3 ++- src/nvim/undo.c | 3 ++- 4 files changed, 67 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index afbb93ef92..e7c2d95cdd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5569,42 +5569,6 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) return OK; } -/* - * Add a list entry to dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/// Add a dict entry to dictionary "d". -/// Returns FAIL when out of memory and when key already exists. -int dict_add_dict(dict_T *d, char *key, dict_T *dict) -{ - dictitem_T *const item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_DICT; - item->di_tv.vval.v_dict = dict; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - dict->dv_refcount++; - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9128,7 +9092,7 @@ static dict_T *get_buffer_info(buf_T *buf) NULL); // Get a reference to buffer variables - dict_add_dict(dict, "variables", buf->b_vars); + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer list_T *const windows = tv_list_alloc(); @@ -9137,13 +9101,13 @@ static dict_T *get_buffer_info(buf_T *buf) tv_list_append_number(windows, (varnumber_T)wp->handle); } } - dict_add_list(dict, "windows", windows); + tv_dict_add_list(dict, S_LEN("windows"), windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); - dict_add_list(dict, "signs", signs); + tv_dict_add_list(dict, S_LEN("signs"), signs); } return dict; @@ -9900,7 +9864,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { llpos_T *llpos; - char buf[6]; + char buf[6]; llpos = &cur->pos.pos[i]; if (llpos->lnum == 0) { @@ -9912,8 +9876,9 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(l, (varnumber_T)llpos->col); tv_list_append_number(l, (varnumber_T)llpos->len); } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); } } else { dict_add_nr_str(dict, "pattern", 0L, cur->pattern); @@ -10082,10 +10047,10 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } - dict_add_list(dict, "windows", l); + tv_dict_add_list(dict, S_LEN("windows"), l); // Make a reference to tabpage variables - dict_add_dict(dict, "variables", tp->tp_vars); + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); return dict; } @@ -10187,7 +10152,7 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) NULL); // Add a reference to window variables - dict_add_dict(dict, "variables", wp->w_vars); + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); return dict; } @@ -17029,7 +16994,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); + tv_dict_add_list(dict, S_LEN("entries"), list); } /* diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0df818040b..f9885926c7 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1166,6 +1166,58 @@ bool tv_dict_get_callback(dict_T *const d, return res; } +//{{{2 dict_add* + +/// Add a list entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param list List to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_list(dict_T *const d, const char *const key, + const size_t key_len, list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + list->lv_refcount++; + return OK; +} + +/// Add a dictionary entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param dict Dictionary to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_dict(dict_T *const d, const char *const key, + const size_t key_len, dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc(key); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_DICT; + item->di_tv.vval.v_dict = dict; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + dict->dv_refcount++; + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4bca2f0889..28ac5aad4e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -42,6 +42,7 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/window.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -2561,7 +2562,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; - dict_add_list(dict, "regcontents", list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type char buf[NUMBUFLEN+2]; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6e21dccebb..8c75d28f2a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -2956,7 +2957,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - dict_add_list(dict, "alt", alt_list); + tv_dict_add_list(dict, S_LEN("alt"), alt_list); } tv_list_append_dict(list, dict); -- cgit From 2dcfc439b2ec2be4d830826061e89860977516be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:56:49 +0300 Subject: eval: Split and move dict_add_nr_str to typval.c Function was split into tv_dict_add_nr() and tv_dict_add_str(). --- src/nvim/edit.c | 24 +++++---- src/nvim/eval.c | 143 ++++++++++++++++++++----------------------------- src/nvim/eval/typval.c | 50 ++++++++++++++++- src/nvim/eval/typval.h | 1 + src/nvim/file_search.c | 4 +- src/nvim/ops.c | 31 ++++++----- src/nvim/option.c | 5 +- src/nvim/quickfix.c | 44 +++++++++------ src/nvim/search.c | 5 +- src/nvim/tag.c | 29 +++++----- src/nvim/undo.c | 21 ++++---- 11 files changed, 202 insertions(+), 155 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 53b196c61f..d1cceb435a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4034,16 +4034,20 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc(); - dict_add_nr_str(dict, "word", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_str)); - dict_add_nr_str(dict, "abbr", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); - dict_add_nr_str(dict, "menu", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); - dict_add_nr_str(dict, "kind", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); - dict_add_nr_str(dict, "info", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + tv_dict_add_str( + dict, S_LEN("abbr"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); + tv_dict_add_str( + dict, S_LEN("menu"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); + tv_dict_add_str( + dict, S_LEN("kind"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); + tv_dict_add_str( + dict, S_LEN("info"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) { compl_curr_match = compl_shown_match; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e7c2d95cdd..a64eb3c959 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5544,31 +5544,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when key already exists. - */ -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) -{ - dictitem_T *item; - - item = tv_dict_item_alloc(key); - item->di_tv.v_lock = 0; - if (str == NULL) { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } else { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9066,9 +9041,10 @@ static void get_buffer_signs(buf_T *buf, list_T *l) for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); - dict_add_nr_str(d, "id", sign->id, NULL); - dict_add_nr_str(d, "lnum", sign->lnum, NULL); - dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), + (const char *)sign_typenr2name(sign->typenr)); tv_list_append_dict(l, d); } @@ -9079,17 +9055,16 @@ static dict_T *get_buffer_info(buf_T *buf) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); - dict_add_nr_str(dict, "name", 0L, - buf->b_ffname != NULL ? buf->b_ffname : (char_u *)""); - dict_add_nr_str(dict, "lnum", buflist_findlnum(buf), NULL); - dict_add_nr_str(dict, "loaded", buf->b_ml.ml_mfp != NULL, NULL); - dict_add_nr_str(dict, "listed", buf->b_p_bl, NULL); - dict_add_nr_str(dict, "changed", bufIsChanged(buf), NULL); - dict_add_nr_str(dict, "changedtick", buf->b_changedtick, NULL); - dict_add_nr_str(dict, "hidden", - buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0, - NULL); + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf->b_changedtick); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); // Get a reference to buffer variables tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); @@ -9411,9 +9386,9 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "char", 0L, last_csearch()); - dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); - dict_add_nr_str(dict, "until", last_csearch_until(), NULL); + tv_dict_add_str(dict, S_LEN("char"), last_csearch()); + tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward()); + tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } /* @@ -9881,17 +9856,18 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, buf, (size_t)len, l); } } else { - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); } - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); if (cur->conceal_char) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); } tv_list_append_dict(rettv->vval.v_list, dict); @@ -10041,7 +10017,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tp_idx, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { @@ -10139,17 +10115,16 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tpnr, NULL); - dict_add_nr_str(dict, "winnr", winnr, NULL); - dict_add_nr_str(dict, "winid", wp->handle, NULL); - dict_add_nr_str(dict, "height", wp->w_height, NULL); - dict_add_nr_str(dict, "width", wp->w_width, NULL); - dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); - dict_add_nr_str(dict, "quickfix", bt_quickfix(wp->w_buffer), NULL); - dict_add_nr_str(dict, "loclist", - (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL), - NULL); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); // Add a reference to window variables tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); @@ -12074,15 +12049,15 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) char *const mapmode = map_mode_to_chars(mp->m_mode); dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, (char_u *)mapmode); + tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); + tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_local); + tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + tv_dict_add_str(dict, S_LEN("mode"), mapmode); xfree(lhs); xfree(mapmode); @@ -16984,13 +16959,13 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; list_T *list; - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), + (varnumber_T)curbuf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); @@ -17223,16 +17198,16 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); + tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); + tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); + tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); + tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); + tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); + tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); + tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); + tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } /// Writes list of strings to file diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f9885926c7..62460bcc3a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1180,7 +1180,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, const size_t key_len, list_T *const list) FUNC_ATTR_NONNULL_ALL { - dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; @@ -1218,6 +1218,54 @@ int tv_dict_add_dict(dict_T *const d, const char *const key, return OK; } +/// Add a number entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] nr Number to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_nr(dict_T *const d, const char *const key, + const size_t key_len, const varnumber_T nr) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a string entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str(dict_T *const d, + const char *const key, const size_t key_len, + const char *const val) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = (char_u *)xstrdup(val); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6183397d12..6929e37e43 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "nvim/hashtab.h" #include "nvim/garray.h" diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index d7120a7309..f7932bc296 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1549,8 +1549,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) assert(false); } - dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); - dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); + tv_dict_add_str(dict, S_LEN("scope"), buf); + tv_dict_add_str(dict, S_LEN("cwd"), new_dir); tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 28ac5aad4e..fd1e2d20f2 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -14,6 +14,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" @@ -2567,17 +2568,17 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) // the register type char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // name of requested register or the empty string for an unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; - dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // kind of operation (yank/delete/change) buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); tv_dict_set_keys_readonly(dict); textlock++; @@ -5484,17 +5485,19 @@ void cursor_pos_info(dict_T *dict) if (dict != NULL) { // Don't shorten this message, the user asked for it. - dict_add_nr_str(dict, "words", word_count, NULL); - dict_add_nr_str(dict, "chars", char_count, NULL); - dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); - - dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", - byte_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", - char_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words", - word_count_cursor, NULL); - } + tv_dict_add_nr(dict, S_LEN("words"), (varnumber_T)word_count); + tv_dict_add_nr(dict, S_LEN("chars"), (varnumber_T)char_count); + tv_dict_add_nr(dict, S_LEN("bytes"), (varnumber_T)(byte_count + bom_count)); + + STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), + "key_len argument in tv_dict_add_nr is wrong"); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", + sizeof("visual_bytes") - 1, (varnumber_T)byte_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", + sizeof("visual_chars") - 1, (varnumber_T)char_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", + sizeof("visual_words") - 1, (varnumber_T)word_count_cursor); + } } /// Check if the default register (used in an unnamed paste) should be a diff --git a/src/nvim/option.c b/src/nvim/option.c index 4c8606a999..1881b329ec 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -6971,9 +6971,10 @@ dict_T *get_winbuf_options(const int bufopt) if (varp != NULL) { if (opt->flags & P_STRING) { - dict_add_nr_str(d, opt->fullname, 0L, *(char_u **)varp); + tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), + *(const char **)varp); } else { - dict_add_nr_str(d, opt->fullname, *varp, NULL); + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp); } } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d23059ff22..06ac2821b0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3972,7 +3972,6 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) int get_errorlist(win_T *wp, int qf_idx, list_T *list) { qf_info_T *qi = &ql_info; - dict_T *dict; char_u buf[2]; qfline_T *qfp; int i; @@ -4000,23 +3999,34 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = tv_dict_alloc(); + dict_T *const dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; - if ( dict_add_nr_str(dict, "bufnr", (long)bufnum, NULL) == FAIL - || dict_add_nr_str(dict, "lnum", (long)qfp->qf_lnum, NULL) == FAIL - || dict_add_nr_str(dict, "col", (long)qfp->qf_col, NULL) == FAIL - || dict_add_nr_str(dict, "vcol", (long)qfp->qf_viscol, NULL) == FAIL - || dict_add_nr_str(dict, "nr", (long)qfp->qf_nr, NULL) == FAIL - || dict_add_nr_str(dict, "pattern", 0L, - qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL - || dict_add_nr_str(dict, "text", 0L, - qfp->qf_text == NULL ? (char_u *)"" : qfp->qf_text) == FAIL - || dict_add_nr_str(dict, "type", 0L, buf) == FAIL - || dict_add_nr_str(dict, "valid", (long)qfp->qf_valid, NULL) == FAIL) - return FAIL; + if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL + || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) + || tv_dict_add_str(dict, S_LEN("pattern"), + (qfp->qf_pattern == NULL + ? "" + : (const char *)qfp->qf_pattern)) == FAIL + || tv_dict_add_str(dict, S_LEN("text"), + (qfp->qf_text == NULL + ? "" + : (const char *)qfp->qf_text)) == FAIL + || tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL + || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) + == FAIL)) { + // tv_dict_add* fail only if key already exist, but this is a newly + // allocated dictionary which is thus guaranteed to have no existing keys. + assert(false); + } qfp = qfp->qf_next; if (qfp == NULL) { @@ -4085,15 +4095,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (t == NULL) { t = (char_u *)""; } - status = dict_add_nr_str(retdict, "title", 0L, t); + status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t); } if ((status == OK) && (flags & QF_GETLIST_NR)) { - status = dict_add_nr_str(retdict, "nr", qf_idx + 1, NULL); + status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1); } if ((status == OK) && (flags & QF_GETLIST_WINID)) { win_T *win = qf_find_win(qi); if (win != NULL) { - status = dict_add_nr_str(retdict, "winid", win->handle, NULL); + status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } diff --git a/src/nvim/search.c b/src/nvim/search.c index fb9e820928..c5c92b41c5 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -356,9 +356,10 @@ int pat_has_uppercase(char_u *pat) return FALSE; } -char_u *last_csearch(void) +const char *last_csearch(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return lastc_bytes; + return (const char *)lastc_bytes; } int last_csearch_forward(void) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index af19a1bfc4..b812dd2ffd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -776,11 +776,12 @@ do_tag ( dict = tv_dict_alloc(); tv_list_append_dict(list, dict); - dict_add_nr_str(dict, "text", 0L, tag_name); - dict_add_nr_str(dict, "filename", 0L, fname); - dict_add_nr_str(dict, "lnum", lnum, NULL); - if (lnum == 0) - dict_add_nr_str(dict, "pattern", 0L, cmd); + tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); + tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); + tv_dict_add_nr(dict, S_LEN("lnum"), lnum); + if (lnum == 0) { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); + } } vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); @@ -2203,7 +2204,7 @@ parse_tag_line ( * Return TRUE if it is a static tag and adjust *tagname to the real tag. * Return FALSE if it is not a static tag. */ -static int test_for_static(tagptrs_T *tagp) +static bool test_for_static(tagptrs_T *tagp) { char_u *p; @@ -2790,7 +2791,8 @@ add_tag_field ( STRLCPY(buf, start, len + 1); } buf[len] = NUL; - retval = dict_add_nr_str(dict, field_name, 0L, buf); + retval = tv_dict_add_str(dict, field_name, STRLEN(field_name), + (const char *)buf); xfree(buf); return retval; } @@ -2806,7 +2808,7 @@ int get_tags(list_T *list, char_u *pat) char_u *full_fname; dict_T *dict; tagptrs_T tp; - long is_static; + bool is_static; ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); @@ -2829,14 +2831,13 @@ int get_tags(list_T *list, char_u *pat) full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL - || add_tag_field(dict, "filename", full_fname, - NULL) == FAIL - || add_tag_field(dict, "cmd", tp.command, - tp.command_end) == FAIL + || add_tag_field(dict, "filename", full_fname, NULL) == FAIL + || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL || add_tag_field(dict, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || dict_add_nr_str(dict, "static", is_static, NULL) == FAIL) + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { ret = FAIL; + } xfree(full_fname); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8c75d28f2a..ba687bf6da 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2943,19 +2943,22 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) while (uhp != NULL) { dict = tv_dict_alloc(); - dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); - dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); - if (uhp == curbuf->b_u_newhead) - dict_add_nr_str(dict, "newhead", 1, NULL); - if (uhp == curbuf->b_u_curhead) - dict_add_nr_str(dict, "curhead", 1, NULL); - if (uhp->uh_save_nr > 0) - dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); + tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); + if (uhp == curbuf->b_u_newhead) { + tv_dict_add_nr(dict, S_LEN("newhead"), 1); + } + if (uhp == curbuf->b_u_curhead) { + tv_dict_add_nr(dict, S_LEN("curhead"), 1); + } + if (uhp->uh_save_nr > 0) { + tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); + } if (uhp->uh_alt_next.ptr != NULL) { list_T *alt_list = tv_list_alloc(); - /* Recursive call to add alternate undo tree. */ + // Recursive call to add alternate undo tree. u_eval_tree(uhp->uh_alt_next.ptr, alt_list); tv_dict_add_list(dict, S_LEN("alt"), alt_list); } -- cgit From 5cdf7177ec71e4b9b3295ead93bedf7ff226a2b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 00:20:01 +0300 Subject: eval: Move get_float_arg to typval.h Assuming `inline` is there for a reason, so it is kept and function was moved to typval.h and not to typval.c which does not have problems with #including message.h. --- src/nvim/eval.c | 60 +++++++++++++++++++------------------------------- src/nvim/eval/typval.h | 27 +++++++++++++++++++++++ src/nvim/gettext.h | 21 ++++++++++++++++++ src/nvim/vim.h | 17 +------------- 4 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/nvim/gettext.h (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a64eb3c959..1d060eff33 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6473,24 +6473,6 @@ static int non_zero_arg(typval_T *argvars) */ -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ -static inline int get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - // Apply a floating point C function on a typval with one float_T. // // Some versions of glibc on i386 have an optimization that makes it harder to @@ -6502,7 +6484,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) { + if (tv_get_float(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6957,14 +6939,15 @@ static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -8557,13 +8540,14 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (get_float_arg(argvars, &f) == OK) { - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else + if (tv_get_float(argvars, &f)) { + if (f < VARNUMBER_MIN) { + rettv->vval.v_number = VARNUMBER_MIN; + } else if (f > VARNUMBER_MAX) { + rettv->vval.v_number = VARNUMBER_MAX; + } else { rettv->vval.v_number = (varnumber_T)f; + } } } @@ -8572,14 +8556,15 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -12790,14 +12775,15 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6929e37e43..0eef2ddaac 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -13,6 +13,7 @@ #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T +#include "nvim/gettext.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -370,6 +371,32 @@ extern bool tv_in_free_unref_items; } \ }) +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; + +// FIXME circular dependency, cannot import message.h. +bool emsgf(const char *const fmt, ...); + +/// Get the float value +/// +/// @param[in] tv VimL object to get value from. +/// @param[out] ret_f Location where resulting float is stored. +/// +/// @return true in case of success, false if tv is not a number or float. +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +{ + if (tv->v_type == VAR_FLOAT) { + *ret_f = tv->vval.v_float; + return true; + } + if (tv->v_type == VAR_NUMBER) { + *ret_f = (float_T)tv->vval.v_number; + return true; + } + emsgf(_("E808: Number or Float required")); + return false; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h new file mode 100644 index 0000000000..aa0e97233e --- /dev/null +++ b/src/nvim/gettext.h @@ -0,0 +1,21 @@ +#ifndef NVIM_GETTEXT_H +#define NVIM_GETTEXT_H + +#ifdef HAVE_WORKING_LIBINTL +# include +# define _(x) gettext((char *)(x)) +// XXX do we actually need this? +# ifdef gettext_noop +# define N_(x) gettext_noop(x) +# else +# define N_(x) x +# endif +#else +# define _(x) ((char *)(x)) +# define N_(x) x +# define bindtextdomain(x, y) // empty +# define bind_textdomain_codeset(x, y) // empty +# define textdomain(x) // empty +#endif + +#endif // NVIM_GETTEXT_H diff --git a/src/nvim/vim.h b/src/nvim/vim.h index ae654251f9..e16ee00309 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -51,22 +51,7 @@ Error: configure did not run properly.Check auto/config.log. /* ================ end of the header file puzzle =============== */ -#ifdef HAVE_WORKING_LIBINTL -# include -# define _(x) gettext((char *)(x)) -// XXX do we actually need this? -# ifdef gettext_noop -# define N_(x) gettext_noop(x) -# else -# define N_(x) x -# endif -#else -# define _(x) ((char *)(x)) -# define N_(x) x -# define bindtextdomain(x, y) /* empty */ -# define bind_textdomain_codeset(x, y) /* empty */ -# define textdomain(x) /* empty */ -#endif +#include "nvim/gettext.h" /* special attribute addition: Put message in history */ #define MSG_HIST 0x1000 -- cgit From 28dafe3ff0b0dc082fb62b2251fd64a167ce7188 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:16:47 +0300 Subject: eval,*: Move get_tv_string to typval.c Function was renamed and changed to return `const char *`. --- src/nvim/api/private/helpers.c | 2 +- src/nvim/buffer.c | 6 +- src/nvim/edit.c | 14 +- src/nvim/eval.c | 1432 ++++++++++++++++++++-------------------- src/nvim/eval.h | 2 +- src/nvim/eval/executor.c | 6 +- src/nvim/eval/typval.c | 28 +- src/nvim/eval/typval.h | 2 + src/nvim/ex_docmd.c | 6 +- src/nvim/ex_eval.c | 20 +- src/nvim/ex_getln.c | 69 +- src/nvim/fileio.c | 36 +- src/nvim/getchar.c | 113 ++-- src/nvim/hardcopy.c | 8 +- src/nvim/message.c | 2 +- src/nvim/msgpack_rpc/channel.c | 20 +- src/nvim/msgpack_rpc/helpers.c | 4 +- src/nvim/normal.c | 21 +- src/nvim/os/fs.c | 25 +- src/nvim/os_unix.c | 4 +- src/nvim/path.c | 36 +- src/nvim/sha256.c | 14 +- src/nvim/shada.c | 2 - src/nvim/spell.c | 48 +- src/nvim/strings.c | 38 +- src/nvim/syntax.c | 92 +-- src/nvim/undo.c | 2 +- src/nvim/version.c | 12 +- 28 files changed, 1072 insertions(+), 992 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6b17ba1a11..2c8a56cc1a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -625,7 +625,7 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE if (str == NULL) { return (String) STRING_INIT; } - return (String) {.data = str, .size = strlen(str)}; + return (String) { .data = str, .size = strlen(str) }; } /// Converts from type Object to a VimL value. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0ce9ce073e..0fb9a21354 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4207,11 +4207,11 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) #ifdef WIN32 if (!buf->b_p_bin) { // If the file name is a shortcut file, use the file it links to. - char_u *rfname = (char_u *)os_resolve_shortcut(*ffname); + char *rfname = os_resolve_shortcut((const char *)(*ffname)); if (rfname != NULL) { xfree(*ffname); - *ffname = rfname; - *sfname = rfname; + *ffname = (char_u *)rfname; + *sfname = (char_u *)rfname; } } #endif diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d1cceb435a..b396251051 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3464,7 +3464,6 @@ expand_by_function ( { list_T *matchlist = NULL; dict_T *matchdict = NULL; - char_u *args[2]; char_u *funcname; pos_T pos; win_T *curwin_save; @@ -3475,9 +3474,8 @@ expand_by_function ( if (*funcname == NUL) return; - /* Call 'completefunc' to obtain the list of matches. */ - args[0] = (char_u *)"0"; - args[1] = base; + // Call 'completefunc' to obtain the list of matches. + const char_u *const args[2] = { (char_u *)"0", base }; pos = curwin->w_cursor; curwin_save = curwin; @@ -4589,7 +4587,6 @@ static int ins_complete(int c, bool enable_pum) * Call user defined function 'completefunc' with "a:findstart" * set to 1 to obtain the length of text to use for completion. */ - char_u *args[2]; int col; char_u *funcname; pos_T pos; @@ -4608,8 +4605,7 @@ static int ins_complete(int c, bool enable_pum) return FAIL; } - args[0] = (char_u *)"1"; - args[1] = NULL; + const char_u *const args[2] = { (char_u *)"1", NULL }; pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; @@ -7111,8 +7107,8 @@ static void ins_ctrl_g(void) */ static void ins_ctrl_hat(void) { - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, Toggle use of ":lmap" mappings. */ + if (map_to_exists_mode("", LANGMAP, false)) { + // ":lmap" mappings exists, Toggle use of ":lmap" mappings. if (State & LANGMAP) { curbuf->b_p_iminsert = B_IMODE_NONE; State &= ~LANGMAP; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d060eff33..7e40f5e828 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -965,31 +965,34 @@ eval_to_bool ( return retval; } -/* - * Top level evaluation function, returning a string. If "skip" is TRUE, - * only parsing to "nextcmd" is done, without reporting errors. Return - * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. - */ -char_u * -eval_to_string_skip ( - char_u *arg, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, + const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { typval_T tv; - char_u *retval; + char *retval; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) + if (skip) { + emsg_skip++; + } + if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) { retval = NULL; - else { - retval = vim_strsave(get_tv_string(&tv)); + } else { + retval = xstrdup(tv_get_string(&tv)); tv_clear(&tv); } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -1015,13 +1018,12 @@ int skip_expr(char_u **pp) char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) { typval_T tv; - char_u *retval; + char *retval; garray_T ga; - char_u numbuf[NUMBUFLEN]; - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + if (eval0(arg, &tv, nextcmd, true) == FAIL) { retval = NULL; - else { + } else { if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { @@ -1031,16 +1033,18 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) } } ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; + retval = (char *)ga.ga_data; } else if (convert && tv.v_type == VAR_FLOAT) { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } else - retval = vim_strsave(get_tv_string(&tv)); + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } tv_clear(&tv); } - return retval; + return (char_u *)retval; } /* @@ -1159,14 +1163,15 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) * Return -1 if anything isn't right. * Used to get the good word and score from the eval_spell_expr() result. */ -int get_spellword(list_T *list, char_u **pp) +int get_spellword(list_T *list, const char **pp) { listitem_T *li; li = list->lv_first; - if (li == NULL) + if (li == NULL) { return -1; - *pp = get_tv_string(&li->li_tv); + } + *pp = tv_get_string(&li->li_tv); li = li->li_next; if (li == NULL) @@ -1198,9 +1203,9 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd) // // Return OK or FAIL. int call_vim_function( - char_u *func, + const char_u *func, int argc, - char_u **argv, + const char_u *const *const argv, int safe, // use the sandbox int str_arg_only, // all arguments are strings typval_T *rettv @@ -1233,7 +1238,7 @@ int call_vim_function( argvars[i].vval.v_number = n; } else { argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = argv[i]; + argvars[i].vval.v_string = (char_u *)argv[i]; } } @@ -1268,8 +1273,8 @@ long call_func_retnr ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -1284,27 +1289,28 @@ call_func_retnr ( return retval; } -/* - * Call vimL function "func" and return the result as a string. - * Returns NULL when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -void * -call_func_retstr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) +/// Call VimL function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with string arguments. +/// @param[in] safe Use the sandbox. +/// +/// @return [allocated] NULL when calling function failes, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, const int argc, + const char_u *const *const argv, + const bool safe) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; - char_u *retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((const char_u *)func, argc, argv, safe, true, &rettv) + == FAIL) { return NULL; + } - retval = vim_strsave(get_tv_string(&rettv)); + char *const retval = xstrdup(tv_get_string(&rettv)); tv_clear(&rettv); return retval; } @@ -1318,8 +1324,8 @@ void * call_func_retlist ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -2202,7 +2208,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (lp->ll_tv->v_type == VAR_DICT) { if (len == -1) { // "[key]": get key from "var1" - key = get_tv_string_chk(&var1); // is number or string + key = (char_u *)tv_get_string(&var1); // is number or string if (key == NULL) { tv_clear(&var1); return NULL; @@ -4343,9 +4349,8 @@ eval_index ( bool empty1 = false; bool empty2 = false; long n1, n2 = 0; - long len = -1; + ptrdiff_t len = -1; int range = false; - char_u *s; char_u *key = NULL; switch (rettv->v_type) { @@ -4455,103 +4460,109 @@ eval_index ( tv_clear(&var1); } if (range) { - if (empty2) + if (empty2) { n2 = -1; - else { + } else { n2 = get_tv_number(&var2); tv_clear(&var2); } } switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: - s = get_tv_string(rettv); - len = (long)STRLEN(s); - if (range) { - /* The resulting variable is a substring. If the indexes - * are out of range the result is empty. */ + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)v; + break; + } + case VAR_LIST: { + len = tv_list_len(rettv->vval.v_list); if (n1 < 0) { n1 = len + n1; - if (n1 < 0) - n1 = 0; } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len; - if (n1 >= len || n2 < 0 || n1 > n2) - s = NULL; - else - s = vim_strnsave(s + n1, (int)(n2 - n1 + 1)); - } else { - /* The resulting variable is a string of a single - * character. If the index is too big or negative the - * result is empty. */ - if (n1 >= len || n1 < 0) - s = NULL; - else - s = vim_strnsave(s + n1, 1); - } - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = s; - break; - - case VAR_LIST: - len = tv_list_len(rettv->vval.v_list); - if (n1 < 0) { - n1 = len + n1; - } - if (!empty1 && (n1 < 0 || n1 >= len)) { - /* For a range we allow invalid values and return an empty - * list. A list index out of range is an error. */ - if (!range) { - if (verbose) - EMSGN(_(e_listidx), n1); - return FAIL; + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + EMSGN(_(e_listidx), n1); + } + return FAIL; + } + n1 = len; } - n1 = len; - } - if (range) { - list_T *l; - listitem_T *item; - - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) - n2 = -1; - l = tv_list_alloc(); - item = tv_list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + if (range) { + list_T *l; + listitem_T *item; + + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); + while (n1++ <= n2) { + tv_list_append_tv(l, &item->li_tv); + item = item->li_next; + } + tv_clear(rettv); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + l->lv_refcount++; + } else { + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); + *rettv = var1; } - tv_clear(rettv); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); - tv_clear(rettv); - *rettv = var1; + break; } - break; - - case VAR_DICT: - if (range) { - if (verbose) { - emsgf(_(e_dictrange)); - } - if (len == -1) { - tv_clear(&var1); + case VAR_DICT: { + if (range) { + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } + return FAIL; } - return FAIL; - } - { - dictitem_T *item; if (len == -1) { key = get_tv_string_chk(&var1); @@ -4561,7 +4572,8 @@ eval_index ( } } - item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4576,14 +4588,15 @@ eval_index ( copy_tv(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; + break; + } + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: { + break; // Not evaluating, skipping over subscript } - break; - case VAR_SPECIAL: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; // Not evaluating, skipping over subscript } } @@ -6251,7 +6264,7 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) /// Also returns OK when an error was encountered while executing the function. int call_func( - char_u *funcname, // name of the function + const char_u *funcname, // name of the function int len, // length of "name" typval_T *rettv, // return value goes here int argcount_in, // number of "argvars" @@ -7249,24 +7262,24 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /// "call(func, arglist [, dict])" function static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *func; - partial_T *partial = NULL; - dict_T *selfdict = NULL; - if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[1].vval.v_list == NULL) + if (argvars[1].vval.v_list == NULL) { return; + } + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; if (argvars[0].v_type == VAR_FUNC) { func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; func = partial_name(partial); } else { - func = get_tv_string(&argvars[0]); + func = (char_u *)tv_get_string(&argvars[0]); } if (*func == NUL) { return; // type error or empty name @@ -7299,15 +7312,15 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { utf8 = get_tv_number_chk(&argvars[1], NULL); + } - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } else - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; + rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( + (const char_u *)tv_get_string(&argvars[0])); + } else { + rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]); + } } /* @@ -7552,16 +7565,17 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; + char_u *dbpath = NULL; + char_u *prepend = NULL; char_u buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) + dbpath = (char_u *)tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) { prepend = get_tv_string_buf(&argvars[2], buf); + } } rettv->vval.v_number = cs_connection(num, dbpath, prepend); @@ -7648,7 +7662,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u nbuf[NUMBUFLEN]; - char_u *name; char_u *flags; rettv->vval.v_number = -1; @@ -7656,7 +7669,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - name = get_tv_string(&argvars[0]); + const char *const name = tv_get_string(&argvars[0]); if (name == NULL || *name == NUL) { EMSG(_(e_invarg)); return; @@ -7670,10 +7683,10 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*flags == NUL) { // delete a file - rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "d") == 0) { // delete an empty directory - rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); @@ -7894,7 +7907,8 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), + rettv->vval.v_string = vim_strsave_escaped( + (const char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -7936,11 +7950,13 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name = get_tv_string(&argvars[0]); + const char *name = tv_get_string(&argvars[0]); // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true) - || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); + rettv->vval.v_number = ( + os_can_exe((const char_u *)name, NULL, true) + || (gettail_dir(name) != name + && os_can_exe((const char_u *)name, NULL, false))); } static char_u * get_list_line(int c, void *cookie, int indent) @@ -7993,11 +8009,11 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) capture_ga = &capture_local; if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd((char *)get_tv_string(&argvars[0])); + do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { - list_T *list = argvars[0].vval.v_list; + list_T *const list = argvars[0].vval.v_list; list->lv_refcount++; - listitem_T *item = list->lv_first; + listitem_T *const item = list->lv_first; do_cmdline(NULL, get_list_line, (void *)&item, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); list->lv_refcount--; @@ -8017,10 +8033,10 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *arg = get_tv_string(&argvars[0]); + const char *arg = tv_get_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe((const char_u *)arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -8034,21 +8050,21 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = false; int len = 0; - char *p = (char *)get_tv_string(&argvars[0]); + const char *p = tv_get_string(&argvars[0]); if (*p == '$') { // Environment variable. // First try "normal" environment variables (fast). if (os_getenv(p + 1) != NULL) { n = true; } else { // Try expanding things like $VIM and ${HOME}. - p = (char *)expand_env_save((char_u *)p); - if (p != NULL && *p != '$') { + char_u *const exp = expand_env_save((char_u *)p); + if (exp != NULL && *exp != '$') { n = true; } - xfree(p); + xfree(exp); } } else if (*p == '&' || *p == '+') { // Option. - n = (get_option_tv((const char **)&p, NULL, true) == OK); + n = (get_option_tv(&p, NULL, true) == OK); if (*skipwhite((const char_u *)p) != NUL) { n = false; // Trailing garbage. } @@ -8076,7 +8092,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { // Handle d.key, l[idx], f(expr). - n = (handle_subscript((const char **)&p, &tv, true, false) == OK); + n = (handle_subscript(&p, &tv, true, false) == OK); if (n) { tv_clear(&tv); } @@ -8096,7 +8112,6 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; size_t len; char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; @@ -8113,11 +8128,11 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = NULL; } - s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; + emsg_off++; + result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); + emsg_off--; if (rettv->v_type == VAR_LIST) { tv_list_alloc_ret(rettv); if (result != NULL) { @@ -8134,21 +8149,24 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; + } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, + WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } - } else + } else { rettv->vval.v_string = NULL; + } } } @@ -8235,7 +8253,6 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *keys, *flags = NULL; char_u nbuf[NUMBUFLEN]; /* This is not allowed in the sandbox. If the commands would still be @@ -8244,10 +8261,11 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_secure()) return; - keys = get_tv_string(&argvars[0]); + const char *const keys = tv_get_string(&argvars[0]); + const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -8257,9 +8275,9 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "filereadable()" function static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_number = - (*p && !os_isdir(p) && os_file_is_readable((char*)p)); + (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); } /* @@ -8268,14 +8286,13 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *filename = (char *)get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); rettv->vval.v_number = os_file_is_writable(filename); } static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fname; char_u *fresult = NULL; char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char_u *p; @@ -8287,7 +8304,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { p = get_tv_string_buf_chk(&argvars[1], pathbuf); @@ -8311,8 +8328,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) do { if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) xfree(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? STRLEN(fname) : 0, + fresult = find_file_in_path_option(first ? (char_u *)fname : NULL, + first ? strlen(fname) : 0, 0, first, path, find_what, curbuf->b_ffname, (find_what == FINDFILE_DIR @@ -8572,8 +8589,8 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); + rettv->vval.v_string = (char_u *)vim_strsave_fnameescape( + tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } @@ -8765,7 +8782,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, s = partial_name(arg_pt); } else { // function('MyFunc', [arg], dict) - s = get_tv_string(&argvars[0]); + s = (char_u *)tv_get_string(&argvars[0]); use_string = true; } @@ -8780,7 +8797,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - EMSG2(_(e_invarg2), use_string ? get_tv_string(&argvars[0]) : s); + emsgf(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL @@ -8958,7 +8977,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + di = tv_dict_find(d, tv_get_string(&argvars[1]), -1); if (di != NULL) { tv = &di->di_tv; } @@ -8977,27 +8996,26 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (pt != NULL) { - char_u *what = get_tv_string(&argvars[1]); - char_u *n; + const char *const what = tv_get_string(&argvars[1]); - if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { + if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - n = partial_name(pt); + const char *const n = (const char *)partial_name(pt); if (n == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = vim_strsave(n); + rettv->vval.v_string = (char_u *)xstrdup(n); if (rettv->v_type == VAR_FUNC) { func_ref(rettv->vval.v_string); } } - } else if (STRCMP(what, "dict") == 0) { + } else if (strcmp(what, "dict") == 0) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = pt->pt_dict; if (pt->pt_dict != NULL) { (pt->pt_dict->dv_refcount)++; } - } else if (STRCMP(what, "args") == 0) { + } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; if (tv_list_alloc_ret(rettv) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { @@ -9448,9 +9466,10 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ExpandInit(&xpc); - xpc.xp_pattern = get_tv_string(&argvars[0]); + xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); + xpc.xp_context = cmdcomplete_str_to_type( + (char_u *)tv_get_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; @@ -9623,20 +9642,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *perm = NULL; + char *perm = NULL; char_u flags[] = "rwx"; - char_u *filename = get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); int32_t file_perm = os_getperm(filename); if (file_perm >= 0) { - perm = vim_strsave((char_u *)"---------"); + perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) + if (file_perm & (1 << (8 - i))) { perm[i] = flags[i % 3]; + } } } rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; + rettv->vval.v_string = (char_u *)perm; } /* @@ -9644,16 +9664,16 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_NUMBER; FileInfo file_info; if (os_fileinfo(fname, &file_info)) { uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir((char_u *)fname)) + if (os_isdir((const char_u *)fname)) { rettv->vval.v_number = 0; - else { + } else { rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ @@ -9671,7 +9691,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); FileInfo file_info; if (os_fileinfo(fname, &file_info)) { @@ -9686,15 +9706,14 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; char_u *type = NULL; char *t; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_fileinfo_link((char *)fname, &file_info)) { + if (os_fileinfo_link(fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -9746,10 +9765,11 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) default: t = "other"; } # else - if (os_isdir(fname)) + if (os_isdir((const char_u *)fname)) { t = "dir"; - else + } else { t = "file"; + } # endif #endif type = vim_strsave((char_u *)t); @@ -10345,11 +10365,12 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (p_wic) options += WILD_ICASE; if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, - options, WILD_ALL); + rettv->vval.v_string = ExpandOne( + &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10393,7 +10414,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath(get_tv_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10425,7 +10446,7 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "has()" function static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - static char *(has_list[]) = { + static const char *const has_list[] = { #ifdef UNIX "unix", #endif @@ -10536,13 +10557,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "winaltkeys", "writebackup", "nvim", - NULL }; bool n = false; - char *name = (char *)get_tv_string(&argvars[0]); - - for (int i = 0; has_list[i] != NULL; i++) { + const char *const name = tv_get_string(&argvars[0]); + for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { if (STRICMP(name, has_list[i]) == 0) { n = true; break; @@ -10608,7 +10627,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - (const char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), -1) != NULL; } @@ -10718,24 +10737,24 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; - char_u *mode; + const char *mode; + const char *const name = tv_get_string(&argvars[0]); + bool abbr = false; char_u buf[NUMBUFLEN]; - int abbr = FALSE; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[1].v_type == VAR_UNKNOWN) { + mode = "nvo"; + } else { + mode = (const char *)get_tv_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); + } } - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; + if (map_to_exists(name, mode, abbr)) { + rettv->vval.v_number = true; + } else { + rettv->vval.v_number = false; + } } /* @@ -10840,7 +10859,8 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); + rettv->vval.v_number = syn_name2id( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10848,7 +10868,8 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0])); + rettv->vval.v_number = highlight_exists( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10868,25 +10889,27 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; vimconv_T vimconv; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); + const char *const str = tv_get_string(&argvars[0]); + char_u buf1[NUMBUFLEN]; + char_u *const from = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[1], buf1))); + char_u buf2[NUMBUFLEN]; + char_u *const to = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); + // If the encodings are equal, no conversion needed. + if (vimconv.vc_type == CONV_NONE) { + rettv->vval.v_string = (char_u *)xstrdup(str); + } else { + rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL); + } convert_setup(&vimconv, NULL, NULL); xfree(from); @@ -11075,7 +11098,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_clr_eos(); for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts((const char *)get_tv_string(&li->li_tv)); + msg_puts(tv_get_string(&li->li_tv)); msg_putchar('\n'); } @@ -11179,7 +11202,7 @@ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0])); + rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); } /* @@ -11191,9 +11214,11 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; rettv->vval.v_number = -1; - const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]), + NULL, &lv, false, false, - GLV_NO_AUTOLOAD, FNE_CHECK_START); + GLV_NO_AUTOLOAD|GLV_READ_ONLY, + FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) { EMSG(_(e_trailing)); @@ -11416,8 +11441,8 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (!input) { // Either the error has been handled by save_tv_as_string(), or there is no // input to send. @@ -11462,10 +11487,10 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } -static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) +static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) { if (cmd_tv->v_type == VAR_STRING) { - char *cmd_str = (char *)get_tv_string(cmd_tv); + const char *cmd_str = tv_get_string(cmd_tv); if (cmd) { *cmd = cmd_str; } @@ -11504,7 +11529,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { char *a = (char *)get_tv_string_chk(&arg->li_tv); if (!a) { - // Did emsg in get_tv_string; just deallocate argv. + // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } @@ -11841,24 +11866,28 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: - rettv->vval.v_number = (varnumber_T)STRLEN( - get_tv_string(&argvars[0])); - break; - case VAR_LIST: - rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); - break; - case VAR_DICT: - rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); - break; - case VAR_UNKNOWN: - case VAR_SPECIAL: - case VAR_FLOAT: - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E701: Invalid type for len()")); - break; + case VAR_STRING: + case VAR_NUMBER: { + rettv->vval.v_number = (varnumber_T)strlen( + tv_get_string(&argvars[0])); + break; + } + case VAR_LIST: { + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); + break; + } + case VAR_DICT: { + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); + break; + } + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E701: Invalid type for len()")); + break; + } } } @@ -11983,7 +12012,6 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *keys; char_u *which; char_u buf[NUMBUFLEN]; char_u *keys_buf = NULL; @@ -11994,13 +12022,14 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mapblock_T *mp; int buffer_local; - /* return empty string for failure */ + // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) + char_u *keys = (char_u *)tv_get_string(&argvars[0]); + if (*keys == NUL) { return; + } if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); @@ -12118,7 +12147,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; li = l->lv_first; } else { - expr = str = get_tv_string(&argvars[0]); + expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); } @@ -12278,7 +12307,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) int prio = 10; /* default priority */ int id = -1; bool error = false; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; rettv->vval.v_number = -1; @@ -12296,7 +12325,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12311,7 +12340,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = match_add(curwin, (const char *)grp, (const char *)pat, prio, id, - NULL, (const char *)conceal_char); + NULL, conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -12338,7 +12367,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; int prio = 10; int id = -1; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { prio = get_tv_number_chk(&argvars[2], &error); @@ -12352,7 +12381,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12368,7 +12397,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, (const char *)conceal_char); + l, conceal_char); } /* @@ -12523,9 +12552,10 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) *path_tail_with_sep(dir) = NUL; if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) { + } + if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); if (ret != 0) { @@ -12894,8 +12924,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int binary = FALSE; - char_u *fname; + bool binary = false; FILE *fd; char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ int io_size = sizeof(buf); @@ -12909,19 +12938,21 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) + if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { + binary = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { maxline = get_tv_number(&argvars[2]); + } } tv_list_alloc_ret(rettv); - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); + // Always open the file in binary mode, library functions have a mind of + // their own about CR-LF conversion. + const char *const fname = tv_get_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen(fname, READBIN)) == NULL) { + EMSG2(_(e_notopen), *fname == NUL ? _("") : fname); return; } @@ -13241,11 +13272,13 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), + } else { + rettv->vval.v_number = vim_rename( + (char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); + } } /* @@ -13253,32 +13286,37 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - - n = get_tv_number(&argvars[1]); + varnumber_T n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); - if (argvars[0].vval.v_list != NULL) { - while (n-- > 0) { - tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); - } + while (n-- > 0) { + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } else { - p = get_tv_string(&argvars[0]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + if (n <= 0) { + return; + } + + const char *const p = tv_get_string(&argvars[0]); - int slen = (int)STRLEN(p); - int len = slen * n; - if (len <= 0) + const size_t slen = strlen(p); + if (slen == 0) { return; + } + const size_t len = slen * n; + // Detect overflow. + if (len / n != slen) { + return; + } - char_u *r = xmallocz(len); - for (int i = 0; i < n; i++) - memmove(r + i * slen, p, (size_t)slen); + char *const r = xmallocz(len); + for (varnumber_T i = 0; i < n; i++) { + memmove(r + i * slen, p, slen); + } - rettv->vval.v_string = r; + rettv->vval.v_string = (char_u *)r; } } @@ -13287,59 +13325,49 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); + rettv->v_type = VAR_STRING; + const char *fname = tv_get_string(&argvars[0]); #ifdef WIN32 - { - char *v = os_resolve_shortcut(p); - if (v != NULL) { - rettv->vval.v_string = (char_u *)v; - } else { - rettv->vval.v_string = vim_strsave(p); - } - } + char *const v = os_resolve_shortcut(fname); + rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; + bool is_relative_to_current = false; + bool has_trailing_pathsep = false; int limit = 100; - p = vim_strsave(p); + char *p = xstrdup(fname); if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; + || (p[1] == '.' && (vim_ispathsep(p[2]))))) { + is_relative_to_current = true; + } - len = STRLEN(p); - if (len > 0 && after_pathsep((char *)p, (char *)p + len)) { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ + ptrdiff_t len = (ptrdiff_t)strlen(p); + if (len > 0 && after_pathsep(p, p + len)) { + has_trailing_pathsep = true; + p[len - 1] = NUL; // The trailing slash breaks readlink(). } - q = path_next_component(p); + char *q = (char *)path_next_component(p); + char *remain = NULL; if (*q != NUL) { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); + // Separate the first path component in "p", and keep the + // remainder (beginning with the path separator). + remain = xstrdup(q - 1); q[-1] = NUL; } - buf = xmallocz(MAXPATHL); + char *const buf = xmallocz(MAXPATHL); + char *cpy; for (;; ) { for (;; ) { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) + len = readlink(p, buf, MAXPATHL); + if (len <= 0) { break; + } buf[len] = NUL; if (limit-- == 0) { @@ -13347,66 +13375,74 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(remain); EMSG(_("E655: Too many symbolic links (cycle?)")); rettv->vval.v_string = NULL; - goto fail; + xfree(buf); + return; } - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep((char *)buf); + // Ensure that the result will have a trailing path separator + // if the argument has one. */ + if (remain == NULL && has_trailing_pathsep) { + add_pathsep(buf); + } - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); + // Separate the first path component in the link value and + // concatenate the remainders. */ + q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); if (*q != NUL) { cpy = remain; - remain = remain ? - concat_str(q - 1, remain) : (char_u *) xstrdup((char *)q - 1); + remain = (remain + ? (char *)concat_str((char_u *)q - 1, (char_u *)remain) + : xstrdup(q - 1)); xfree(cpy); q[-1] = NUL; } - q = path_tail(p); + q = (char *)path_tail((char_u *)p); if (q > p && *q == NUL) { - /* Ignore trailing path separator. */ + // Ignore trailing path separator. q[-1] = NUL; - q = path_tail(p); + q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path(buf)) { - /* symlink is relative to directory of argument */ - cpy = xmalloc(STRLEN(p) + STRLEN(buf) + 1); - STRCPY(cpy, p); - STRCPY(path_tail(cpy), buf); + if (q > p && !path_is_absolute_path((const char_u *)buf)) { + // Symlink is relative to directory of argument. + const size_t p_len = strlen(p); + const size_t buf_len = strlen(buf); + cpy = xmalloc(p_len + buf_len + 1); + memcpy(cpy, p, p_len); + memcpy(path_tail((char_u *)cpy), buf, buf_len + 1); xfree(p); p = cpy; } else { xfree(p); - p = vim_strsave(buf); + p = xstrdup(buf); } } - if (remain == NULL) + if (remain == NULL) { break; + } - /* Append the first path component of "remain" to "p". */ - q = path_next_component(remain + 1); + // Append the first path component of "remain" to "p". + q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - STRNCAT(cpy, remain, len); + const size_t p_len = strlen(p); + cpy = xmallocz(p_len + len); + memcpy(cpy, p, p_len + 1); + strncat(cpy + p_len, remain, len); xfree(p); p = cpy; - /* Shorten "remain". */ - if (*q != NUL) + // Shorten "remain". + if (*q != NUL) { STRMOVE(remain, q - 1); - else { + } else { xfree(remain); remain = NULL; } } - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ + // If the result is a relative path name, make it explicitly relative to + // the current directory if and only if the argument had this form. if (!vim_ispathsep(*p)) { if (is_relative_to_current && *p != NUL @@ -13416,42 +13452,40 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (p[1] == '.' && (p[2] == NUL || vim_ispathsep(p[2])))))) { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); + // Prepend "./". + cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p); xfree(p); p = cpy; } else if (!is_relative_to_current) { - /* Strip leading "./". */ + // Strip leading "./". q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) + while (q[0] == '.' && vim_ispathsep(q[1])) { q += 2; - if (q > p) + } + if (q > p) { STRMOVE(p, p + 2); + } } } - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ + // Ensure that the result will have no trailing path separator + // if the argument had none. But keep "/" or "//". if (!has_trailing_pathsep) { - q = p + STRLEN(p); - if (after_pathsep((char *)p, (char *)q)) - *path_tail_with_sep(p) = NUL; + q = p + strlen(p); + if (after_pathsep(p, q)) { + *path_tail_with_sep((char_u *)p) = NUL; + } } - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)p; + xfree(buf); } # else - rettv->vval.v_string = vim_strsave(p); + rettv->vval.v_string = (char_u *)xstrdup(p); # endif #endif simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - xfree(buf); -#endif - rettv->v_type = VAR_STRING; } /* @@ -13542,7 +13576,6 @@ static int get_search_arg(typval_T *varp, int *flagsp) static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; - char_u *pat; pos_T pos; pos_T save_cursor; bool save_p_ws = p_ws; @@ -13554,10 +13587,11 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; - pat = get_tv_string(&argvars[0]); - dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */ - if (dir == 0) + const char *const pat = tv_get_string(&argvars[0]); + dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. + if (dir == 0) { goto theend; + } flags = *flagsp; if (flags & SP_START) { options |= SEARCH_START; @@ -13592,13 +13626,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) */ if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); goto theend; } pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm); if (subpatnum != FAIL) { if (flags & SP_SUBPAT) retval = subpatnum; @@ -13655,7 +13689,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; @@ -13722,7 +13756,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args, &err); @@ -13797,7 +13831,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Copy arguments to the vector if (argsl > 0) { for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *) get_tv_string(&arg->li_tv)); + argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); } } @@ -13962,7 +13996,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) */ if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[3])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } @@ -14232,7 +14266,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } else { - rettv->vval.v_string = vim_strsave(get_tv_string(argvars)); + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string(argvars)); } } else { rettv->vval.v_string = (char_u *)server_address_new(); @@ -14613,7 +14647,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Note: there are three number buffers involved: // - group_buf below. // - numbuf in tv_dict_get_string(). - // - mybuf in get_tv_string(). + // - mybuf in tv_get_string(). // // If you change this code make sure that buffers will not get // accidentally reused. @@ -14623,8 +14657,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int id = (int)tv_dict_get_number(d, "id"); dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); const char *const conceal = (conceal_di != NULL - ? (const char *)get_tv_string( - &conceal_di->di_tv) + ? tv_get_string(&conceal_di->di_tv) : NULL); if (i == 0) { if (match_add(curwin, group, @@ -14899,11 +14932,11 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); - const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0); + const char *p = tv_get_string(&argvars[0]); + const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0); // make a copy of the hash (sha256_bytes returns a static buffer) - rettv->vval.v_string = (char_u *) xstrdup((char *) hash); + rettv->vval.v_string = (char_u *)xstrdup(hash); rettv->v_type = VAR_STRING; } @@ -14913,7 +14946,8 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true); + (const char_u *)tv_get_string(&argvars[0]), non_zero_arg(&argvars[1]), + true); rettv->v_type = VAR_STRING; } @@ -14930,11 +14964,9 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ + const char *const p = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)xstrdup(p); + simplify_filename(rettv->vval.v_string); // Simplify in place. rettv->v_type = VAR_STRING; } @@ -14950,7 +14982,7 @@ typedef struct { bool item_compare_numeric; bool item_compare_numbers; bool item_compare_float; - char_u *item_compare_func; + const char *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; bool item_compare_func_err; @@ -15058,7 +15090,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) typval_T rettv; typval_T argv[3]; int dummy; - char_u *func_name; + const char *func_name; partial_T *partial = sortinfo->item_compare_partial; // shortcut after failure in previous call; compare all items equal @@ -15072,7 +15104,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (partial == NULL) { func_name = sortinfo->item_compare_func; } else { - func_name = partial_name(partial); + func_name = (const char *)partial_name(partial); } // Copy the values. This is needed to be able to set v_lock to VAR_FIXED @@ -15081,7 +15113,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func(func_name, + res = call_func((const char_u *)func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); @@ -15167,7 +15199,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = argvars[1].vval.v_string; + info.item_compare_func = (const char *)argvars[1].vval.v_string; } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { @@ -15180,7 +15212,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (i == 1) { info.item_compare_ic = true; } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = get_tv_string(&argvars[1]); + info.item_compare_func = tv_get_string(&argvars[1]); } else if (i != 0) { EMSG(_(e_invarg)); goto theend; @@ -15189,16 +15221,16 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (*info.item_compare_func == NUL) { // empty string means default sort info.item_compare_func = NULL; - } else if (STRCMP(info.item_compare_func, "n") == 0) { + } else if (strcmp(info.item_compare_func, "n") == 0) { info.item_compare_func = NULL; info.item_compare_numeric = true; - } else if (STRCMP(info.item_compare_func, "N") == 0) { + } else if (strcmp(info.item_compare_func, "N") == 0) { info.item_compare_func = NULL; info.item_compare_numbers = true; - } else if (STRCMP(info.item_compare_func, "f") == 0) { + } else if (strcmp(info.item_compare_func, "f") == 0) { info.item_compare_func = NULL; info.item_compare_float = true; - } else if (STRCMP(info.item_compare_func, "i") == 0) { + } else if (strcmp(info.item_compare_func, "i") == 0) { info.item_compare_func = NULL; info.item_compare_ic = true; } @@ -15332,11 +15364,9 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr) */ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - rettv->v_type = VAR_STRING; - s = get_tv_string(&argvars[0]); - rettv->vval.v_string = eval_soundfold(s); + const char *const s = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)eval_soundfold(s); } /* @@ -15387,7 +15417,6 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; bool typeerr = false; int maxcount; garray_T ga; @@ -15397,7 +15426,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - str = get_tv_string(&argvars[0]); + const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = get_tv_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) @@ -15410,15 +15439,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else maxcount = 25; - spell_suggest_list(&ga, str, maxcount, need_capital, false); + spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); - for (int i = 0; i < ga.ga_len; ++i) { - str = ((char_u **)ga.ga_data)[i]; + for (int i = 0; i < ga.ga_len; i++) { + char *p = ((char **)ga.ga_data)[i]; li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; + li->li_tv.vval.v_string = (char_u *)p; tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); @@ -15427,8 +15456,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - char_u *end; char_u *pat = NULL; regmatch_T regmatch; char_u patbuf[NUMBUFLEN]; @@ -15442,7 +15469,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_cpo = p_cpo; p_cpo = (char_u *)""; - str = get_tv_string(&argvars[0]); + const char *str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { @@ -15464,29 +15491,33 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); + if (*str == NUL) { + match = false; // Empty item at the end. + } else { + match = vim_regexec_nl(®match, (char_u *)str, col); + } + const char *end; + if (match) { + end = (const char *)regmatch.startp[0]; + } else { + end = str + strlen(str); + } if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < - regmatch.endp[0])) { - tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); + && *str != NUL + && match + && end < (const char *)regmatch.endp[0])) { + tv_list_append_string(rettv->vval.v_list, str, end - str); } if (!match) break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) + // Advance to just after the match. + if (regmatch.endp[0] > (char_u *)str) { col = 0; - else { - /* Don't get stuck at the same match. */ + } else { + // Don't get stuck at the same match. col = (*mb_ptr2len)(regmatch.endp[0]); } - str = regmatch.endp[0]; + str = (const char *)regmatch.endp[0]; } vim_regfree(regmatch.regprog); @@ -15500,11 +15531,12 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); - if (*p == '+') + if (*p == '+') { p = skipwhite(p + 1); - (void) string2float((char *) p, &rettv->vval.v_float); + } + (void)string2float((char *)p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } @@ -15512,7 +15544,6 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; - char_u *p; long n; int what; @@ -15524,22 +15555,26 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); if (*p == '+') { p = skipwhite(p + 1); } switch (base) { - case 2: - what = STR2NR_BIN + STR2NR_FORCE; + case 2: { + what = STR2NR_BIN | STR2NR_FORCE; break; - case 8: - what = STR2NR_OCT + STR2NR_FORCE; + } + case 8: { + what = STR2NR_OCT | STR2NR_FORCE; break; - case 16: - what = STR2NR_HEX + STR2NR_FORCE; + } + case 16: { + what = STR2NR_HEX | STR2NR_FORCE; break; - default: + } + default: { what = 0; + } } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; @@ -15550,17 +15585,16 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u result_buf[256]; time_t seconds; - char_u *p; rettv->v_type = VAR_STRING; - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) + char *p = (char *)tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); - else + } else { seconds = (time_t)get_tv_number(&argvars[1]); + } struct tm curtime; struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); @@ -15574,23 +15608,27 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) conv.vc_type = CONV_NONE; enc = enc_locale(); convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime_ptr); - else + if (conv.vc_type != CONV_NONE) { + p = (char *)string_convert(&conv, (char_u *)p, NULL); + } + char result_buf[256]; + if (p != NULL) { + (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr); + } else { result_buf[0] = NUL; + } - if (conv.vc_type != CONV_NONE) + if (conv.vc_type != CONV_NONE) { xfree(p); + } convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else - rettv->vval.v_string = vim_strsave(result_buf); + if (conv.vc_type != CONV_NONE) { + rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL); + } else { + rettv->vval.v_string = (char_u *)xstrdup(result_buf); + } - /* Release conversion descriptors */ + // Release conversion descriptors. convert_setup(&conv, NULL, NULL); xfree(enc); } @@ -15672,8 +15710,7 @@ static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } /* @@ -15681,7 +15718,7 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; int (*func_mb_ptr2char_adv)(char_u **pp); @@ -15694,8 +15731,8 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv(&s); - ++len; + func_mb_ptr2char_adv((char_u **)&s); + len++; } rettv->vval.v_number = len; } @@ -15706,13 +15743,14 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); int col = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { col = get_tv_number(&argvars[1]); + } - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); } /* @@ -15720,15 +15758,15 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); - rettv->vval.v_number = (varnumber_T) mb_string2cells(s); + rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s); } // "strcharpart()" function static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *const p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); const size_t slen = STRLEN(p); int nbyte = 0; @@ -15737,7 +15775,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { - nbyte += MB_CPTR2LEN(p + nbyte); + nbyte += MB_CPTR2LEN((const char_u *)p + nbyte); nchar--; } } else { @@ -15753,7 +15791,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (off < 0) { len += 1; } else { - len += MB_CPTR2LEN(p + off); + len += (size_t)MB_CPTR2LEN((const char_u *)p + off); } charlen--; } @@ -15776,7 +15814,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + nbyte, len); + rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len); } /* @@ -15784,39 +15822,37 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - int len; - int slen; bool error = false; - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); - n = get_tv_number_chk(&argvars[1], &error); - if (error) + varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T len; + if (error) { len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) + } else if (argvars[2].v_type != VAR_UNKNOWN) { len = get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ + } else { + len = slen - n; // Default len: all bytes that are available. + } - /* - * Only return the overlap between the specified part and the actual - * string. - */ + // Only return the overlap between the specified part and the actual + // string. if (n < 0) { len += n; n = 0; - } else if (n > slen) + } else if (n > (varnumber_T)slen) { n = slen; - if (len < 0) + } + if (len < 0) { len = 0; - else if (n + len > slen) + } else if (n + len > (varnumber_T)slen) { len = slen - n; + } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); + rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } /* @@ -15871,7 +15907,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); + rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0])); } /* @@ -15962,20 +15998,17 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = NULL; - int id; - char_u *what; - char_u *mode; - char_u modebuf[NUMBUFLEN]; + const int id = get_tv_number(&argvars[0]); + const char *const what = tv_get_string(&argvars[1]); int modec; - - id = get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - mode = get_tv_string_buf(&argvars[2], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode = (const char *)get_tv_string_buf(&argvars[2], + (char_u *)modebuf); modec = TOLOWER_ASC(mode[0]); - if (modec != 'c' && modec != 'g') - modec = 0; /* replace invalid with current */ + if (modec != 'c' && modec != 'g') { + modec = 0; // Replace invalid with current. + } } else if (ui_rgb_attached()) { modec = 'g'; } else { @@ -15983,54 +16016,56 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } + const char *p = NULL; switch (TOLOWER_ASC(what[0])) { - case 'b': - if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */ + case 'b': { + if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] + p = highlight_color(id, what, modec); + } else { // bold + p = highlight_has_attr(id, HL_BOLD, modec); + } + break; + } + case 'f': { // fg[#] or font p = highlight_color(id, what, modec); - else /* bold */ - p = highlight_has_attr(id, HL_BOLD, modec); - break; - - case 'f': /* fg[#] or font */ - p = highlight_color(id, what, modec); - break; - - case 'i': - if (TOLOWER_ASC(what[1]) == 'n') /* inverse */ + break; + } + case 'i': { + if (TOLOWER_ASC(what[1]) == 'n') { // inverse + p = highlight_has_attr(id, HL_INVERSE, modec); + } else { // italic + p = highlight_has_attr(id, HL_ITALIC, modec); + } + break; + } + case 'n': { // name + p = get_highlight_name(NULL, id - 1); + break; + } + case 'r': { // reverse p = highlight_has_attr(id, HL_INVERSE, modec); - else /* italic */ - p = highlight_has_attr(id, HL_ITALIC, modec); - break; - - case 'n': // name - p = (char_u *)get_highlight_name(NULL, id - 1); - break; - - case 'r': /* reverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - break; - - case 's': - if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */ - p = highlight_color(id, what, modec); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') - /* underline */ - p = highlight_has_attr(id, HL_UNDERLINE, modec); - else - /* undercurl */ - p = highlight_has_attr(id, HL_UNDERCURL, modec); - break; + break; + } + case 's': { + if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] + p = highlight_color(id, what, modec); + } else { // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); + } + break; + } + case 'u': { + if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + } else { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + } + break; + } } - if (p != NULL) - p = vim_strsave(p); rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); } /* @@ -16147,8 +16182,8 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get input to the shell command (if any), and its length - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (input_len < 0) { assert(input == NULL); return; @@ -16359,15 +16394,14 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *tag_pattern; - - tag_pattern = get_tv_string(&argvars[0]); + const char *const tag_pattern = tv_get_string(&argvars[0]); - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) + rettv->vval.v_number = false; + if (*tag_pattern == NUL) { return; + } - (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); } /* @@ -16391,7 +16425,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - char *cmd; + const char *cmd; bool executable = true; char **argv = tv_to_argv(&argvars[0], &cmd, &executable); if (!argv) { @@ -16614,7 +16648,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT || (dict = argvars[2].vval.v_dict) == NULL) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); return; } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); @@ -16740,7 +16774,7 @@ void timer_teardown(void) */ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = vim_strsave(get_tv_string(&argvars[0])); + char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0])); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -16772,7 +16806,7 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); + rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0])); } /* @@ -16780,56 +16814,48 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; - int inlen; int fromlen; int tolen; int idx; - char_u *cpstr; - int cplen; - int first = TRUE; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; garray_T ga; - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *in_str = tv_get_string(&argvars[0]); + const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); + const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); - /* Default return value: empty string. */ + // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ + if (fromstr == NULL || tostr == NULL) { + return; // type error; errmsg already given + } ga_init(&ga, (int)sizeof(char), 80); - if (!has_mbyte) - /* not multi-byte: fromstr and tostr must be the same length */ + if (!has_mbyte) { + // Not multi-byte: fromstr and tostr must be the same length. if (STRLEN(fromstr) != STRLEN(tostr)) { -error: - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; + goto error; } + } - /* fromstr and tostr have to contain the same number of chars */ + // fromstr and tostr have to contain the same number of chars. + bool first = true; while (*in_str != NUL) { if (has_mbyte) { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; + const char *cpstr = in_str; + const int inlen = (*mb_ptr2len)((const char_u *)in_str); + int cplen = inlen; idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) { + for (const char_u *p = fromstr; *p != NUL; p += fromlen) { fromlen = (*mb_ptr2len)(p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { for (p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); if (idx-- == 0) { cplen = tolen; - cpstr = p; + cpstr = (char *)p; break; } } @@ -16841,16 +16867,17 @@ error: } if (first && cpstr == in_str) { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) { + // Check that fromstr and tostr have the same number of + // (multi-byte) characters. Done only once when a character + // of in_str doesn't appear in fromstr. + first = false; + for (const char_u *p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); - --idx; + idx--; } - if (idx != 0) + if (idx != 0) { goto error; + } } ga_grow(&ga, cplen); @@ -16859,13 +16886,14 @@ error: in_str += inlen; } else { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) + // When not using multi-byte chars we can do it faster. + char_u *p = vim_strchr(fromstr, *in_str); + if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); - else + } else { ga_append(&ga, *in_str); - ++in_str; + } + in_str++; } } @@ -16873,6 +16901,11 @@ error: ga_append(&ga, NUL); rettv->vval.v_string = ga.ga_data; + return; +error: + EMSG2(_(e_invarg2), fromstr); + ga_clear(&ga); + return; } /* @@ -16918,20 +16951,18 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - { - char_u *fname = get_tv_string(&argvars[0]); + const char *const fname = tv_get_string(&argvars[0]); - if (*fname == NUL) { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } else { - char *ffname = FullName_save((char *)fname, false); + if (*fname == NUL) { + // If there is no file name there will be no undo file. + rettv->vval.v_string = NULL; + } else { + char *ffname = FullName_save(fname, false); - if (ffname != NULL) { - rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); - } - xfree(ffname); + if (ffname != NULL) { + rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); } + xfree(ffname); } } @@ -17290,7 +17321,7 @@ void init_static_list(staticList10_T *sl) /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) +static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { if (tv->v_type == VAR_UNKNOWN) { @@ -17301,9 +17332,9 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) // For types other than list, let get_tv_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char_u *ret = get_tv_string_chk(tv); - if (ret && (*len = STRLEN(ret))) { - ret = vim_strsave(ret); + char *ret = (char *)get_tv_string_chk(tv); + if (ret && (*len = strlen(ret))) { + ret = xstrdup(ret); } else { ret = NULL; *len = -1; @@ -17315,17 +17346,17 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) *len = 0; list_T *list = tv->vval.v_list; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += STRLEN(get_tv_string(&li->li_tv)) + 1; + *len += strlen(tv_get_string(&li->li_tv)) + 1; } if (*len == 0) { return NULL; } - char_u *ret = xmalloc(*len + endnl); - char_u *end = ret; + char *ret = xmalloc(*len + endnl); + char *end = ret; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) { + for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || li->li_next != NULL) { @@ -17879,7 +17910,7 @@ long get_vim_var_nr(int idx) FUNC_ATTR_PURE */ char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { - return get_tv_string(&vimvars[idx].vv_tv); + return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } /* @@ -18499,28 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -/// Get the string value of a variable -/// -/// @warning For number and special values it uses a single, static buffer. It -/// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use get_tv_string() output after -/// calling it again. -/// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but -/// return NULL on error. -/// -/// @param[in] varp Varible to get value of. -/// -/// @return Variable value if it is VAR_STRING variable, number converted to -/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty -/// string. -char_u *get_tv_string(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); -} - char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { @@ -18787,7 +18796,7 @@ static hashtab_T *find_var_ht(const char *name, const size_t name_len, /* * Get the string value of a (global/local) variable. - * Note: see get_tv_string() for how long the pointer remains valid. + * Note: see tv_get_string() for how long the pointer remains valid. * Returns NULL when it doesn't exist. */ char_u *get_var_value(const char *const name) @@ -18798,7 +18807,7 @@ char_u *get_var_value(const char *const name) if (v == NULL) { return NULL; } - return get_tv_string(&v->di_tv); + return (char_u *)tv_get_string(&v->di_tv); } /* @@ -19016,9 +19025,9 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) - v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv)); - else { + if (copy || tv->v_type != VAR_STRING) { + v->di_tv.vval.v_string = (char_u *)xstrdup(tv_get_string(tv)); + } else { // Take over the string to avoid an extra alloc/free. v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; @@ -19439,7 +19448,6 @@ void ex_execute(exarg_T *eap) int ret = OK; char_u *p; garray_T ga; - int len; int save_did_emsg; ga_init(&ga, 1, 80); @@ -19461,12 +19469,13 @@ void ex_execute(exarg_T *eap) } if (!eap->skip) { - p = get_tv_string(&rettv); - len = (int)STRLEN(p); + const char *const argstr = tv_get_string(&rettv); + const size_t len = strlen(argstr); ga_grow(&ga, len + 2); - if (!GA_EMPTY(&ga)) + if (!GA_EMPTY(&ga)) { ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); ga.ga_len += len; } @@ -21920,8 +21929,9 @@ int store_session_globals(FILE *fd) && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { // Escape special characters with a backslash. Turn a LF and // CR into \n and \r. - char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); + char_u *const p = vim_strsave_escaped( + (const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); for (char_u *t = p; *t != NUL; t++) { if (*t == '\n') { *t = 'n'; @@ -21992,7 +22002,7 @@ void ex_oldfiles(exarg_T *eap) for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { msg_outnum(++nr); MSG_PUTS(": "); - msg_outtrans(get_tv_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ @@ -22007,15 +22017,15 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char *p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = (char *)expand_env_save((char_u *)p); - eap->arg = (char_u *)p; + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); - xfree(p); + xfree(s); } } } @@ -22728,7 +22738,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) arguments->lv_refcount++; int dummy; - (void)call_func((uint8_t *)func, + (void)call_func((const char_u *)func, name_len, &rettv, 2, @@ -22750,7 +22760,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -bool eval_has_provider(char *name) +bool eval_has_provider(const char *name) { #define check_provider(name) \ if (has_##name == -1) { \ diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 7f6fd76c46..070bc35bd5 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -2,7 +2,7 @@ #define NVIM_EVAL_H #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" // For exarg_T #include "nvim/eval/typval.h" #include "nvim/profile.h" diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index ab48ace400..41b55e4a57 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -77,10 +77,10 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, if (tv2->v_type == VAR_FLOAT) { break; } - char *s = (char *)get_tv_string(tv1); + const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; - s = (char *)concat_str((char_u *)s, - get_tv_string_buf(tv2, (char_u *)numbuf)); + char *const s = (char *)concat_str( + (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 62460bcc3a..cbeb2e059c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -733,7 +733,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) /// @param[in] n Index in a list. /// /// @return [allocated] Copy of the list item string value. -char *tv_list_find_str(list_T *l, int n) +const char *tv_list_find_str(list_T *l, int n) FUNC_ATTR_MALLOC { const listitem_T *const li = tv_list_find(l, n - 1); @@ -741,7 +741,7 @@ char *tv_list_find_str(list_T *l, int n) EMSGN(_(e_listidx), n); return NULL; } - return (char *)get_tv_string(&li->li_tv); + return tv_get_string(&li->li_tv); } /// Locate item in a list and return its index @@ -2014,3 +2014,27 @@ bool tv_check_str_or_nr(const typval_T *const tv) assert(false); return false; } + +//{{{2 Get + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char_u mybuf[NUMBUFLEN]; + return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 0eef2ddaac..5645772124 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -205,6 +205,8 @@ struct dictvar_S { /// Type used for script ID typedef int scid_T; +/// Format argument for scid_T +#define PRIdSCID "d" // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 87fe52c119..5209dfc451 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -270,7 +270,7 @@ do_exmode ( /* * Execute a simple command line. Used for translated commands like "*". */ -int do_cmdline_cmd(char *cmd) +int do_cmdline_cmd(const char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); @@ -9347,8 +9347,8 @@ static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) *p = '/'; } - /* escape special characters */ - p = vim_strsave_fnameescape(sname, FALSE); + // Escape special characters. + p = (char_u *)vim_strsave_fnameescape((const char *)sname, false); xfree(sname); /* write the result */ diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 7a34a181e2..3f71ae1795 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1149,23 +1149,25 @@ void ex_endwhile(exarg_T *eap) */ void ex_throw(exarg_T *eap) { - char_u *arg = eap->arg; - char_u *value; + const char *arg = (const char *)eap->arg; + char *value; - if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); - else { + if (*arg != NUL && *arg != '|' && *arg != '\n') { + value = eval_to_string_skip(arg, (const char **)&eap->nextcmd, + (bool)eap->skip); + } else { EMSG(_(e_argreq)); value = NULL; } - /* On error or when an exception is thrown during argument evaluation, do - * not throw. */ + // On error or when an exception is thrown during argument evaluation, do + // not throw. if (!eap->skip && value != NULL) { - if (throw_exception(value, ET_USER, NULL) == FAIL) + if (throw_exception((char_u *)value, ET_USER, NULL) == FAIL) { xfree(value); - else + } else { do_throw(eap->cstack); + } } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 872b7fe365..3bd8d580ab 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -8,6 +8,7 @@ #include #include +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -960,7 +961,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode((char_u *)"", LANGMAP, false)) { + if (map_to_exists_mode("", LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. State ^= LANGMAP; if (s->b_im_ptr != NULL) { @@ -3120,9 +3121,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o #endif } #ifdef BACKSLASH_IN_FILENAME - p = vim_strsave_fnameescape(files[i], FALSE); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false); #else - p = vim_strsave_fnameescape(files[i], xp->xp_shell); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], + xp->xp_shell); #endif xfree(files[i]); files[i] = p; @@ -3152,42 +3154,49 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o } } -/* - * Escape special characters in "fname" for when used as a file name argument - * after a Vim command, or, when "shell" is non-zero, a shell command. - * Returns the result in allocated memory. - */ -char_u *vim_strsave_fnameescape(char_u *fname, int shell) FUNC_ATTR_NONNULL_RET +/// Escape special characters in a file name for use as a command argument +/// +/// @param[in] fname File name to escape. +/// @param[in] shell What to escape for: if false, escapes for VimL command, +/// if true then it escapes for a shell command. +/// +/// @return [allocated] escaped file name. +char *vim_strsave_fnameescape(const char *const fname, const bool shell) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *p; #ifdef BACKSLASH_IN_FILENAME -#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`%#'\"|!<") - char_u buf[20]; +#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" + char_u buf[sizeof(PATH_ESC_CHARS)]; int j = 0; - /* Don't escape '[', '{' and '!' if they are in 'isfname'. */ - for (p = PATH_ESC_CHARS; *p != NUL; ++p) - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) - buf[j++] = *p; + // Don't escape '[', '{' and '!' if they are in 'isfname'. + for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) { + if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) { + buf[j++] = *s; + } + } buf[j] = NUL; - p = vim_strsave_escaped(fname, buf); + char *p = (char *)vim_strsave_escaped((const char_u *)fname, + (const char_u *)buf); #else #define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") #define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") - p = vim_strsave_escaped(fname, shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS); + char *p = (char *)vim_strsave_escaped( + (const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); if (shell && csh_like_shell()) { - /* For csh and similar shells need to put two backslashes before '!'. - * One is taken by Vim, one by the shell. */ - char_u *s = vim_strsave_escaped(p, (char_u *)"!"); + // For csh and similar shells need to put two backslashes before '!'. + // One is taken by Vim, one by the shell. + char *s = (char *)vim_strsave_escaped((const char_u *)p, + (const char_u *)"!"); xfree(p); p = s; } #endif - /* '>' and '+' are special at the start of some commands, e.g. ":edit" and - * ":write". "cd -" has a special meaning. */ + // '>' and '+' are special at the start of some commands, e.g. ":edit" and + // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname(&p); + escape_fname((char_u **)&p); } return p; @@ -4197,9 +4206,11 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u keep; garray_T ga; - retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); - if (retstr == NULL) + retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, + num_file, file); + if (retstr == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); for (s = retstr; *s != NUL; s = e) { @@ -4237,9 +4248,11 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) listitem_T *li; garray_T ga; - retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); - if (retlist == NULL) + retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, + num_file, file); + if (retlist == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); /* Loop over the items in the list. */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d948e20b32..67ac9f9957 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -428,7 +428,7 @@ readfile ( } if (!read_buffer && !read_stdin) { - perm = os_getperm(fname); + perm = os_getperm((const char *)fname); #ifdef UNIX // On Unix it is possible to read a directory, so we have to // check for it before os_open(). @@ -2606,10 +2606,10 @@ buf_write ( newfile = TRUE; perm = -1; } else { - perm = os_getperm(fname); - if (perm < 0) - newfile = TRUE; - else if (os_isdir(fname)) { + perm = os_getperm((const char *)fname); + if (perm < 0) { + newfile = true; + } else if (os_isdir(fname)) { errnum = (char_u *)"E502: "; errmsg = (char_u *)_("is a directory"); goto fail; @@ -3628,7 +3628,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm(fname) & 0777); + os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4548,9 +4548,9 @@ int put_time(FILE *fd, time_t time_) /// os_rename() only works if both files are on the same file system, this /// function will (attempts to?) copy the file across if rename fails -- webb -// +/// /// @return -1 for failure, 0 for success -int vim_rename(char_u *from, char_u *to) +int vim_rename(const char_u *from, const char_u *to) { int fd_in; int fd_out; @@ -4569,10 +4569,12 @@ int vim_rename(char_u *from, char_u *to) * the file name differs we need to go through a temp file. */ if (fnamecmp(from, to) == 0) { - if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) + if (p_fic && (STRCMP(path_tail((char_u *)from), path_tail((char_u *)to)) + != 0)) { use_tmp_file = true; - else + } else { return 0; + } } // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. @@ -4638,9 +4640,9 @@ int vim_rename(char_u *from, char_u *to) /* * Rename() failed, try copying the file. */ - perm = os_getperm(from); + perm = os_getperm((const char *)from); #ifdef HAVE_ACL - /* For systems that support ACL: get the ACL from the original file. */ + // For systems that support ACL: get the ACL from the original file. acl = mch_get_acl(from); #endif fd_in = os_open((char *)from, O_RDONLY, 0); @@ -5261,7 +5263,7 @@ static void vim_maketempdir(void) /// Delete "name" and everything in it, recursively. /// @param name The path which should be deleted. /// @return 0 for success, -1 if some file was not deleted. -int delete_recursive(char_u *name) +int delete_recursive(const char *name) { int result = 0; @@ -5275,7 +5277,7 @@ int delete_recursive(char_u *name) EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS | EW_DODOT | EW_EMPTYOK) == OK) { for (int i = 0; i < file_count; i++) { - if (delete_recursive(files[i]) != 0) { + if (delete_recursive((const char *)files[i]) != 0) { result = -1; } } @@ -5285,9 +5287,9 @@ int delete_recursive(char_u *name) } xfree(exp); - os_rmdir((char *)name); + os_rmdir(name); } else { - result = os_remove((char *)name) == 0 ? 0 : -1; + result = os_remove(name) == 0 ? 0 : -1; } return result; @@ -5299,7 +5301,7 @@ void vim_deltempdir(void) if (vim_tempdir != NULL) { // remove the trailing path separator path_tail(vim_tempdir)[-1] = NUL; - delete_recursive(vim_tempdir); + delete_recursive((const char *)vim_tempdir); xfree(vim_tempdir); vim_tempdir = NULL; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 076ee13a80..b64f089b96 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3218,82 +3218,99 @@ showmap ( ui_flush(); /* show one line at a time */ } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". - * Recognize termcap codes in "str". - * Also checks mappings local to the current buffer. - */ -int map_to_exists(char_u *str, char_u *modechars, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] str String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] modechars Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +bool map_to_exists(const char *const str, const char *const modechars, + const bool abbr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int mode = 0; - char_u *rhs; - char_u *buf; int retval; - rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, - CPO_TO_CPO_FLAGS); - - if (vim_strchr(modechars, 'n') != NULL) - mode |= NORMAL; - if (vim_strchr(modechars, 'v') != NULL) - mode |= VISUAL + SELECTMODE; - if (vim_strchr(modechars, 'x') != NULL) - mode |= VISUAL; - if (vim_strchr(modechars, 's') != NULL) - mode |= SELECTMODE; - if (vim_strchr(modechars, 'o') != NULL) - mode |= OP_PENDING; - if (vim_strchr(modechars, 'i') != NULL) - mode |= INSERT; - if (vim_strchr(modechars, 'l') != NULL) - mode |= LANGMAP; - if (vim_strchr(modechars, 'c') != NULL) - mode |= CMDLINE; - - retval = map_to_exists_mode(rhs, mode, abbr); + char_u *buf; + char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, + false, true, false, + CPO_TO_CPO_FLAGS); + +#define MAPMODE(mode, modechars, chr, modeflags) \ + do { \ + if (strchr(modechars, chr) != NULL) { \ + mode |= modeflags; \ + } \ + } while (0) + MAPMODE(mode, modechars, 'n', NORMAL); + MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE); + MAPMODE(mode, modechars, 'x', VISUAL); + MAPMODE(mode, modechars, 's', SELECTMODE); + MAPMODE(mode, modechars, 'o', OP_PENDING); + MAPMODE(mode, modechars, 'i', INSERT); + MAPMODE(mode, modechars, 'l', LANGMAP); + MAPMODE(mode, modechars, 'c', CMDLINE); +#undef MAPMODE + + retval = map_to_exists_mode((const char *)rhs, mode, abbr); xfree(buf); return retval; } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "mode". - * Also checks mappings local to the current buffer. - */ -int map_to_exists_mode(char_u *rhs, int mode, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] rhs String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] mode Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) { mapblock_T *mp; int hash; - int expand_buffer = FALSE; + bool expand_buffer = false; validate_maphash(); - /* Do it twice: once for global maps and once for local maps. */ - for (;; ) { - for (hash = 0; hash < 256; ++hash) { + // Do it twice: once for global maps and once for local maps. + for (;;) { + for (hash = 0; hash < 256; hash++) { if (abbr) { - if (hash > 0) /* there is only one abbr list */ + if (hash > 0) { // There is only one abbr list. break; - if (expand_buffer) + } + if (expand_buffer) { mp = curbuf->b_first_abbr; - else + } else { mp = first_abbr; - } else if (expand_buffer) + } + } else if (expand_buffer) { mp = curbuf->b_maphash[hash]; - else + } else { mp = maphash[hash]; + } for (; mp; mp = mp->m_next) { if ((mp->m_mode & mode) - && strstr((char *)mp->m_str, (char *)rhs) != NULL) - return TRUE; + && strstr((char *)mp->m_str, rhs) != NULL) { + return true; + } } } - if (expand_buffer) + if (expand_buffer) { break; - expand_buffer = TRUE; + } + expand_buffer = true; } - return FALSE; + return false; } /* diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 19d97ecfef..4cb05ffc12 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -369,7 +369,6 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) { int colorindex; uint32_t fg_color; - char *color; pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); @@ -377,11 +376,12 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); { - color = (char *)highlight_color(hl_id, (char_u *)"fg", modec); - if (color == NULL) + const char *color = highlight_color(hl_id, "fg", modec); + if (color == NULL) { colorindex = 0; - else + } else { colorindex = atoi(color); + } if (colorindex >= 0 && colorindex < t_colors) fg_color = prt_get_term_color(colorindex); diff --git a/src/nvim/message.c b/src/nvim/message.c index bf54284881..423f5a27e7 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -718,7 +718,7 @@ int delete_first_msg(void) void ex_messages(void *const eap_p) FUNC_ATTR_NONNULL_ALL { - exarg_T *eap = (exarg_T *)eap_p; + const exarg_T *const eap = (const exarg_T *)eap_p; struct msg_hist *p; int c = 0; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de6167c7fc..259dcc523c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -147,7 +147,7 @@ void channel_from_connection(SocketWatcher *watcher) /// @param name The event name, an arbitrary string /// @param args Array with event arguments /// @return True if the event was sent successfully, false otherwise. -bool channel_send_event(uint64_t id, char *name, Array args) +bool channel_send_event(uint64_t id, const char *name, Array args) { Channel *channel = NULL; @@ -160,7 +160,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (channel) { if (channel->pending_requests) { // Pending request, queue the notification for later sending. - String method = cstr_as_string(name); + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); kv_push(channel->delayed_notifications, buffer); } else { @@ -182,7 +182,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) /// @param[out] error True if the return value is an error /// @return Whatever the remote method returned Object channel_send_call(uint64_t id, - char *method_name, + const char *method_name, Array args, Error *err) { @@ -519,10 +519,10 @@ static void send_error(Channel *channel, uint64_t id, char *err) static void send_request(Channel *channel, uint64_t id, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, id, method, @@ -532,10 +532,10 @@ static void send_request(Channel *channel, } static void send_event(Channel *channel, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, 0, method, @@ -544,7 +544,7 @@ static void send_event(Channel *channel, 1)); } -static void broadcast_event(char *name, Array args) +static void broadcast_event(const char *name, Array args) { kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; @@ -560,7 +560,7 @@ static void broadcast_event(char *name, Array args) goto end; } - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(0, 0, method, @@ -728,7 +728,7 @@ static void call_set_error(Channel *channel, char *msg) static WBuffer *serialize_request(uint64_t channel_id, uint64_t request_id, - String method, + const String method, Array args, msgpack_sbuffer *sbuffer, size_t refcount) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5137b375f0..808bb863fd 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -322,7 +322,7 @@ void msgpack_rpc_from_float(Float result, msgpack_packer *res) msgpack_pack_double(res, result); } -void msgpack_rpc_from_string(String result, msgpack_packer *res) +void msgpack_rpc_from_string(const String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_str(res, result.size); @@ -478,7 +478,7 @@ Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, /// Serializes a msgpack-rpc request or notification(id == 0) void msgpack_rpc_serialize_request(uint64_t request_id, - String method, + const String method, Array args, msgpack_packer *pac) FUNC_ATTR_NONNULL_ARG(4) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 4cca5ec948..6ef929120e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2075,7 +2075,6 @@ static void op_colon(oparg_T *oap) */ static void op_function(oparg_T *oap) { - char_u *(argv[1]); int save_virtual_op = virtual_op; if (*p_opfunc == NUL) @@ -2089,16 +2088,16 @@ static void op_function(oparg_T *oap) decl(&curbuf->b_op_end); } - if (oap->motion_type == kMTBlockWise) { - argv[0] = (char_u *)"block"; - } else if (oap->motion_type == kMTLineWise) { - argv[0] = (char_u *)"line"; - } else { - argv[0] = (char_u *)"char"; - } + const char_u *const argv[1] = { + (const char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]), + }; - /* Reset virtual_op so that 'virtualedit' can be changed in the - * function. */ + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. virtual_op = MAYBE; (void)call_func_retnr(p_opfunc, 1, argv, false); @@ -4757,7 +4756,7 @@ static void nv_ident(cmdarg_T *cap) ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape(ptr, false); + p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false); } else { // Escape the argument properly for a shell command p = vim_strsave_shellescape(ptr, true, true); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2beeae7ec6..0372bc8a8c 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -91,11 +91,11 @@ int os_dirname(char_u *buf, size_t len) /// Check if the given path is a directory and not a symlink to a directory. /// @return `true` if `name` is a directory and NOT a symlink to a directory. /// `false` if `name` is not a directory or if an error occurred. -bool os_isrealdir(const char_u *name) +bool os_isrealdir(const char *name) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) { return false; } if (S_ISLNK(request.statbuf.st_mode)) { @@ -111,7 +111,7 @@ bool os_isrealdir(const char_u *name) bool os_isdir(const char_u *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm(name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; } @@ -236,7 +236,8 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) pathext); #else // Must have path separator, cannot execute files in the current directory. - bool ok = gettail_dir(name) != name && is_executable((char *)name); + const bool ok = ((const char_u *)gettail_dir((const char *)name) != name + && is_executable((char *)name)); #endif if (ok) { if (abspath != NULL) { @@ -254,7 +255,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm((char_u *)name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; @@ -606,11 +607,11 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// Get the file permissions for a given file. /// /// @return libuv error code on error. -int32_t os_getperm(const char_u *name) +int32_t os_getperm(const char *name) FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; - int stat_result = os_stat((char *)name, &statbuf); + int stat_result = os_stat(name, &statbuf); if (stat_result == kLibuvSuccess) { return (int32_t)statbuf.st_mode; } else { @@ -979,13 +980,13 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points /// to and return that name in allocated memory. /// Otherwise NULL is returned. -char *os_resolve_shortcut(char_u *fname) +char *os_resolve_shortcut(const char *fname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; OLECHAR wsz[MAX_PATH]; char *rfname = NULL; - int len; IShellLinkW *pslw = NULL; WIN32_FIND_DATAW ffdw; @@ -994,7 +995,7 @@ char *os_resolve_shortcut(char_u *fname) if (fname == NULL) { return rfname; } - len = (int)STRLEN(fname); + const size_t len = strlen(fname); if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) { return rfname; } @@ -1006,7 +1007,7 @@ char *os_resolve_shortcut(char_u *fname) &IID_IShellLinkW, (void **)&pslw); if (hr == S_OK) { WCHAR *p; - int conversion_result = utf8_to_utf16((char *)fname, &p); + const int conversion_result = utf8_to_utf16(fname, &p); if (conversion_result != 0) { EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result)); } @@ -1036,7 +1037,7 @@ char *os_resolve_shortcut(char_u *fname) ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR)); hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); if (hr == S_OK && wsz[0] != NUL) { - int conversion_result = utf16_to_utf8(wsz, &rfname); + const int conversion_result = utf16_to_utf8(wsz, &rfname); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result)); } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index ed3410ffe5..acd86f06dc 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -110,14 +110,14 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) // Return a pointer to the ACL of file "fname" in allocated memory. // Return NULL if the ACL is not available for whatever reason. -vim_acl_T mch_get_acl(char_u *fname) +vim_acl_T mch_get_acl(const char_u *fname) { vim_acl_T ret = NULL; return ret; } // Set the ACL of file "fname" to "acl" (unless it's NULL). -void mch_set_acl(char_u *fname, vim_acl_T aclent) +void mch_set_acl(const char_u *fname, vim_acl_T aclent) { if (aclent == NULL) return; diff --git a/src/nvim/path.c b/src/nvim/path.c index 2bd87b608e..e92261f4fd 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -159,7 +159,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len) /// @param fname A file path. (Must be != NULL.) /// @return Pointer to first found path separator + 1. /// An empty string, if `fname` doesn't contain a path separator, -char_u *path_next_component(char_u *fname) +const char *path_next_component(const char *fname) { assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { @@ -431,7 +431,7 @@ bool add_pathsep(char *p) /// /// @return [allocated] Copy of absolute path to `fname` or NULL when /// `fname` is NULL. -char *FullName_save(char *fname, bool force) +char *FullName_save(const char *fname, bool force) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { if (fname == NULL) { @@ -906,9 +906,9 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { - char_u *path = fnames[i]; + char_u *path = fnames[i]; int is_in_curdir; - char_u *dir_end = gettail_dir(path); + char_u *dir_end = (char_u *)gettail_dir((const char *)path); char_u *pathsep_p; char_u *path_cutoff; @@ -1010,18 +1010,22 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) ga_remove_duplicate_strings(gap); } -/// Return the end of the directory name, on the first path -/// separator: -/// "/path/file", "/path/dir/", "/path//dir", "/file" -/// ^ ^ ^ ^ -char_u *gettail_dir(const char_u *fname) +/// Find end of the directory name +/// +/// @param[in] fname File name to process. +/// +/// @return end of the directory name, on the first path separator: +/// +/// "/path/file", "/path/dir/", "/path//dir", "/file" +/// ^ ^ ^ ^ +const char *gettail_dir(const char *const fname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const char_u *dir_end = fname; - const char_u *next_dir_end = fname; + const char *dir_end = fname; + const char *next_dir_end = fname; bool look_for_sep = true; - const char_u *p; - for (p = fname; *p != NUL; ) { + for (const char *p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { if (look_for_sep) { next_dir_end = p; @@ -1034,7 +1038,7 @@ char_u *gettail_dir(const char_u *fname) } mb_ptr_adv(p); } - return (char_u *)dir_end; + return dir_end; } @@ -1553,8 +1557,8 @@ void simplify_filename(char_u *filename) p = tail; /* skip to char after ".." or "../" */ } } else { - ++components; /* simple path component */ - p = path_next_component(p); + components++; // Simple path component. + p = (char_u *)path_next_component((const char *)p); } } while (*p != NUL); } diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index 7670b64468..c72dafd08e 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -259,11 +259,11 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE]) /// /// @returns hex digest of "buf[buf_len]" in a static array. /// if "salt" is not NULL also do "salt[salt_len]". -char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, - const char_u *restrict salt, size_t salt_len) +const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, + const uint8_t *restrict salt, size_t salt_len) { char_u sha256sum[SHA256_SUM_SIZE]; - static char_u hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL + static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL context_sha256_T ctx; sha256_self_test(); @@ -277,7 +277,7 @@ char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, sha256_finish(&ctx, sha256sum); for (size_t j = 0; j < SHA256_SUM_SIZE; j++) { - snprintf((char *) hexit + j * SHA_STEP, SHA_STEP+1, "%02x", sha256sum[j]); + snprintf(hexit + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]); } hexit[sizeof(hexit) - 1] = '\0'; return hexit; @@ -308,7 +308,7 @@ bool sha256_self_test(void) context_sha256_T ctx; char_u buf[1000]; char_u sha256sum[SHA256_SUM_SIZE]; - char_u *hexit; + const char *hexit; static bool sha256_self_tested = false; static bool failures = false; @@ -320,8 +320,8 @@ bool sha256_self_test(void) for (size_t i = 0; i < 3; i++) { if (i < 2) { - hexit = sha256_bytes((char_u *) sha_self_test_msg[i], - STRLEN(sha_self_test_msg[i]), + hexit = sha256_bytes((uint8_t *)sha_self_test_msg[i], + strlen(sha_self_test_msg[i]), NULL, 0); STRCPY(output, hexit); } else { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 2fe042cda8..0f04a5e9cf 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -82,8 +82,6 @@ KHASH_SET_INIT_STR(strset) (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) #define convert_setup(vcp, from, to) \ (convert_setup(vcp, (char_u *)from, (char_u *)to)) -#define os_getperm(f) \ - (os_getperm((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 56f0350aef..84bee9b97f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3232,7 +3232,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) list_T *list; listitem_T *li; int score; - char_u *p; + const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. @@ -3244,9 +3244,10 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) if (li->li_tv.v_type == VAR_LIST) { // Get the word and the score from the items. score = get_spellword(li->li_tv.vval.v_list, &p); - if (score >= 0 && score <= su->su_maxscore) - add_suggestion(su, &su->su_ga, p, su->su_badlen, - score, 0, true, su->su_sallang, false); + if (score >= 0 && score <= su->su_maxscore) { + add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, + score, 0, true, su->su_sallang, false); + } } tv_list_unref(list); } @@ -5616,7 +5617,7 @@ static void add_suggestion ( suginfo_T *su, garray_T *gap, // either su_ga or su_sga - char_u *goodword, + const char_u *goodword, int badlenarg, // len of bad word replaced with "goodword" int score, int altscore, @@ -5630,13 +5631,11 @@ add_suggestion ( int badlen; // len of bad word changed suggest_T *stp; suggest_T new_sug; - int i; - char_u *pgood, *pbad; // Minimize "badlen" for consistency. Avoids that changing "the the" to // "thee the" is added next to changing the first "the" the "thee". - pgood = goodword + STRLEN(goodword); - pbad = su->su_badptr + badlenarg; + const char_u *pgood = goodword + STRLEN(goodword); + char_u *pbad = su->su_badptr + badlenarg; for (;; ) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); @@ -5656,9 +5655,10 @@ add_suggestion ( // the first "the" to itself. return; - if (GA_EMPTY(gap)) + int i; + if (GA_EMPTY(gap)) { i = -1; - else { + } else { // Check if the word is already there. Also check the length that is // being replaced "thes," -> "these" is a different suggestion from // "thes" -> "these". @@ -5857,27 +5857,31 @@ cleanup_suggestions ( return maxscore; } -// Soundfold a string, for soundfold(). -// Result is in allocated memory, NULL for an error. -char_u *eval_soundfold(char_u *word) +/// Soundfold a string, for soundfold() +/// +/// @param[in] word Word to soundfold. +/// +/// @return [allocated] soundfolded string or NULL in case of error. May return +/// copy of the input string if soundfolding is not +/// supported by any of the languages in &spellang. +char *eval_soundfold(const char *const word) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - langp_T *lp; - char_u sound[MAXWLEN]; - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { // Use the sound-folding of the first language that supports it. - for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { + langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // soundfold the word - spell_soundfold(lp->lp_slang, word, false, sound); - return vim_strsave(sound); + char_u sound[MAXWLEN]; + spell_soundfold(lp->lp_slang, (char_u *)word, false, sound); + return xstrdup((const char *)sound); } } } // No language with sound folding, return word as-is. - return vim_strsave(word); + return xstrdup(word); } /// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index b964fed35a..9cfa126a56 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -291,30 +291,33 @@ void vim_strup(char_u *p) } } -/* - * Make string "s" all upper-case and return it in allocated memory. - * Handles multi-byte characters as well as possible. - */ -char_u *strup_save(const char_u *orig) +/// Make given string all upper-case +/// +/// Handels multi-byte characters as good as possible. +/// +/// @param[in] orig Input string. +/// +/// @return [allocated] upper-cased string. +char *strup_save(const char *const orig) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *res = vim_strsave(orig); + char *res = xstrdup(orig); - char_u *p = res; + char *p = res; while (*p != NUL) { int l; if (enc_utf8) { - int c = utf_ptr2char(p); + int c = utf_ptr2char((const char_u *)p); int uc = utf_toupper(c); - /* Reallocate string when byte count changes. This is rare, - * thus it's OK to do another malloc()/free(). */ - l = utf_ptr2len(p); + // Reallocate string when byte count changes. This is rare, + // thus it's OK to do another malloc()/free(). + l = utf_ptr2len((const char_u *)p); int newl = utf_char2len(uc); if (newl != l) { // TODO(philix): use xrealloc() in strup_save() - char_u *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); + char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); memcpy(s, res, (size_t)(p - res)); STRCPY(s + (p - res) + newl, p + l); p = s + (p - res); @@ -322,12 +325,13 @@ char_u *strup_save(const char_u *orig) res = s; } - utf_char2bytes(uc, p); + utf_char2bytes(uc, (char_u *)p); p += newl; - } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else { - *p = (char_u) TOUPPER_LOC(*p); // note that toupper() can be a macro + } else if (has_mbyte && (l = (*mb_ptr2len)((const char_u *)p)) > 1) { + p += l; // Skip multi-byte character. + } else { + // note that toupper() can be a macro + *p = (char)(uint8_t)TOUPPER_LOC(*p); p++; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 632a5bf2a5..3f84b8080f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6930,21 +6930,21 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg return didh; } -/* - * Return "1" if highlight group "id" has attribute "flag". - * Return NULL otherwise. - */ -char_u * -highlight_has_attr ( - int id, - int flag, - int modec // 'g' for GUI, 'c' for cterm -) +/// Check whether highlight group has attribute +/// +/// @param[in] id Highilght group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int attr; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } if (modec == 'g') { attr = HL_TABLE()[id - 1].sg_gui; @@ -6952,39 +6952,42 @@ highlight_has_attr ( attr = HL_TABLE()[id - 1].sg_cterm; } - if (attr & flag) - return (char_u *)"1"; - return NULL; + return (attr & flag) ? "1" : NULL; } -/* - * Return color name of highlight group "id". - */ -char_u * -highlight_color ( - int id, - char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ - int modec /* 'g' for GUI, 'c' for cterm, 't' for term */ -) +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, + const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - static char_u name[20]; + static char name[20]; int n; - int fg = FALSE; - int sp = FALSE; - int font = FALSE; + bool fg = false; + bool sp = false; + bool font = false; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') - fg = TRUE; - else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') - font = TRUE; - else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') - sp = TRUE; - else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { return NULL; + } if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { @@ -6997,19 +7000,20 @@ highlight_color ( if (n < 0 || n > 0xffffff) { return NULL; } - snprintf((char *)name, sizeof(name), "#%06x", n); + snprintf(name, sizeof(name), "#%06x", n); return name; } if (fg) { - return HL_TABLE()[id - 1].sg_rgb_fg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; } if (sp) { - return HL_TABLE()[id - 1].sg_rgb_sp_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; } - return HL_TABLE()[id - 1].sg_rgb_bg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } - if (font || sp) + if (font || sp) { return NULL; + } if (modec == 'c') { if (fg) { n = HL_TABLE()[id - 1].sg_cterm_fg - 1; @@ -7019,10 +7023,10 @@ highlight_color ( if (n < 0) { return NULL; } - snprintf((char *)name, sizeof(name), "%d", n); + snprintf(name, sizeof(name), "%d", n); return name; } - /* term doesn't have color */ + // term doesn't have color. return NULL; } @@ -7133,7 +7137,7 @@ int syn_name2id(const char_u *name) /* * Return TRUE if highlight group "name" exists. */ -int highlight_exists(char_u *name) +int highlight_exists(const char_u *name) { return syn_name2id(name) > 0; } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index ba687bf6da..010ef2c8fa 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1081,7 +1081,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, */ perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm(buf->b_ffname); + perm = os_getperm((const char *)buf->b_ffname); if (perm < 0) { perm = 0600; } diff --git a/src/nvim/version.c b/src/nvim/version.c index dd583f6ffd..f1d39b8492 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -73,7 +73,7 @@ static char *features[] = { }; // clang-format off -static int included_patches[] = { +static const int included_patches[] = { // 2367,NA // 2366 NA // 2365 NA @@ -2461,10 +2461,10 @@ static char *(extra_patches[]) = { /// @param version Version string like "1.3.42" /// /// @return true if Nvim is at or above the version. -bool has_nvim_version(char *version_str) - FUNC_ATTR_NONNULL_ALL +bool has_nvim_version(const char *const version_str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char *p = version_str; + const char *p = version_str; int major = 0; int minor = 0; int patch = 0; @@ -2473,7 +2473,7 @@ bool has_nvim_version(char *version_str) return false; } major = atoi(p); - p = strchr(p, '.'); // Find the next dot. + p = strchr(p, '.'); // Find the next dot. if (p) { p++; // Advance past the dot. @@ -2481,7 +2481,7 @@ bool has_nvim_version(char *version_str) return false; } minor = atoi(p); - p = strchr(p, '.'); + p = strchr(p, '.'); if (p) { p++; if (!ascii_isdigit(*p)) { -- cgit From 949f09bdbba592a12629c71e20ff7bb49a21db6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:47:56 +0300 Subject: eval: Move get_tv_string_buf() to eval/typval.c --- src/nvim/eval.c | 184 +++++++++++++++++++++++------------------------ src/nvim/eval/executor.c | 3 +- src/nvim/eval/typval.c | 36 ++++++++-- src/nvim/ex_docmd.c | 13 +++- 4 files changed, 130 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7e40f5e828..d26ff5e41d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3531,8 +3531,6 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) int type_is = FALSE; /* TRUE for "is" and "isnot" */ int len = 2; long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; int ic; /* @@ -3737,31 +3735,33 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_NOMATCH: break; /* avoid gcc warning */ } } else { - s1 = get_tv_string_buf(rettv, buf1); - s2 = get_tv_string_buf(&var2, buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf(&var2, buf2); if (type != TYPE_MATCH && type != TYPE_NOMATCH) { - i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + i = mb_strcmp_ic((bool)ic, s1, s2); } else { i = 0; } n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: - n1 = pattern_match(s2, s1, ic); - if (type == TYPE_NOMATCH) { - n1 = !n1; + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; + + case TYPE_MATCH: + case TYPE_NOMATCH: { + n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); + if (type == TYPE_NOMATCH) { + n1 = !n1; + } + break; } - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ + case TYPE_UNKNOWN: break; // Avoid gcc warning. } } tv_clear(rettv); @@ -3794,8 +3794,6 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) int op; long n1, n2; float_T f1 = 0, f2 = 0; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *p; /* @@ -3842,14 +3840,17 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (op == '.') { - s1 = get_tv_string_buf(rettv, buf1); /* already checked */ - s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error ? + char buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); return FAIL; } - p = concat_str(s1, s2); + p = concat_str((const char_u *)s1, (const char_u *)s2); tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -7565,20 +7566,21 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; + const char *dbpath = NULL; + const char *prepend = NULL; + char buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = (char_u *)tv_get_string(&argvars[1]); + dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - prepend = get_tv_string_buf(&argvars[2], buf); + prepend = tv_get_string_buf(&argvars[2], buf); } } - rettv->vval.v_number = cs_connection(num, dbpath, prepend); + rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, + (char_u *)prepend); } /// "cursor(lnum, col)" function, or @@ -7661,9 +7663,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - char_u *flags; - rettv->vval.v_number = -1; if (check_restricted() || check_secure()) { return; @@ -7675,19 +7674,21 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + char nbuf[NUMBUFLEN]; + const char *flags; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } else { - flags = (char_u *)""; + flags = ""; } if (*flags == NUL) { // delete a file rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "d") == 0) { + } else if (strcmp(flags, "d") == 0) { // delete an empty directory rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "rf") == 0) { + } else if (strcmp(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); } else { @@ -7905,11 +7906,11 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; rettv->vval.v_string = vim_strsave_escaped( (const char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -8253,19 +8254,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - - /* This is not allowed in the sandbox. If the commands would still be - * executed in the sandbox it would be OK, but it probably happens later, - * when "sandbox" is no longer set. */ - if (check_secure()) + // This is not allowed in the sandbox. If the commands would still be + // executed in the sandbox it would be OK, but it probably happens later, + // when "sandbox" is no longer set. + if (check_secure()) { return; + } const char *const keys = tv_get_string(&argvars[0]); - + char nbuf[NUMBUFLEN]; const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -10740,11 +10740,11 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *mode; const char *const name = tv_get_string(&argvars[0]); bool abbr = false; - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; if (argvars[1].v_type == VAR_UNKNOWN) { mode = "nvo"; } else { - mode = (const char *)get_tv_string_buf(&argvars[1], buf); + mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); } @@ -10763,17 +10763,16 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType histype; - char_u *str; - char_u buf[NUMBUFLEN]; rettv->vval.v_number = false; if (check_restricted() || check_secure()) { return; } - str = get_tv_string_chk(&argvars[0]); // NULL on type error + char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { - str = get_tv_string_buf(&argvars[1], buf); + char buf[NUMBUFLEN]; + str = (char_u *)tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); add_to_history(histype, str, false, NUL); @@ -10789,7 +10788,6 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u buf[NUMBUFLEN]; char_u *str; str = get_tv_string_chk(&argvars[0]); // NULL on type error @@ -10801,11 +10799,12 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int) get_tv_number(&argvars[1])); + (int)get_tv_number(&argvars[1])); } else { // string given: remove all matching entries + char buf[NUMBUFLEN]; n = del_history_entry(get_histtype(str, STRLEN(str), false), - get_tv_string_buf(&argvars[1], buf)); + (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; } @@ -10895,12 +10894,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const str = tv_get_string(&argvars[0]); - char_u buf1[NUMBUFLEN]; + char buf1[NUMBUFLEN]; char_u *const from = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[1], buf1))); - char_u buf2[NUMBUFLEN]; + (char_u *)tv_get_string_buf(&argvars[1], buf1))); + char buf2[NUMBUFLEN]; char_u *const to = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[2], buf2))); + (char_u *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); @@ -10983,7 +10982,6 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) char_u *prompt = get_tv_string_chk(&argvars[0]); char_u *p = NULL; int c; - char_u buf[NUMBUFLEN]; int cmd_silent_save = cmd_silent; char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; @@ -11013,9 +11011,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmdline_row = msg_row; if (argvars[1].v_type != VAR_UNKNOWN) { + char_u buf[NUMBUFLEN]; defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) + if (defstr != NULL) { stuffReadbuffSpec(defstr); + } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { char_u *xp_name; @@ -11026,8 +11026,9 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) rettv->vval.v_string = NULL; xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) + if (xp_name == NULL) { return; + } xp_namelen = (int)STRLEN(xp_name); @@ -11047,9 +11048,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) } if (inputdialog && rettv->vval.v_string == NULL && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); + && argvars[2].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf( + &argvars[2], buf)); + } xfree(xp_arg); @@ -12535,21 +12538,21 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *dir; - char_u buf[NUMBUFLEN]; int prot = 0755; rettv->vval.v_number = FAIL; if (check_restricted() || check_secure()) return; - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) + char buf[NUMBUFLEN]; + const char *const dir = tv_get_string_buf(&argvars[0], buf); + if (*dir == NUL) { rettv->vval.v_number = FAIL; - else { - if (*path_tail(dir) == NUL) - /* remove trailing slashes */ - *path_tail_with_sep(dir) = NUL; + } else { + if (*path_tail((char_u *)dir) == NUL) { + // Remove trailing slashes. + *path_tail_with_sep((char_u *)dir) = NUL; + } if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { @@ -12557,7 +12560,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; - int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); + int ret = os_mkdir_recurse(dir, prot, &failed_dir); if (ret != 0) { EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); xfree(failed_dir); @@ -12840,14 +12843,13 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; { - char_u buf[NUMBUFLEN]; int len; int saved_did_emsg = did_emsg; - char *fmt; - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); + // Get the required length, allocate the buffer and do it for real. + did_emsg = false; + char buf[NUMBUFLEN]; + const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { char *s = xmalloc(len + 1); @@ -13270,14 +13272,13 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; } else { + char buf[NUMBUFLEN]; rettv->vval.v_number = vim_rename( - (char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); } } @@ -16003,8 +16004,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int modec; if (argvars[2].v_type != VAR_UNKNOWN) { char modebuf[NUMBUFLEN]; - const char *const mode = (const char *)get_tv_string_buf(&argvars[2], - (char_u *)modebuf); + const char *const mode = tv_get_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); if (modec != 'c' && modec != 'g') { modec = 0; // Replace invalid with current. @@ -18530,14 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - char_u *const res = get_tv_string_buf_chk(varp, buf); - - return res != NULL ? res : (char_u *)""; -} - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 41b55e4a57..d2d0873792 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -80,7 +80,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; char *const s = (char *)concat_str( - (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); + (const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, + numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index cbeb2e059c..2c2d0ecaab 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1127,7 +1127,7 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, if (di == NULL) { return NULL; } - return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); + return tv_get_string_buf(&di->di_tv, numbuf); } /// Get a function from a dictionary @@ -1948,8 +1948,8 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, case VAR_STRING: { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; - const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); - const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + const char *s1 = tv_get_string_buf(tv1, buf1); + const char *s2 = tv_get_string_buf(tv2, buf2); return mb_strcmp_ic((bool)ic, s1, s2) == 0; } case VAR_SPECIAL: { @@ -2021,7 +2021,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use tv_get_string() output after +/// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but @@ -2035,6 +2035,30 @@ bool tv_check_str_or_nr(const typval_T *const tv) const char *tv_get_string(const typval_T *const varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - static char_u mybuf[NUMBUFLEN]; - return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); + static char mybuf[NUMBUFLEN]; + return tv_get_string_buf((typval_T *)varp, mybuf); +} + +/// Get the string value of a variable +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string_buf(const typval_T *const varp, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const res = (const char *)get_tv_string_buf_chk( + (typval_T *)varp, (char_u *)buf); + + return res != NULL ? res : ""; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5209dfc451..6b661cff11 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7724,7 +7724,7 @@ static void ex_mkrc(exarg_T *eap) /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { - vim_mkdir_emsg(p_vdir, 0755); + vim_mkdir_emsg((const char *)p_vdir, 0755); } fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); @@ -7836,10 +7836,17 @@ static void ex_mkrc(exarg_T *eap) xfree(viewFile); } -int vim_mkdir_emsg(char_u *name, int prot) +/// Try creating a directory, give error message on failure +/// +/// @param[in] name Directory to create. +/// @param[in] prot Directory permissions. +/// +/// @return OK in case of success, FAIL otherwise. +int vim_mkdir_emsg(const char *const name, const int prot) + FUNC_ATTR_NONNULL_ALL { int ret; - if ((ret = os_mkdir((char *)name, prot)) != 0) { + if ((ret = os_mkdir(name, prot)) != 0) { EMSG3(_(e_mkdir), name, os_strerror(ret)); return FAIL; } -- cgit From 7ee5cc7429017a4a08a8b62b628bea156fea0008 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:09:29 +0300 Subject: eval: Move get_tv_lnum and get_tv_float to eval/typval.h Additionally - Rename former tv_get_float to tv_get_float_chk due to name conflict (former get_tv_float is better suited for being tv_get_float). - Add E907 error to get_tv_float() and test that it is being raised when appropriate. --- src/nvim/eval.c | 261 ++++++++++++++++++++----------------------------- src/nvim/eval/typval.c | 94 +++++++++++++++--- src/nvim/eval/typval.h | 9 +- 3 files changed, 193 insertions(+), 171 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d26ff5e41d..6307dd8abc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6498,7 +6498,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6610,7 +6610,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) u_sync(TRUE); } - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 0 && lnum <= curbuf->b_ml.ml_line_count && u_save(lnum, lnum + 1) == OK) { @@ -6957,7 +6957,7 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -7333,7 +7333,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) linenr_T lnum; pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_c_indent(); @@ -7613,7 +7613,7 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_curswant = false; } } else { - line = get_tv_lnum(argvars); + line = tv_get_lnum(argvars); col = get_tv_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { coladd = get_tv_number_chk(&argvars[2], NULL); @@ -7808,7 +7808,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); + rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); } /* @@ -7816,7 +7816,7 @@ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum = get_tv_lnum(argvars); + linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; static int changedtick = 0; static int fnum = 0; @@ -8557,7 +8557,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { if (f < VARNUMBER_MIN) { rettv->vval.v_number = VARNUMBER_MIN; } else if (f > VARNUMBER_MAX) { @@ -8577,7 +8577,7 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -8629,16 +8629,16 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) { - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) { - if (end) + linenr_T first; + linenr_T last; + if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) { + if (end) { rettv->vval.v_number = (varnumber_T)last; - else + } else { rettv->vval.v_number = (varnumber_T)first; + } return; } } @@ -8666,11 +8666,10 @@ static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = foldLevel(lnum); + } } /* @@ -8734,7 +8733,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *text; char_u buf[51]; foldinfo_T foldinfo; @@ -8742,10 +8740,11 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) + linenr_T lnum = tv_get_lnum(argvars); + // Treat illegal types and illegal string values for {lnum} the same. + if (lnum < 0) { lnum = 0; + } fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { text = get_foldtext(curwin, lnum, lnum + fold_count - 1, @@ -9782,17 +9781,16 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; linenr_T end; - int retlist; + bool retlist; - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { end = 0; - retlist = FALSE; + retlist = false; } else { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; + end = tv_get_lnum(&argvars[1]); + retlist = true; } get_buffer_lines(curbuf, lnum, end, retlist, rettv); @@ -10920,13 +10918,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = get_indent_lnum(lnum); - else + } else { rettv->vval.v_number = -1; + } } /* @@ -11975,15 +11972,15 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); - if (rettv->vval.v_number >= 0) - ++rettv->vval.v_number; + } + if (rettv->vval.v_number >= 0) { + rettv->vval.v_number++; + } } /* @@ -11991,17 +11988,15 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + const pos_T pos = curwin->w_cursor; + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_lisp_indent(); curwin->w_cursor = pos; - } else + } else { rettv->vval.v_number = -1; + } } /* @@ -12746,13 +12741,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum; - for (lnum = get_tv_lnum(argvars);; ++lnum) { + for (lnum = tv_get_lnum(argvars);; lnum++) { if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; break; } - if (*skipwhite(ml_get(lnum)) != NUL) + if (*skipwhite(ml_get(lnum)) != NUL) { break; + } } rettv->vval.v_number = lnum; } @@ -12812,7 +12808,7 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -12824,14 +12820,14 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; - else - while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) - --lnum; + } else { + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { + lnum--; + } + } rettv->vval.v_number = lnum; } @@ -14428,14 +14424,13 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; - lnum = get_tv_lnum(&argvars[0]); + linenr_T lnum = tv_get_lnum(&argvars[0]); if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; @@ -15017,8 +15012,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } if (sortinfo->item_compare_float) { - float_T v1 = get_tv_float(tv1); - float_T v2 = get_tv_float(tv2); + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15977,19 +15972,18 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = 0; - long lnum; - long col; - int trans; - bool transerr = false; + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = get_tv_number_chk(&argvars[2], &transerr); + bool transerr = false; + const int trans = get_tv_number_chk(&argvars[2], &transerr); + int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && col < (long)STRLEN(ml_get(lnum))) - id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE); + && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) { + id = syn_get_id(curwin, lnum, col, trans, NULL, false); + } rettv->vval.v_number = id; } @@ -16090,8 +16084,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; int syntax_flags = 0; int cchar; int matchid = 0; @@ -16100,15 +16092,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); + && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { + (void)syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character @@ -16137,21 +16130,19 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; - rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum))) { + && (size_t)col <= STRLEN(ml_get(lnum))) { tv_list_alloc_ret(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); + (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -17457,31 +17448,35 @@ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Translate a String variable into a position. - * Returns NULL when there is an error. - */ -static pos_T * -var2fpos ( - typval_T *varp, - int dollar_lnum, /* TRUE when $ is last line */ - int *fnum /* set to fnum for '0, 'A, etc. */ -) +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, + int *const ret_fnum) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { char_u *name; static pos_T pos; pos_T *pp; - /* Argument can be [lnum, col, coladd]. */ - if (varp->v_type == VAR_LIST) { + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { list_T *l; int len; bool error = false; listitem_T *li; - l = varp->vval.v_list; - if (l == NULL) + l = tv->vval.v_list; + if (l == NULL) { return NULL; + } // Get the line number. pos.lnum = tv_list_find_nr(l, 0L, &error); @@ -17521,20 +17516,24 @@ var2fpos ( return &pos; } - name = get_tv_string_chk(varp); - if (name == NULL) + name = get_tv_string_chk(tv); + if (name == NULL) { return NULL; - if (name[0] == '.') /* cursor */ + } + if (name[0] == '.') { // Cursor. return &curwin->w_cursor; - if (name[0] == 'v' && name[1] == NUL) { /* Visual start */ - if (VIsual_active) + } + if (name[0] == 'v' && name[1] == NUL) { // Visual start. + if (VIsual_active) { return &VIsual; + } return &curwin->w_cursor; } - if (name[0] == '\'') { /* mark */ - pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); - if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) + if (name[0] == '\'') { // Mark. + pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; + } return pp; } @@ -18465,54 +18464,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -static float_T get_tv_float(typval_T *varp) -{ - switch (varp->v_type) { - case VAR_NUMBER: - return (float_T)(varp->vval.v_number); - case VAR_FLOAT: - return varp->vval.v_float; - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E891: Using a Funcref as a Float")); - break; - case VAR_STRING: - EMSG(_("E892: Using a String as a Float")); - break; - case VAR_LIST: - EMSG(_("E893: Using a List as a Float")); - break; - case VAR_DICT: - EMSG(_("E894: Using a Dictionary as a Float")); - break; - default: - EMSG2(_(e_intern2), "get_tv_float()"); - break; - } - return 0; -} - -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ -static linenr_T get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) { /* no valid number, try using line() */ - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv, NULL); - lnum = rettv.vval.v_number; - tv_clear(&rettv); - } - return lnum; -} - /* * Get the lnum from the first argument. * Also accepts "$", then "buf" is used. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2c2d0ecaab..6a16b3869a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -16,6 +16,7 @@ #include "nvim/hashtab.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/pos.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -1730,11 +1731,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// -/// @param[in,out] varp Value to free. -void tv_clear(typval_T *varp) +/// @param[in,out] tv Value to free. +void tv_clear(typval_T *tv) { - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + if (tv != NULL && tv->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); (void)evn_ret; assert(evn_ret == OK); } @@ -2017,7 +2018,72 @@ bool tv_check_str_or_nr(const typval_T *const tv) //{{{2 Get -/// Get the string value of a variable +/// Get the line number from VimL object +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string like ".", "$", … (works with current buffer +/// only). +/// +/// @return Line number or -1 or 0. +linenr_T tv_get_lnum(const typval_T *const tv) +{ + linenr_T lnum = get_tv_number_chk(tv, NULL); + if (lnum == 0) { // No valid number, try using same function as line() does. + int fnum; + pos_T *const fp = var2fpos(tv, true, &fnum); + if (fp != NULL) { + lnum = fp->lnum; + } + } + return lnum; +} + +/// Get the floating-point value of a VimL object +/// +/// Raises an error if object is not number or floating-point. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Floating-point value of the variable or zero. +float_T tv_get_float(const typval_T *const tv) +{ + switch (tv->v_type) { + case VAR_NUMBER: { + return (float_T)(tv->vval.v_number); + } + case VAR_FLOAT: { + return tv->vval.v_float; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E891: Using a Funcref as a Float")); + break; + } + case VAR_STRING: { + EMSG(_("E892: Using a String as a Float")); + break; + } + case VAR_LIST: { + EMSG(_("E893: Using a List as a Float")); + break; + } + case VAR_DICT: { + EMSG(_("E894: Using a Dictionary as a Float")); + break; + } + case VAR_SPECIAL: { + EMSG(_("E907: Using a special value as a Float")); + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + } + return 0; +} + +/// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use @@ -2027,38 +2093,38 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string(const typval_T *const varp) +const char *tv_get_string(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char mybuf[NUMBUFLEN]; - return tv_get_string_buf((typval_T *)varp, mybuf); + return tv_get_string_buf((typval_T *)tv, mybuf); } -/// Get the string value of a variable +/// Get the string value of a VimL object /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// @param buf Buffer used to hold numbers and special variables converted to /// string. When function encounters one of these stringified value /// will be written to buf and buf will be returned. /// /// Buffer must have NUMBUFLEN size. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string_buf(const typval_T *const varp, char *const buf) +const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)varp, (char_u *)buf); + (typval_T *)tv, (char_u *)buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 5645772124..3c294b45b8 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -14,6 +14,7 @@ #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T #include "nvim/gettext.h" +#include "nvim/message.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -373,7 +374,8 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; // FIXME circular dependency, cannot import message.h. @@ -381,11 +383,14 @@ bool emsgf(const char *const fmt, ...); /// Get the float value /// +/// Raises an error if object is not number or floating-point. +/// /// @param[in] tv VimL object to get value from. /// @param[out] ret_f Location where resulting float is stored. /// /// @return true in case of success, false if tv is not a number or float. -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) { if (tv->v_type == VAR_FLOAT) { *ret_f = tv->vval.v_float; -- cgit From 1b3e13da5be48739ac4291208e421b68e607cc0f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:18:29 +0300 Subject: eval: Refactor get_tv_lnum_buf --- src/nvim/eval.c | 50 +++++++++++++++++++++++++++----------------------- src/nvim/eval/typval.c | 2 ++ 2 files changed, 29 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6307dd8abc..2a7314d969 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9190,13 +9190,34 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli } } +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +static linenr_T tv_get_lnum_buf(const typval_T *const tv, + const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return get_tv_number_chk(tv, NULL); +} + /* * "getbufline()" function */ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - linenr_T end; buf_T *buf = NULL; if (tv_check_str_or_nr(&argvars[0])) { @@ -9205,12 +9226,10 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg_off--; } - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) { - end = lnum; - } else { - end = get_tv_lnum_buf(&argvars[2], buf); - } + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN + ? lnum + : tv_get_lnum_buf(&argvars[2], buf)); get_buffer_lines(buf, lnum, end, true, rettv); } @@ -18464,21 +18483,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return get_tv_number_chk(&argvars[0], NULL); -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6a16b3869a..38032762dc 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2026,6 +2026,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @return Line number or -1 or 0. linenr_T tv_get_lnum(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { linenr_T lnum = get_tv_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. @@ -2046,6 +2047,7 @@ linenr_T tv_get_lnum(const typval_T *const tv) /// /// @return Floating-point value of the variable or zero. float_T tv_get_float(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (tv->v_type) { case VAR_NUMBER: { -- cgit From 233b0c93bba66492d7b8b61f8ac61082f03668a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:55:58 +0300 Subject: eval: Move get_tv_number[_chk] to eval/typval.c --- src/nvim/eval.c | 659 ++++++++++++++++++++++------------------------- src/nvim/eval/executor.c | 8 +- src/nvim/eval/typval.c | 130 +++++++++- src/nvim/strings.c | 2 +- src/nvim/window.c | 20 +- 5 files changed, 446 insertions(+), 373 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2a7314d969..676df1a301 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -954,7 +954,7 @@ eval_to_bool ( } else { *error = false; if (!skip) { - retval = (get_tv_number_chk(&tv, error) != 0); + retval = (tv_get_number_chk(&tv, error) != 0); tv_clear(&tv); } } @@ -1081,10 +1081,10 @@ int eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == FAIL) + if (eval1(&p, &rettv, true) == FAIL) { retval = -1; - else { - retval = get_tv_number_chk(&rettv, NULL); + } else { + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); } --emsg_off; @@ -1174,9 +1174,10 @@ int get_spellword(list_T *list, const char **pp) *pp = tv_get_string(&li->li_tv); li = li->li_next; - if (li == NULL) + if (li == NULL) { return -1; - return get_tv_number(&li->li_tv); + } + return tv_get_number(&li->li_tv); } /* @@ -1284,7 +1285,7 @@ call_func_retnr ( if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) return -1; - retval = get_tv_number_chk(&rettv, NULL); + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); return retval; } @@ -1912,7 +1913,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) { EMSG(_(e_letunexp)); } else { - long n; int opt_type; long numval; char_u *stringval = NULL; @@ -1921,8 +1921,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char c1 = *p; *p = NUL; - n = get_tv_number(tv); - s = get_tv_string_chk(tv); /* != NULL if number or string */ + varnumber_T n = tv_get_number(tv); + s = get_tv_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { opt_type = get_option_value(arg, &numval, &stringval, opt_flags); if ((opt_type == 1 && *op == '.') @@ -2284,13 +2284,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_tv = &lp->ll_di->di_tv; } else { - /* - * Get the number and item for the only or first index of the List. - */ + // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = get_tv_number(&var1); // Is number or string. + lp->ll_n1 = tv_get_number(&var1); // Is number or string. tv_clear(&var1); } lp->ll_dict = NULL; @@ -2319,7 +2317,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); // Is number or string. + lp->ll_n2 = tv_get_number(&var2); // Is number or string. tv_clear(&var2); if (lp->ll_n2 < 0) { ni = tv_list_find(lp->ll_list, lp->ll_n2); @@ -3320,7 +3318,7 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3395,7 +3393,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3416,7 +3414,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) { + if (tv_get_number_chk(&var2, &error) != 0) { result = true; } tv_clear(&var2); @@ -3464,7 +3462,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) { + if (tv_get_number_chk(rettv, &error) == 0) { result = false; } tv_clear(rettv); @@ -3485,7 +3483,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) { + if (tv_get_number_chk(&var2, &error) == 0) { result = false; } tv_clear(&var2); @@ -3694,25 +3692,27 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) && type != TYPE_MATCH && type != TYPE_NOMATCH) { float_T f1, f2; - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; - else - f1 = get_tv_number(rettv); - if (var2.v_type == VAR_FLOAT) + } else { + f1 = tv_get_number(rettv); + } + if (var2.v_type == VAR_FLOAT) { f2 = var2.vval.v_float; - else - f2 = get_tv_number(&var2); - n1 = FALSE; + } else { + f2 = tv_get_number(&var2); + } + n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (f1 == f2); break; + case TYPE_NEQUAL: n1 = (f1 != f2); break; + case TYPE_GREATER: n1 = (f1 > f2); break; + case TYPE_GEQUAL: n1 = (f1 >= f2); break; + case TYPE_SMALLER: n1 = (f1 < f2); break; + case TYPE_SEQUAL: n1 = (f1 <= f2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } /* @@ -3721,18 +3721,18 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = get_tv_number(rettv); - n2 = get_tv_number(&var2); + n1 = tv_get_number(rettv); + n2 = tv_get_number(&var2); switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } else { char buf1[NUMBUFLEN]; @@ -3872,7 +3872,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f1 = rettv->vval.v_float; n1 = 0; } else { - n1 = get_tv_number_chk(rettv, &error); + n1 = tv_get_number_chk(rettv, &error); if (error) { /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned @@ -3887,7 +3887,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); if (error) { tv_clear(rettv); tv_clear(&var2); @@ -3968,8 +3968,9 @@ eval6 ( f1 = rettv->vval.v_float; use_float = TRUE; n1 = 0; - } else - n1 = get_tv_number_chk(rettv, &error); + } else { + n1 = tv_get_number_chk(rettv, &error); + } tv_clear(rettv); if (error) { return FAIL; @@ -3994,7 +3995,7 @@ eval6 ( f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); tv_clear(&var2); if (error) { return FAIL; @@ -4296,7 +4297,7 @@ static int eval7( if (rettv->v_type == VAR_FLOAT) { f = rettv->vval.v_float; } else { - val = get_tv_number_chk(rettv, &error); + val = tv_get_number_chk(rettv, &error); } if (error) { tv_clear(rettv); @@ -4457,14 +4458,14 @@ eval_index ( if (evaluate) { n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { - n1 = get_tv_number(&var1); + n1 = tv_get_number(&var1); tv_clear(&var1); } if (range) { if (empty2) { n2 = -1; } else { - n2 = get_tv_number(&var2); + n2 = tv_get_number(&var2); tv_clear(&var2); } } @@ -6543,13 +6544,14 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; bool error = false; - n = get_tv_number_chk(&argvars[0], &error); - if (error) + n = tv_get_number_chk(&argvars[0], &error); + if (error) { rettv->vval.v_number = -1; - else if (n > 0) + } else if (n > 0) { rettv->vval.v_number = n; - else + } else { rettv->vval.v_number = -n; + } } } @@ -6578,8 +6580,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); } @@ -6680,11 +6682,13 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) int idx; if (argvars[0].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else + idx = tv_get_number_chk(&argvars[0], NULL); + if (idx >= 0 && idx < ARGCOUNT) { + rettv->vval.v_string = (char_u *)xstrdup( + (const char *)alist_name(&ARGLIST[idx])); + } else { rettv->vval.v_string = NULL; + } rettv->v_type = VAR_STRING; } else { tv_list_alloc_ret(rettv); @@ -6855,9 +6859,9 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { bool error = false; - const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); if (error) { return; @@ -6884,7 +6888,7 @@ static void assert_bool(typval_T *argvars, bool is_true) garray_T ga; if ((argvars[0].v_type != VAR_NUMBER - || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true + || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true || error) && (argvars[0].v_type != VAR_SPECIAL || (argvars[0].vval.v_special @@ -7118,7 +7122,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // new buffer. if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 + && tv_get_number_chk(&argvars[1], &error) != 0 && !error && (name = get_tv_string_chk(&argvars[0])) != NULL && !error) { @@ -7176,7 +7180,7 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long boff = get_tv_number(&argvars[0]) - 1; + long boff = tv_get_number(&argvars[0]) - 1; if (boff < 0) { rettv->vval.v_number = -1; } else { @@ -7192,7 +7196,7 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) long idx; str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); + idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; if (str == NULL || idx < 0) return; @@ -7314,7 +7318,7 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int utf8 = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - utf8 = get_tv_number_chk(&argvars[1], NULL); + utf8 = tv_get_number_chk(&argvars[1], NULL); } rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( @@ -7392,8 +7396,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int startcol; - if ((State & INSERT) == 0) { EMSG(_("E785: complete() can only be used in Insert mode")); return; @@ -7409,9 +7411,10 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - startcol = get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) + const int startcol = tv_get_number_chk(&argvars[0], NULL); + if (startcol <= 0) { return; + } set_completion(startcol - 1, argvars[1].vval.v_list); } @@ -7459,7 +7462,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buttons == NULL) error = TRUE; if (argvars[2].v_type != VAR_UNKNOWN) { - def = get_tv_number_chk(&argvars[2], &error); + def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = get_tv_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) @@ -7511,9 +7514,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - ic = get_tv_number_chk(&argvars[2], &error); + ic = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[3], &error); + idx = tv_get_number_chk(&argvars[3], &error); if (!error) { li = tv_list_find(l, idx); if (li == NULL) { @@ -7538,9 +7541,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + ic = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { EMSG(_(e_invarg)); + } } todo = error ? 0 : (int)d->dv_hashtab.ht_used; @@ -7572,7 +7576,7 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)get_tv_number(&argvars[0]); + num = (int)tv_get_number(&argvars[0]); dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { prepend = tv_get_string_buf(&argvars[2], buf); @@ -7614,9 +7618,9 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else { line = tv_get_lnum(argvars); - col = get_tv_number_chk(&argvars[1], NULL); + col = tv_get_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { - coladd = get_tv_number_chk(&argvars[2], NULL); + coladd = tv_get_number_chk(&argvars[2], NULL); } } if (line < 0 || col < 0 @@ -7649,10 +7653,11 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int noref = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - noref = get_tv_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + noref = tv_get_number_chk(&argvars[1], NULL); + } if (noref < 0 || noref > 1) { - EMSG(_(e_invarg)); + emsgf(_(e_invarg)); } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() @@ -7851,11 +7856,12 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ + col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + if (col >= change_start && col <= change_end) { + hlID = HLF_TXD; // Changed text. + } else { + hlID = HLF_CHD; // Changed line. + } } rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; } @@ -8123,7 +8129,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) + && tv_get_number_chk(&argvars[2], &error) && !error) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; @@ -8145,8 +8151,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ if (argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error)) + && tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; @@ -8191,9 +8198,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + before = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } if (before == l1->lv_len) { item = NULL; @@ -8315,7 +8323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) path = p; if (argvars[2].v_type != VAR_UNKNOWN) { - count = get_tv_number_chk(&argvars[2], &error); + count = tv_get_number_chk(&argvars[2], &error); } } } @@ -8512,10 +8520,10 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) bool error = false; // filter(): when expr is zero remove the item - *remp = (get_tv_number_chk(&rettv, &error) == 0); + *remp = (tv_get_number_chk(&rettv, &error) == 0); tv_clear(&rettv); // On type error, nothing has been removed; return FAIL to stop the - // loop. The error message was given by get_tv_number_chk(). + // loop. The error message was given by tv_get_number_chk(). if (error) { goto theend; } @@ -8946,12 +8954,13 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "garbagecollect()" function static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - /* This is postponed until we are back at the toplevel, because we may be - * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */ - want_garbage_collect = TRUE; + // This is postponed until we are back at the toplevel, because we may be + // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". + want_garbage_collect = true; - if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1) - garbage_collect_at_exit = TRUE; + if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) { + garbage_collect_at_exit = true; + } } /* @@ -8969,7 +8978,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = &li->li_tv; } @@ -9110,23 +9119,24 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; di = tv_dict_find(sel_d, S_LEN("buflisted")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_buflisted = true; } di = tv_dict_find(sel_d, S_LEN("bufloaded")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_bufloaded = true; } } } else if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one buffer. Argument specifies the buffer - (void)get_tv_number(&argvars[0]); // issue errmsg if type error - emsg_off++; - argbuf = get_buf_tv(&argvars[0], false); - emsg_off--; - if (argbuf == NULL) { - return; + if (tv_check_num(&argvars[0])) { // issue errmsg if type error + emsg_off++; + argbuf = get_buf_tv(&argvars[0], false); + emsg_off--; + if (argbuf == NULL) { + return; + } } } @@ -9210,7 +9220,7 @@ static linenr_T tv_get_lnum_buf(const typval_T *const tv, && buf != NULL) { return buf->b_ml.ml_line_count; } - return get_tv_number_chk(tv, NULL); + return tv_get_number_chk(tv, NULL); } /* @@ -9321,7 +9331,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } n = safe_vgetc(); - } else if (get_tv_number_chk(&argvars[0], &error) == 1) { + } else if (tv_get_number_chk(&argvars[0], &error) == 1) { // getchar(1): only check if char avail n = vpeekc_any(); } else if (error || vpeekc_any() == NUL) { @@ -9332,8 +9342,9 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = safe_vgetc(); } - if (n == K_IGNORE) + if (n == K_IGNORE) { continue; + } break; } no_mapping--; @@ -9460,7 +9471,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) | WILD_NO_BEEP; if (argvars[2].v_type != VAR_UNKNOWN) { - filtered = get_tv_number_chk(&argvars[2], NULL); + filtered = (bool)tv_get_number_chk(&argvars[2], NULL); } if (p_wic) { @@ -9970,9 +9981,9 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) strregname = get_tv_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - arg2 = get_tv_number_chk(&argvars[1], &error); + arg2 = tv_get_number_chk(&argvars[1], &error); if (!error && argvars[2].v_type != VAR_UNKNOWN) { - return_list = get_tv_number_chk(&argvars[2], &error); + return_list = tv_get_number_chk(&argvars[2], &error); } } } else { @@ -10062,7 +10073,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page - tparg = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tparg == NULL) { return; } @@ -10099,7 +10110,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the // first window in the tabpage, otherwise the window is not valid. @@ -10216,7 +10227,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = (int)get_tv_number_chk(vp, NULL); + int nr = (int)tv_get_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -10251,7 +10262,7 @@ static win_T *find_tabwin(typval_T *wvp, typval_T *tvp) if (wvp->v_type != VAR_UNKNOWN) { if (tvp->v_type != VAR_UNKNOWN) { - long n = get_tv_number(tvp); + long n = tv_get_number(tvp); if (n >= 0) { tp = find_tabpage(n); } @@ -10291,10 +10302,11 @@ getwinvar ( tabpage_T *oldtabpage = NULL; bool done = false; - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { tp = curtab; + } win = find_win_by_nr(&argvars[off], tp); const char *varname = (const char *)get_tv_string_chk( &argvars[off + 1]); @@ -10363,15 +10375,16 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) * for 'wildignore' and don't put matches for 'suffixes' at the end. */ rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[1], &error)) + if (tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (argvars[2].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) { + && tv_get_number_chk(&argvars[3], &error)) { options |= WILD_ALLLINKS; } } @@ -10410,17 +10423,17 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { // When the optional second argument is non-zero, don't remove matches // for 'wildignore' and don't put matches for 'suffixes' at the end. - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { flags |= WILD_KEEP_ALL; } if (argvars[3].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[3], &error)) { + if (tv_get_number_chk(&argvars[3], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[4].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[4], &error)) { + && tv_get_number_chk(&argvars[4], &error)) { flags |= WILD_ALLLINKS; } } @@ -10763,7 +10776,7 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); + abbr = tv_get_number(&argvars[2]); } } @@ -10816,7 +10829,7 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int)get_tv_number(&argvars[1])); + (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries char buf[NUMBUFLEN]; @@ -10843,7 +10856,7 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { - idx = (int)get_tv_number_chk(&argvars[1], NULL); + idx = (int)tv_get_number_chk(&argvars[1], NULL); } // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); @@ -10968,12 +10981,14 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Start at specified item. Use the cached index that tv_list_find() // sets, so that a negative number also works. - item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); + item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = tv_get_number_chk(&argvars[3], &error); + } + if (error) { item = NULL; + } } for (; item != NULL; item = item->li_next, ++idx) @@ -11186,7 +11201,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); + before = tv_get_number_chk(&argvars[2], &error); } if (error) { // type error; errmsg already given @@ -11213,7 +11228,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); + rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } /* @@ -12051,9 +12066,10 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = get_tv_number(&argvars[3]); + abbr = tv_get_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + get_dict = tv_get_number(&argvars[3]); + } } } else which = (char_u *)""; @@ -12175,9 +12191,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start = get_tv_number_chk(&argvars[2], &error); - if (error) + start = tv_get_number_chk(&argvars[2], &error); + if (error) { goto theend; + } if (l != NULL) { li = tv_list_find(l, start); if (li == NULL) { @@ -12200,10 +12217,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - if (argvars[3].v_type != VAR_UNKNOWN) - nth = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + nth = tv_get_number_chk(&argvars[3], &error); + } + if (error) { goto theend; + } } regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); @@ -12331,9 +12350,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (grp == NULL || pat == NULL) return; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12387,9 +12406,9 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12424,7 +12443,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv); - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); if (id >= 1 && id <= 3) { matchitem_T *m; @@ -12446,7 +12465,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); + (int)tv_get_number(&argvars[0]), true); } /* @@ -12493,14 +12512,16 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) if (l != NULL) { li = l->lv_first; if (li != NULL) { - n = get_tv_number_chk(&li->li_tv, &error); + n = tv_get_number_chk(&li->li_tv, &error); for (;; ) { li = li->li_next; - if (li == NULL) + if (li == NULL) { break; - i = get_tv_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) + } + i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { n = i; + } } } } @@ -12516,7 +12537,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); + i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -12570,7 +12591,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = get_tv_number_chk(&argvars[2], NULL); + prot = tv_get_number_chk(&argvars[2], NULL); } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; @@ -12782,14 +12803,16 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + if (argvars[1].v_type != VAR_UNKNOWN) { + utf8 = tv_get_number_chk(&argvars[1], NULL); + } + if (utf8) { + buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } else { + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } } else { - buf[0] = (char_u)get_tv_number(&argvars[0]); + buf[0] = (char_u)tv_get_number(&argvars[0]); buf[1] = NUL; } rettv->v_type = VAR_STRING; @@ -12801,8 +12824,8 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + | tv_get_number_chk(&argvars[1], NULL); } /* @@ -12911,14 +12934,15 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) long i; bool error = false; - start = get_tv_number_chk(&argvars[0], &error); + start = tv_get_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { end = start - 1; start = 0; } else { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) { + stride = tv_get_number_chk(&argvars[2], &error); + } } if (error) { @@ -12959,7 +12983,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) binary = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - maxline = get_tv_number(&argvars[2]); + maxline = tv_get_number(&argvars[2]); } } @@ -13239,7 +13263,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { bool error = false; - idx = get_tv_number_chk(&argvars[1], &error); + idx = tv_get_number_chk(&argvars[1], &error); if (error) { // Type error: do nothing, errmsg already given. } else if ((item = tv_list_find(l, idx)) == NULL) { @@ -13251,8 +13275,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) *rettv = item->li_tv; xfree(item); } else { - /* Remove range of items, return list with values. */ - end = get_tv_number_chk(&argvars[2], &error); + // Remove range of items, return list with values. + end = tv_get_number_chk(&argvars[2], &error); if (error) { // Type error: do nothing. } else if ((item2 = tv_list_find(l, end)) == NULL) { @@ -13302,7 +13326,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - varnumber_T n = get_tv_number(&argvars[1]); + varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); while (n-- > 0) { @@ -13621,13 +13645,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[3].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) { goto theend; + } } } @@ -13889,17 +13915,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else + } else { c = ScreenAttrs[LineOffset[row] + col]; + } rettv->vval.v_number = c; } @@ -13908,17 +13933,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int off; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else { + } else { off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; @@ -13969,9 +13992,10 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *name = get_tv_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - locally = get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = get_tv_number_chk(&argvars[2], &error) != 0; + locally = tv_get_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) { + thisblock = tv_get_number_chk(&argvars[2], &error) != 0; + } } if (!error && name != NULL) rettv->vval.v_number = find_decl(name, STRLEN(name), locally, @@ -14027,13 +14051,15 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) else { skip = get_tv_string_buf_chk(&argvars[4], nbuf3); if (argvars[5].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[6].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) { goto theend; + } } } } @@ -14337,7 +14363,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) aucmd_prepbuf(&aco, buf); varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); @@ -14385,12 +14411,12 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) di = tv_dict_find(d, S_LEN("forward")); if (di != NULL) { - set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); } di = tv_dict_find(d, S_LEN("until")); if (di != NULL) { - set_csearch_until(!!get_tv_number(&di->di_tv)); + set_csearch_until(!!tv_get_number(&di->di_tv)); } } } @@ -14400,10 +14426,11 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int pos = (int)get_tv_number(&argvars[0]) - 1; + const int pos = (int)tv_get_number(&argvars[0]) - 1; - if (pos >= 0) + if (pos >= 0) { rettv->vval.v_number = set_cmdline_pos(pos); + } } @@ -14854,7 +14881,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; @@ -14904,7 +14931,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tabpage_T *tp = NULL; if (off == 1) { - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); } else { tp = curtab; } @@ -14923,7 +14950,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) bool error = false; varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char_u nbuf[NUMBUFLEN]; char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { @@ -15024,8 +15051,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) typval_T *tv2 = &si2->item->li_tv; if (sortinfo->item_compare_numbers) { - long v1 = get_tv_number(tv1); - long v2 = get_tv_number(tv2); + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15138,7 +15165,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (res == FAIL) { res = ITEM_COMPARE_FAIL; } else { - res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type @@ -15220,7 +15247,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { bool error = false; - i = get_tv_number_chk(&argvars[1], &error); + i = tv_get_number_chk(&argvars[1], &error); if (error) { goto theend; // type error; errmsg already given } @@ -15443,13 +15470,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) + maxcount = tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { - need_capital = get_tv_number_chk(&argvars[2], &typeerr); - if (typeerr) + need_capital = tv_get_number_chk(&argvars[2], &typeerr); + if (typeerr) { return; + } } } else maxcount = 25; @@ -15491,7 +15520,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) typeerr = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } if (pat == NULL || *pat == NUL) @@ -15563,7 +15592,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what; if (argvars[1].v_type != VAR_UNKNOWN) { - base = get_tv_number(&argvars[1]); + base = tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; @@ -15608,7 +15637,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); } else { - seconds = (time_t)get_tv_number(&argvars[1]); + seconds = (time_t)tv_get_number(&argvars[1]); } struct tm curtime; @@ -15658,9 +15687,8 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (str == NULL) { return; } - bool error = false; - varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); + varnumber_T charidx = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15699,11 +15727,13 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) + start_idx = tv_get_number_chk(&argvars[2], &error); + if (error || start_idx >= (int)STRLEN(haystack)) { return; - if (start_idx >= 0) + } + if (start_idx >= 0) { haystack += start_idx; + } } pos = (char_u *)strstr((char *)haystack, (char *)needle); @@ -15739,7 +15769,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = get_tv_number_chk(&argvars[1], NULL); + skipcc = tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { EMSG(_(e_invarg)); @@ -15762,7 +15792,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = get_tv_number(&argvars[1]); + col = tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -15786,7 +15816,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int nbyte = 0; bool error = false; - varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); + varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { @@ -15799,7 +15829,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = get_tv_number(&argvars[2]); + int charlen = tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -15842,12 +15872,12 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const p = tv_get_string(&argvars[0]); const size_t slen = strlen(p); - varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T n = tv_get_number_chk(&argvars[1], &error); varnumber_T len; if (error) { len = 0; } else if (argvars[2].v_type != VAR_UNKNOWN) { - len = get_tv_number(&argvars[2]); + len = tv_get_number(&argvars[2]); } else { len = slen - n; // Default len: all bytes that are available. } @@ -15891,12 +15921,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) haystack_len = (int)STRLEN(haystack); if (argvars[2].v_type != VAR_UNKNOWN) { - /* Third argument: upper limit for index */ - end_idx = get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } else + // Third argument: upper limit for index. + end_idx = tv_get_number_chk(&argvars[2], NULL); + if (end_idx < 0) { + return; // Can never find a match. + } + } else { end_idx = haystack_len; + } if (*needle == NUL) { /* Empty string matches past the end. */ @@ -15931,7 +15963,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - int no = (int)get_tv_number_chk(&argvars[0], &error); + int no = (int)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -15943,7 +15975,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = get_tv_number_chk(&argvars[1], &error); + retList = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15993,10 +16025,10 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = get_tv_number_chk(&argvars[2], &transerr); + const int trans = tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16012,7 +16044,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const int id = get_tv_number(&argvars[0]); + const int id = (int)tv_get_number(&argvars[0]); const char *const what = tv_get_string(&argvars[1]); int modec; if (argvars[2].v_type != VAR_UNKNOWN) { @@ -16086,14 +16118,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id; + int id = tv_get_number(&argvars[0]); - id = get_tv_number(&argvars[0]); - - if (id > 0) + if (id > 0) { id = syn_get_final_id(id); - else + } else { id = 0; + } rettv->vval.v_number = id; } @@ -16113,7 +16144,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); @@ -16154,7 +16185,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16232,7 +16263,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (retlist) { int keepempty = 0; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - keepempty = get_tv_number(&argvars[2]); + keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; @@ -16277,15 +16308,15 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *tp; win_T *wp = NULL; - if (argvars[0].v_type == VAR_UNKNOWN) + if (argvars[0].v_type == VAR_UNKNOWN) { wp = firstwin; - else { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) + } else { + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) { wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } } if (wp != NULL) { tv_list_alloc_ret(rettv); @@ -16367,13 +16398,12 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { nr = 0; - else + } else { nr = get_winnr(tp, &argvars[1]); + } rettv->vval.v_number = nr; } @@ -16648,7 +16678,7 @@ static bool set_ref_in_callback(Callback *callback, int copyID, /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long timeout = get_tv_number(&argvars[0]); + const long timeout = tv_get_number(&argvars[0]); timer_T *timer; int repeat = 1; dict_T *dict; @@ -16663,7 +16693,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = get_tv_number(&di->di_tv); + repeat = tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -16703,7 +16733,7 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer == NULL) { return; @@ -17177,29 +17207,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = get_tv_number(&di->di_tv); + curwin->w_cursor.lnum = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = get_tv_number(&di->di_tv); + curwin->w_cursor.col = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = get_tv_number(&di->di_tv); + curwin->w_cursor.coladd = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = get_tv_number(&di->di_tv); + curwin->w_curswant = tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, get_tv_number(&di->di_tv)); + set_topline(curwin, tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = get_tv_number(&di->di_tv); + curwin->w_topfill = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = get_tv_number(&di->di_tv); + curwin->w_leftcol = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = get_tv_number(&di->di_tv); + curwin->w_skipcol = tv_get_number(&di->di_tv); } check_cursor(); @@ -17462,8 +17492,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); } @@ -18408,83 +18438,6 @@ void free_tv(typval_T *varp) // TODO(ZyX-I): move to eval/typval -/// Get the number value of a variable -/// -/// @note Use get_tv_number_chk() if you need to determine whether there was an -/// error. -/// -/// @param[in] varp Variable to get value from. -/// -/// @return Number value: vim_str2nr() output for VAR_STRING variables, value -/// for VAR_NUMBER variables, -1 for other types. -varnumber_T get_tv_number(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - bool error = false; - return get_tv_number_chk(varp, &error); -} - -varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) -{ - varnumber_T n = 0; - - switch (varp->v_type) { - case VAR_NUMBER: { - return varp->vval.v_number; - } - case VAR_FLOAT: { - EMSG(_("E805: Using a Float as a Number")); - break; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Using a Funcref as a Number")); - break; - } - case VAR_STRING: { - if (varp->vval.v_string != NULL) { - long nr; - vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); - n = (varnumber_T)nr; - } - return n; - } - case VAR_LIST: { - EMSG(_("E745: Using a List as a Number")); - break; - } - case VAR_DICT: { - EMSG(_("E728: Using a Dictionary as a Number")); - break; - } - case VAR_SPECIAL: { - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; - } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; - } - } - break; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; - } - } - if (denote == NULL) { - // useful for values that must be unsigned - n = -1; - } else { - *denote = true; - } - return n; -} - -// TODO(ZyX-I): move to eval/typval - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT @@ -18981,7 +18934,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } return; } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = get_tv_number(tv); + v->di_tv.vval.v_number = tv_get_number(tv); if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); } else if (strcmp(varname, "hlsearch") == 0) { diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index d2d0873792..ec6c86ac64 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -50,7 +50,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } if (*op == '+' || *op == '-') { // nr += nr or nr -= nr - varnumber_T n = get_tv_number(tv1); + varnumber_T n = tv_get_number(tv1); if (tv2->v_type == VAR_FLOAT) { float_T f = n; @@ -64,9 +64,9 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, tv1->vval.v_float = f; } else { if (*op == '+') { - n += get_tv_number(tv2); + n += tv_get_number(tv2); } else { - n -= get_tv_number(tv2); + n -= tv_get_number(tv2); } tv_clear(tv1); tv1->v_type = VAR_NUMBER; @@ -96,7 +96,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } const float_T f = (tv2->v_type == VAR_FLOAT ? tv2->vval.v_float - : get_tv_number(tv2)); + : tv_get_number(tv2)); if (*op == '+') { tv1->vval.v_float += f; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 38032762dc..087e76de10 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -17,6 +17,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/pos.h" +#include "nvim/charset.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -725,7 +726,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) } return -1; } - return get_tv_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(&li->li_tv, ret_error); } /// Get list item l[n - 1] as a string @@ -1087,7 +1088,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) if (di == NULL) { return 0; } - return get_tv_number(&di->di_tv); + return tv_get_number(&di->di_tv); } /// Get a string item from a dictionary @@ -1971,7 +1972,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// Check that given value is a number or string /// -/// Error messages are compatible with get_tv_number() previously used for the +/// Error messages are compatible with tv_get_number() previously used for the /// same purpose in buf*() functions. Special values are not accepted (previous /// behaviour: silently fail to find buffer). /// @@ -2016,8 +2017,127 @@ bool tv_check_str_or_nr(const typval_T *const tv) return false; } +#define FUNC_ERROR "E703: Using a Funcref as a Number" + +static const char *const num_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E745: Using a List as a Number"), + [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), + [VAR_FLOAT]=N_("E805: Using a Float as a Number"), + [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a number or can be converted to it +/// +/// Error messages are compatible with tv_get_number() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_num(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(num_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get +/// Get the number value of a VimL object +/// +/// @note Use tv_get_number_chk() if you need to determine whether there was an +/// error. +/// +/// @param[in] tv Object to get value from. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 for other types. +varnumber_T tv_get_number(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool error = false; + return tv_get_number_chk(tv, &error); +} + +/// Get the number value of a VimL object +/// +/// @param[in] tv Object to get value from. +/// @param[out] ret_error If type error occurred then `true` will be written +/// to this location. Otherwise it is not touched. +/// +/// @note Needs to be initialized to `false` to be +/// useful. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 (ret_error == NULL) or 0 (otherwise) for +/// other types. +varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + switch (tv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: { + EMSG(_(num_errors[tv->v_type])); + break; + } + case VAR_NUMBER: { + return tv->vval.v_number; + } + case VAR_STRING: { + varnumber_T n = 0; + if (tv->vval.v_string != NULL) { + long nr; + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; + } + return n; + } + case VAR_SPECIAL: { + switch (tv->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + break; + } + } + if (ret_error != NULL) { + *ret_error = true; + } + return (ret_error == NULL ? -1 : 0); +} + /// Get the line number from VimL object /// /// @param[in] tv Object to get value from. Is expected to be a number or @@ -2028,7 +2148,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) linenr_T tv_get_lnum(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - linenr_T lnum = get_tv_number_chk(tv, NULL); + linenr_T lnum = tv_get_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. int fnum; pos_T *const fp = var2fpos(tv, true, &fnum); @@ -2078,7 +2198,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float()"); + EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 9cfa126a56..e20979c307 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -576,7 +576,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) } else { (*idxp)++; bool err = false; - n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); + n = tv_get_number_chk(&tvs[idx], &err); if (err) { n = 0; } diff --git a/src/nvim/window.c b/src/nvim/window.c index a3b0e6fc2d..6020159af9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5593,7 +5593,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, if (subli == NULL) { goto fail; } - lnum = get_tv_number_chk(&subli->li_tv, &error); + lnum = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5604,13 +5604,13 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, m->pos.pos[i].lnum = lnum; subli = subli->li_next; if (subli != NULL) { - col = get_tv_number_chk(&subli->li_tv, &error); + col = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } subli = subli->li_next; if (subli != NULL) { - len = get_tv_number_chk(&subli->li_tv, &error); + len = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5810,14 +5810,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = get_tv_number(&argvars[0]); + int winnr = tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = get_tv_number(&argvars[1]); + int tabnr = tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -5844,7 +5844,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5879,7 +5879,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) { int winnr = 1; int tabnr = 1; - int id = get_tv_number(&argvars[0]); + handle_T id = (handle_T)tv_get_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); tv_list_append_number(list, tabnr); @@ -5888,7 +5888,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) win_T * win_id2wp(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5902,7 +5902,7 @@ win_T * win_id2wp(typval_T *argvars) int win_id2win(typval_T *argvars) { int nr = 1; - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -5915,7 +5915,7 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = get_tv_number(&argvars[0]); + int bufnr = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { -- cgit From 50ebd1dff5c4e995c4f7e7980870e43d9defabc6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 09:15:28 +0300 Subject: eval: Move free_tv to eval/typval.h, remove most of its usages --- src/nvim/api/vim.c | 11 ++++++----- src/nvim/eval.c | 53 ++++++-------------------------------------------- src/nvim/eval/typval.c | 40 +++++++++++++++++++++++++++++++++++++ src/nvim/ex_eval.c | 15 +++++++------- src/nvim/quickfix.c | 13 ++++++------- 5 files changed, 66 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index db2f25a2a6..975446057c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -182,19 +182,20 @@ Object nvim_eval(String expr, Error *err) Object rv = OBJECT_INIT; // Evaluate the expression try_start(); - typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); - if (!expr_result) { + typval_T rettv; + if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) { api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { // No errors, convert the result - rv = vim_to_object(expr_result); + rv = vim_to_object(&rettv); } - // Free the vim object - free_tv(expr_result); + // Free the Vim object + tv_clear(&rettv); + return rv; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 676df1a301..65bb90fc15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -719,13 +719,12 @@ int current_func_returned(void) */ void set_internal_string_var(char_u *name, char_u *value) { - char_u *val = vim_strsave(value); - typval_T *tvp = xcalloc(1, sizeof(typval_T)); + const typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; - tvp->v_type = VAR_STRING; - tvp->vval.v_string = val; - set_var((const char *)name, tvp, false); - free_tv(tvp); + set_var((const char *)name, (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -3264,7 +3263,7 @@ typedef enum { * Note: "rettv.v_lock" is not set. * Return OK or FAIL. */ -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; @@ -18404,38 +18403,6 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -/* - * Free the memory for a variable type-value. - */ -void free_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - // FALLTHROUGH - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_PARTIAL: - partial_unref(varp->vval.v_partial); - break; - case VAR_LIST: - tv_list_unref(varp->vval.v_list); - break; - case VAR_DICT: - tv_dict_unref(varp->vval.v_dict); - break; - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(varp); - } -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! @@ -21525,14 +21492,6 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) return idx < 0; } -/* - * Free the variable with a pending return value. - */ -void discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - /* * Generate a return command for producing the value of "rettv". The result * is an allocated string. Used by report_pending() for verbose messages. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 087e76de10..ca635dcae9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1742,6 +1742,46 @@ void tv_clear(typval_T *tv) } } +//{{{3 Free + +/// Free allocated VimL object and value stored inside +/// +/// @param tv Object to free. +void tv_free(typval_T *tv) +{ + if (tv != NULL) { + switch (tv->v_type) { + case VAR_PARTIAL: { + partial_unref(tv->vval.v_partial); + break; + } + case VAR_FUNC: { + func_unref(tv->vval.v_string); + // FALLTHROUGH + } + case VAR_STRING: { + xfree(tv->vval.v_string); + break; + } + case VAR_LIST: { + tv_list_unref(tv->vval.v_list); + break; + } + case VAR_DICT: { + tv_dict_unref(tv->vval.v_dict); + break; + } + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: { + break; + } + } + xfree(tv); + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 3f71ae1795..65112c4dd8 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -14,6 +14,7 @@ #include "nvim/ex_eval.h" #include "nvim/charset.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/message.h" @@ -21,8 +22,6 @@ #include "nvim/regexp.h" #include "nvim/strings.h" - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.c.generated.h" #endif @@ -59,12 +58,14 @@ * is an error exception.) - The macros can be defined as expressions checking * for a variable that is allowed to be changed during execution of a script. */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE +// Values used for the Vim release. +#define THROW_ON_ERROR true +#define THROW_ON_ERROR_TRUE +#define THROW_ON_INTERRUPT true +#define THROW_ON_INTERRUPT_TRUE + +#define discard_pending_return(p) tv_free((typval_T *)(p)) /* * When several errors appear in a row, setting "force_abort" is delayed until diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 06ac2821b0..4fa5c85abd 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4372,7 +4372,6 @@ void ex_cbuffer(exarg_T *eap) */ void ex_cexpr(exarg_T *eap) { - typval_T *tv; qf_info_T *qi = &ql_info; const char *au_name = NULL; @@ -4412,11 +4411,11 @@ void ex_cexpr(exarg_T *eap) /* Evaluate the expression. When the result is a string or a list we can * use it to fill the errorlist. */ - tv = eval_expr(eap->arg, NULL); - if (tv != NULL) { - if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) - || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL)) { - if (qf_init_ext(qi, NULL, NULL, tv, p_efm, + typval_T tv; + if (eval0(eap->arg, &tv, NULL, true) != FAIL) { + if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) + || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { @@ -4431,7 +4430,7 @@ void ex_cexpr(exarg_T *eap) } else { EMSG(_("E777: String or List expected")); } - free_tv(tv); + tv_clear(&tv); } } -- cgit From c8e63a8db84e9d9f7bd855085a87d93631504fc7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:25:24 +0300 Subject: eval: Move remaining get_tv_string* functions to eval/typval.c --- src/nvim/ascii.h | 8 +- src/nvim/charset.c | 8 +- src/nvim/edit.c | 204 ++++---- src/nvim/eval.c | 1280 +++++++++++++++++++++++------------------------- src/nvim/eval/typval.c | 118 ++++- src/nvim/eval/typval.h | 1 + src/nvim/ex_cmds.c | 4 +- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 31 +- src/nvim/fileio.c | 37 +- src/nvim/getchar.c | 159 +++--- src/nvim/globals.h | 1 + src/nvim/mbyte.c | 4 +- src/nvim/normal.c | 71 +-- src/nvim/ops.c | 60 +-- src/nvim/option.c | 19 +- src/nvim/os/fs.c | 4 +- src/nvim/regexp.c | 15 +- src/nvim/screen.c | 2 +- src/nvim/spell.c | 82 ++-- src/nvim/spellfile.c | 54 +- src/nvim/strings.c | 11 +- src/nvim/undo.c | 4 +- 23 files changed, 1142 insertions(+), 1037 deletions(-) (limited to 'src') diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 31851a84e6..37b83efb61 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -20,15 +20,13 @@ #define BS '\010' #define TAB '\011' #define NL '\012' -#define NL_STR (char_u *)"\012" +#define NL_STR "\012" #define FF '\014' #define CAR '\015' /* CR is used by Mac OS X */ #define ESC '\033' -#define ESC_STR (char_u *)"\033" -#define ESC_STR_nc "\033" +#define ESC_STR "\033" #define DEL 0x7f -#define DEL_STR (char_u *)"\177" -#define CSI 0x9b /* Control Sequence Introducer */ +#define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ #define STERM 0x9c /* String Terminator */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index cb6a9fa43c..6c22108853 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -90,7 +90,6 @@ int buf_init_chartab(buf_T *buf, int global) { int c; int c2; - char_u *p; int i; bool tilde; bool do_isalpha; @@ -144,7 +143,8 @@ int buf_init_chartab(buf_T *buf, int global) // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' // options Each option is a list of characters, character numbers or // ranges, separated by commas, e.g.: "200-210,x,#-178,-" - for (i = global ? 0 : 3; i <= 3; ++i) { + for (i = global ? 0 : 3; i <= 3; i++) { + const char_u *p; if (i == 0) { // first round: 'isident' p = p_isi; @@ -169,7 +169,7 @@ int buf_init_chartab(buf_T *buf, int global) } if (ascii_isdigit(*p)) { - c = getdigits_int(&p); + c = getdigits_int((char_u **)&p); } else { c = mb_ptr2char_adv(&p); } @@ -179,7 +179,7 @@ int buf_init_chartab(buf_T *buf, int global) ++p; if (ascii_isdigit(*p)) { - c2 = getdigits_int(&p); + c2 = getdigits_int((char_u **)&p); } else { c2 = mb_ptr2char_adv(&p); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b396251051..da09aed3dc 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1423,7 +1423,7 @@ static void ins_ctrl_v(void) edit_putchar('^', TRUE); did_putchar = TRUE; } - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + AppendToRedobuff(CTRL_V_STR); add_to_showcmd_c(Ctrl_V); @@ -1977,7 +1977,6 @@ static bool ins_compl_accept_char(int c) */ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) { - char_u *p; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -1987,11 +1986,11 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int int was_letter = FALSE; if (p_ic && curbuf->b_p_inf && len > 0) { - /* Infer case of completed part. */ + // Infer case of completed part. - /* Find actual length of completion. */ + // Find actual length of completion. if (has_mbyte) { - p = str; + const char_u *p = str; actual_len = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2002,7 +2001,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Find actual length of original text. */ if (has_mbyte) { - p = compl_orig_text; + const char_u *p = compl_orig_text; actual_compl_length = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2018,27 +2017,35 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Allocate wide character array for the completion and fill it. */ wca = xmalloc(actual_len * sizeof(*wca)); - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + if (has_mbyte) { + wca[i] = mb_ptr2char_adv(&p); + } else { + wca[i] = *(p++); + } + } + } - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) { - has_lower = TRUE; - if (vim_isupper(wca[i])) { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_tolower(wca[i]); - break; + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + has_lower = true; + if (vim_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = vim_tolower(wca[i]); + } + break; + } } } } @@ -2048,49 +2055,57 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int * upper case. */ if (!has_lower) { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { c = mb_ptr2char_adv(&p); - else + } else { c = *(p++); + } if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { wca[i] = vim_toupper(wca[i]); + } break; } was_letter = vim_islower(c) || vim_isupper(c); } } - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) - wca[i] = vim_tolower(wca[i]); - else if (vim_isupper(c)) - wca[i] = vim_toupper(wca[i]); + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + wca[i] = vim_tolower(wca[i]); + } else if (vim_isupper(c)) { + wca[i] = vim_toupper(wca[i]); + } + } } - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + if (has_mbyte) { + p += (*mb_char2bytes)(wca[i++], p); + } else { + *(p++) = wca[i++]; + } + } + *p = NUL; + } xfree(wca); @@ -3594,7 +3609,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = (const char *)get_tv_string_chk(tv); + word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } if (word == NULL || (!aempty && *word == NUL)) { @@ -5785,15 +5800,16 @@ comp_textwidth ( */ static void redo_literal(int c) { - char_u buf[10]; + char buf[10]; - /* Only digits need special treatment. Translate them into a string of - * three digits. */ + // Only digits need special treatment. Translate them into a string of + // three digits. if (ascii_isdigit(c)) { - vim_snprintf((char *)buf, sizeof(buf), "%03d", c); + vim_snprintf(buf, sizeof(buf), "%03d", c); AppendToRedobuff(buf); - } else + } else { AppendCharToRedobuff(c); + } } // start_arrow() is called when an arrow key is used in insert mode. @@ -5822,8 +5838,8 @@ static void start_arrow_common(pos_T *end_insert_pos, bool end_change) { if (!arrow_used && end_change) { // something has been inserted AppendToRedobuff(ESC_STR); - stop_insert(end_insert_pos, FALSE, FALSE); - arrow_used = TRUE; /* this means we stopped the current insert */ + stop_insert(end_insert_pos, false, false); + arrow_used = true; // This means we stopped the current insert. } check_spell_redraw(); } @@ -5880,7 +5896,7 @@ int stop_arrow(void) vr_lines_changed = 1; } ResetRedobuff(); - AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ + AppendToRedobuff("1i"); // Pretend we start an insertion. new_insert_skip = 2; } else if (ins_need_undo) { if (u_save_cursor() == OK) @@ -6345,12 +6361,13 @@ stuff_inserted ( } do { - stuffReadbuff(ptr); - /* a trailing "0" is inserted as "048", "^" as "^" */ - if (last) - stuffReadbuff((char_u *)(last == '0' - ? "\026\060\064\070" - : "\026^")); + stuffReadbuff((const char *)ptr); + // A trailing "0" is inserted as "048", "^" as "^". + if (last) { + stuffReadbuff((last == '0' + ? "\026\060\064\070" + : "\026^")); + } } while (--count > 0); if (last) @@ -7143,13 +7160,12 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) disabled_redraw = false; } if (!arrow_used) { - /* - * Don't append the ESC for "r" and "grx". - * When 'insertmode' is set only CTRL-L stops Insert mode. Needed for - * when "count" is non-zero. - */ - if (cmdchar != 'r' && cmdchar != 'v') - AppendToRedobuff(p_im ? (char_u *)"\014" : ESC_STR); + // Don't append the ESC for "r" and "grx". + // When 'insertmode' is set only CTRL-L stops Insert mode. Needed for + // when "count" is non-zero. + if (cmdchar != 'r' && cmdchar != 'v') { + AppendToRedobuff(p_im ? "\014" : ESC_STR); + } /* * Repeating insert may take a long time. Check for @@ -7303,7 +7319,8 @@ static bool ins_start_select(int c) // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; + const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER, + (char)(uint8_t)mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); @@ -8111,11 +8128,11 @@ static bool ins_tab(void) return true; } - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendToRedobuff((char_u *)"\t"); + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + AppendToRedobuff("\t"); if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); @@ -8380,8 +8397,8 @@ static int ins_digraph(void) edit_unputchar(); } if (cc != ESC) { - AppendToRedobuff((char_u *)CTRL_V_STR); - c = getdigraph(c, cc, TRUE); + AppendToRedobuff(CTRL_V_STR); + c = getdigraph(c, cc, true); clear_showcmd(); return c; } @@ -8443,12 +8460,13 @@ static int ins_ctrl_ey(int tc) if (c != NUL) { long tw_save; - /* The character must be taken literally, insert like it - * was typed after a CTRL-V, and pretend 'textwidth' - * wasn't set. Digits, 'o' and 'x' are special after a - * CTRL-V, don't use it for these. */ - if (c < 256 && !isalnum(c)) - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + // The character must be taken literally, insert like it + // was typed after a CTRL-V, and pretend 'textwidth' + // wasn't set. Digits, 'o' and 'x' are special after a + // CTRL-V, don't use it for these. + if (c < 256 && !isalnum(c)) { + AppendToRedobuff(CTRL_V_STR); + } tw_save = curbuf->b_p_tw; curbuf->b_p_tw = -1; insert_special(c, TRUE, FALSE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 65bb90fc15..3a0075e48a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -173,7 +173,6 @@ static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); @@ -1852,7 +1851,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char_u *const op) FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; char_u *arg_end = NULL; int len; int opt_flags; @@ -1864,7 +1862,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (*arg == '$') { // Find the end of the name. arg++; - name = arg; + char *name = (char *)arg; len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); @@ -1875,26 +1873,28 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite(arg)) == NULL) { EMSG(_(e_letunexp)); } else if (!check_secure()) { - const char_u c1 = name[len]; + const char c1 = name[len]; name[len] = NUL; - char_u *p = get_tv_string_chk(tv); + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv((char *)name); + char *s = vim_getenv(name); if (s != NULL) { - p = tofree = concat_str((char_u *)s, p); + tofree = concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; xfree(s); } } if (p != NULL) { - vim_setenv((char *)name, (char *)p); - if (STRICMP(name, "HOME") == 0) + vim_setenv(name, p); + if (STRICMP(name, "HOME") == 0) { init_homedir(); - else if (didset_vim && STRICMP(name, "VIM") == 0) - didset_vim = FALSE; - else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) - didset_vimruntime = FALSE; + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } arg_end = arg; } name[len] = c1; @@ -1914,16 +1914,16 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, } else { int opt_type; long numval; - char_u *stringval = NULL; - char_u *s; + char *stringval = NULL; const char c1 = *p; *p = NUL; varnumber_T n = tv_get_number(tv); - s = get_tv_string_chk(tv); // != NULL if number or string. + const char *s = tv_get_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { EMSG2(_(e_letwrong), op); @@ -1935,44 +1935,45 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, n = numval - n; } } else if (opt_type == 0 && stringval != NULL) { // string - s = concat_str(stringval, s); - xfree(stringval); - stringval = s; + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; } } } if (s != NULL) { - set_option_value((const char *)arg, n, (char *)s, opt_flags); + set_option_value((const char *)arg, n, s, opt_flags); arg_end = (char_u *)p; } *p = c1; xfree(stringval); } - } - /* - * ":let @r = expr": Set register contents. - */ - else if (*arg == '@') { - ++arg; - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) - EMSG(_(e_letunexp)); - else { - char_u *ptofree = NULL; + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + arg++; + if (op != NULL && (*op == '+' || *op == '-')) { + emsgf(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + emsgf(_(e_letunexp)); + } else { char_u *s; - char_u *p = get_tv_string_chk(tv); + char_u *ptofree = NULL; + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { - p = ptofree = concat_str(s, p); + ptofree = concat_str(s, (const char_u *)p); + p = (const char *)ptofree; xfree(s); } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false); + write_reg_contents(*arg == '@' ? '"' : *arg, + (const char_u *)p, STRLEN(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -2129,13 +2130,14 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { /* Get the index [expr] or the first index [expr: ]. */ p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! return NULL; - if (get_tv_string_chk(&var1) == NULL) { + } + if (!tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return NULL; @@ -2174,7 +2176,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } return NULL; } - if (get_tv_string_chk(&var2) == NULL) { + if (!tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -3810,16 +3812,15 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) break; if ((op != '+' || rettv->v_type != VAR_LIST) - && (op == '.' || rettv->v_type != VAR_FLOAT) - ) { - /* For "list + ...", an illegal use of the first operand as - * a number cannot be determined before evaluating the 2nd - * operand: if this is also a list, all is ok. - * For "something . ...", "something - ..." or "non-list + ...", - * we know that the first operand needs to be a string or number - * without evaluating the 2nd operand. So check before to avoid - * side effects after an error. */ - if (evaluate && get_tv_string_chk(rettv) == NULL) { + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // we know that the first operand needs to be a string or number + // without evaluating the 2nd operand. So check before to avoid + // side effects after an error. + if (evaluate && !tv_check_str(rettv)) { tv_clear(rettv); return FAIL; } @@ -3840,10 +3841,10 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ if (op == '.') { char buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf2[NUMBUFLEN]; // s1 already checked const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); @@ -4411,7 +4412,7 @@ eval_index ( empty1 = true; } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return FAIL; @@ -4430,7 +4431,7 @@ eval_index ( tv_clear(&var1); } return FAIL; - } else if (evaluate && get_tv_string_chk(&var2) == NULL) { + } else if (evaluate && !tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -4566,7 +4567,7 @@ eval_index ( } if (len == -1) { - key = get_tv_string_chk(&var1); + key = (char_u *)tv_get_string_chk(&var1); if (key == NULL) { tv_clear(&var1); return FAIL; @@ -5570,7 +5571,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *key = NULL; dictitem_T *item; char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; /* * First check if it's not a curly-braces thing: {expr}. @@ -5602,9 +5603,9 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - key = get_tv_string_buf_chk(&tvkey, buf); + key = (char_u *)tv_get_string_buf_chk(&tvkey, buf); if (key == NULL) { - // "key" is NULL when get_tv_string_buf_chk() gave an errmsg + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg tv_clear(&tvkey); goto failret; } @@ -6598,7 +6599,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long lnum; - char_u *line; list_T *l = NULL; listitem_T *li = NULL; typval_T *tv; @@ -6622,21 +6622,23 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; } for (;; ) { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) { /* type error */ - rettv->vval.v_number = 1; /* Failed */ + if (l == NULL) { + tv = &argvars[1]; // Append a string. + } else if (li == NULL) { + break; // End of list. + } else { + tv = &li->li_tv; // Append item from list. + } + const char *const line = tv_get_string_chk(tv); + if (line == NULL) { // Type error. + rettv->vval.v_number = 1; // Failed. break; } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) + ml_append(lnum + added, (char_u *)line, (colnr_T)0, false); + added++; + if (l == NULL) { break; + } li = li->li_next; } @@ -6802,7 +6804,7 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; - char *error = (char *)get_tv_string_chk(&argvars[0]); + const char *const error = tv_get_string_chk(&argvars[0]); if (vimvars[VV_EXCEPTION].vv_str == NULL) { prepare_assert_error(&ga); ga_concat(&ga, (char_u *)"v:exception is not set"); @@ -6821,22 +6823,22 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "assert_fails(cmd [, error])" function static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *cmd = get_tv_string_chk(&argvars[0]); + const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; called_emsg = false; suppress_errthrow = true; emsg_silent = true; - do_cmdline_cmd((char *)cmd); + do_cmdline_cmd(cmd); if (!called_emsg) { prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"command did not fail: "); - ga_concat(&ga, cmd); + ga_concat(&ga, (const char_u *)"command did not fail: "); + ga_concat(&ga, (const char_u *)cmd); assert_error(&ga); ga_clear(&ga); } else if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + const char *const error = tv_get_string_buf_chk(&argvars[1], buf); if (error == NULL || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) { @@ -6911,14 +6913,15 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void assert_match_common(typval_T *argvars, assert_type_T atype) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); - char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); + const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); if (pat == NULL || text == NULL) { EMSG(_(e_invarg)); - } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { + } else if (pattern_match((char_u *)pat, (char_u *)text, false) + != (atype == ASSERT_MATCH)) { garray_T ga; prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); @@ -7107,7 +7110,6 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - char_u *name; rettv->vval.v_number = -1; if (!tv_check_str_or_nr(&argvars[0])) { @@ -7119,13 +7121,14 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // If the buffer isn't found and the second argument is not zero create a // new buffer. + const char *name; if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL + && (name = tv_get_string_chk(&argvars[0])) != NULL && !error) { - buf = buflist_new(name, NULL, (linenr_T)1, 0); + buf = buflist_new((char_u *)name, NULL, 1, 0); } if (buf != NULL) { @@ -7190,24 +7193,23 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void byteidx(typval_T *argvars, typval_T *rettv, int comp) { - char_u *t; - char_u *str; - long idx; - - str = get_tv_string_chk(&argvars[0]); - idx = tv_get_number_chk(&argvars[1], NULL); + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; - if (str == NULL || idx < 0) + if (str == NULL || idx < 0) { return; + } - t = str; + const char *t = str; for (; idx > 0; idx--) { - if (*t == NUL) /* EOL reached */ + if (*t == NUL) { // EOL reached. return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); + } + if (enc_utf8 && comp) { + t += utf_ptr2len((const char_u *)t); + } else { + t += (*mb_ptr2len)((const char_u *)t); + } } rettv->vval.v_number = (varnumber_T)(t - str); } @@ -7444,47 +7446,51 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *message; - char_u *buttons = NULL; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *message; + const char *buttons = NULL; int def = 1; int type = VIM_GENERIC; - char_u *typestr; + const char *typestr; bool error = false; - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; + message = tv_get_string_chk(&argvars[0]); + if (message == NULL) { + error = true; + } if (argvars[1].v_type != VAR_UNKNOWN) { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) { + error = true; + } if (argvars[2].v_type != VAR_UNKNOWN) { def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - typestr = get_tv_string_buf_chk(&argvars[3], buf2); - if (typestr == NULL) - error = TRUE; - else { + typestr = tv_get_string_buf_chk(&argvars[3], buf2); + if (typestr == NULL) { + error = true; + } else { switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; + case 'E': type = VIM_ERROR; break; + case 'Q': type = VIM_QUESTION; break; + case 'I': type = VIM_INFO; break; + case 'W': type = VIM_WARNING; break; + case 'G': type = VIM_GENERIC; break; } } } } } - if (buttons == NULL || *buttons == NUL) - buttons = (char_u *)_("&Ok"); + if (buttons == NULL || *buttons == NUL) { + buttons = _("&Ok"); + } - if (!error) - rettv->vval.v_number = do_dialog(type, NULL, message, buttons, - def, NULL, FALSE); + if (!error) { + rettv->vval.v_number = do_dialog( + type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, false); + } } /* @@ -7708,32 +7714,29 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "key"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } + const size_t key_pattern_len = strlen(key_pattern); Callback callback; if (!callback_from_typval(&callback, &argvars[2])) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_len); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->callback = callback; watcher->busy = false; QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); @@ -7747,26 +7750,17 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); - return; - } - - if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } @@ -7924,16 +7918,15 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); + const char *s = tv_get_string_chk(&argvars[0]); + if (s != NULL) { + s = (const char *)skipwhite((const char_u *)s); + } - char_u *p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) { - if (p != NULL && !aborting()) { - EMSG2(_(e_invexpr2), p); + const char *const expr_start = s; + if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) { + if (expr_start != NULL && !aborting()) { + EMSG2(_(e_invexpr2), expr_start); } need_clr_eos = FALSE; rettv->v_type = VAR_NUMBER; @@ -7965,75 +7958,74 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } -static char_u * get_list_line(int c, void *cookie, int indent) +static char_u *get_list_line(int c, void *cookie, int indent) { - listitem_T **p = (listitem_T **)cookie; - listitem_T *item = *p; - char_u buf[NUMBUFLEN]; - char_u *s; + const listitem_T **const p = (const listitem_T **)cookie; + const listitem_T *item = *p; if (item == NULL) { return NULL; } - s = get_tv_string_buf_chk(&item->li_tv, buf); + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); *p = item->li_next; - return s == NULL ? NULL : vim_strsave(s); + return (char_u *)(s == NULL ? NULL : xstrdup(s)); } // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - bool save_emsg_noredir = emsg_noredir; - garray_T *save_capture_ga = capture_ga; + const int save_msg_silent = msg_silent; + const int save_emsg_silent = emsg_silent; + const bool save_emsg_noredir = emsg_noredir; + garray_T *const save_capture_ga = capture_ga; - if (check_secure()) { - return; - } + if (check_secure()) { + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + if (argvars[1].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&argvars[1], buf); - if (s == NULL) { - return; - } - if (STRNCMP(s, "silent", 6) == 0) { - msg_silent++; - } - if (STRCMP(s, "silent!") == 0) { - emsg_silent = true; - emsg_noredir = true; - } - } else { + if (s == NULL) { + return; + } + if (strncmp(s, S_LEN("silent")) == 0) { msg_silent++; } - - garray_T capture_local; - ga_init(&capture_local, (int)sizeof(char), 80); - capture_ga = &capture_local; - - if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd(tv_get_string(&argvars[0])); - } else if (argvars[0].vval.v_list != NULL) { - list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + if (strcmp(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; } - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - emsg_noredir = save_emsg_noredir; + } else { + msg_silent++; + } - ga_append(capture_ga, NUL); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); + capture_ga = &capture_local; - capture_ga = save_capture_ga; + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd(tv_get_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + list_T *const list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *const item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); + + capture_ga = save_capture_ga; } /// "exepath()" function @@ -8231,7 +8223,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { const char *const av[] = { "keep", "force", "error" }; - action = (const char *)get_tv_string_chk(&argvars[2]); + action = tv_get_string_chk(&argvars[2]); if (action == NULL) { return; // Type error; error message already given. } @@ -8300,10 +8292,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; + char_u *fresult = NULL; + char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; int count = 1; bool first = true; bool error = false; @@ -8313,13 +8303,15 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) const char *fname = tv_get_string(&argvars[0]); + char pathbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else { - if (*p != NUL) - path = p; + const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf); + if (p == NULL) { + error = true; + } else { + if (*p != NUL) { + path = (char_u *)p; + } if (argvars[2].v_type != VAR_UNKNOWN) { count = tv_get_number_chk(&argvars[2], &error); @@ -8473,8 +8465,6 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) { typval_T rettv; typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; int retval = FAIL; int dummy; @@ -8482,7 +8472,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { - s = expr->vval.v_string; + const char_u *const s = expr->vval.v_string; if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { goto theend; @@ -8490,23 +8480,24 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; - s = partial_name(partial); + const char_u *const s = partial_name(partial); if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, NULL) == FAIL) { goto theend; } } else { - s = get_tv_string_buf_chk(expr, buf); + char buf[NUMBUFLEN]; + const char *s = tv_get_string_buf_chk(expr, buf); if (s == NULL) { goto theend; } - s = skipwhite(s); - if (eval1(&s, &rettv, true) == FAIL) { + s = (const char *)skipwhite((const char_u *)s); + if (eval1((char_u **)&s, &rettv, true) == FAIL) { goto theend; } if (*s != NUL) { // check for trailing chars after expr - EMSG2(_(e_invexpr2), s); + emsgf(_(e_invexpr2), s); goto theend; } } @@ -8606,27 +8597,26 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; - char_u *mods; - size_t usedlen = 0; + char_u *fbuf = NULL; size_t len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) + char buf[NUMBUFLEN]; + const char *fname = tv_get_string_chk(&argvars[0]); + const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) { fname = NULL; - else { - len = STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + } else { + len = strlen(fname); + size_t usedlen = 0; + (void)modify_fname((char_u *)mods, &usedlen, (char_u **)&fname, &fbuf, + &len); } rettv->v_type = VAR_STRING; - if (fname == NULL) + if (fname == NULL) { rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); + } else { + rettv->vval.v_string = (char_u *)xmemdupz(fname, len); + } xfree(fbuf); } @@ -9257,7 +9247,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_getbufvar_end; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); emsg_off++; buf_T *const buf = get_buf_tv(&argvars[0], false); @@ -9970,14 +9960,13 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "getreg()" function static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; int arg2 = false; bool return_list = false; bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); + strregname = tv_get_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { arg2 = tv_get_number_chk(&argvars[1], &error); @@ -9986,16 +9975,17 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - strregname = vimvars[VV_REG].vv_str; + strregname = (const char *)vimvars[VV_REG].vv_str; } if (error) { return; } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } if (return_list) { rettv->v_type = VAR_LIST; @@ -10016,23 +10006,24 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) { /* type error; errmsg already given */ + strregname = tv_get_string_chk(&argvars[0]); + if (strregname == NULL) { // Type error; errmsg already given. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; return; } - } else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; + } else { + // Default to v:register. + strregname = (const char *)vimvars[VV_REG].vv_str; + } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } colnr_T reglen = 0; char buf[NUMBUFLEN + 2]; @@ -10108,7 +10099,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the @@ -10307,8 +10298,7 @@ getwinvar ( tp = curtab; } win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk( - &argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -10438,12 +10428,12 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + char buf1[NUMBUFLEN]; + const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10464,12 +10454,13 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) - ? NULL - : file_pat_to_reg_pat(pat, NULL, NULL, false); + rettv->vval.v_string = ((pat == NULL) + ? NULL + : file_pat_to_reg_pat((char_u *)pat, NULL, NULL, + false)); } /// "has()" function @@ -10797,14 +10788,14 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_restricted() || check_secure()) { return; } - char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error - histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + 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 = (char_u *)tv_get_string_buf(&argvars[1], buf); + str = tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, false, NUL); + add_to_history(histype, (char_u *)str, false, NUL); rettv->vval.v_number = true; return; } @@ -10817,22 +10808,20 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); // NULL on type error + 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)); + 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), + 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), + n = del_history_entry(get_histtype(str, strlen(str), false), (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; @@ -10845,13 +10834,12 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType type; int idx; - char_u *str; - str = get_tv_string_chk(&argvars[0]); // NULL on type error + 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); + type = get_histtype(str, strlen(str), false); if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { @@ -10870,9 +10858,9 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int i; - char_u *history = get_tv_string_chk(&argvars[0]); + const char *const history = tv_get_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, strlen(history), false); if (i != HIST_INVALID) { i = get_history_idx(i); @@ -11009,11 +10997,8 @@ static int inputsecret_flag = 0; */ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) { - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; + const char *prompt = tv_get_string_chk(&argvars[0]); int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; char_u *xp_arg = NULL; @@ -11022,49 +11007,45 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmd_silent = FALSE; /* Want to see the prompt. */ if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, '\n'); + if (p == NULL) { p = prompt; - else { - ++p; - c = *p; - *p = NUL; + } else { + p++; msg_start(); msg_clr_eos(); - msg_puts_attr((const char *)prompt, echo_attr); + msg_puts_attr_len(prompt, p - prompt, echo_attr); msg_didout = false; msg_starthere(); - *p = c; } cmdline_row = msg_row; + const char *defstr = ""; if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - defstr = get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + defstr = tv_get_string_buf_chk(&argvars[1], buf); if (defstr != NULL) { stuffReadbuffSpec(defstr); } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - uint32_t argt; - - /* input() with a third argument: completion */ + // input() with a third argument: completion rettv->vval.v_string = NULL; - xp_name = get_tv_string_buf_chk(&argvars[2], buf); + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf); if (xp_name == NULL) { return; } - xp_namelen = (int)STRLEN(xp_name); + const int xp_namelen = (int)strlen(xp_name); - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) + uint32_t argt; + if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) { return; + } } } @@ -11072,8 +11053,8 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); + getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, + xp_type, xp_arg); ex_normal_busy = save_ex_normal_busy; } if (inputdialog && rettv->vval.v_string == NULL @@ -11544,8 +11525,8 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) assert(argl->lv_first); - const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL, true)) { + const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; } @@ -11553,14 +11534,14 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } if (cmd) { - *cmd = (char *)exe; + *cmd = exe; } // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - char *a = (char *)get_tv_string_chk(&arg->li_tv); + const char *a = tv_get_string_chk(&arg->li_tv); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); @@ -11803,50 +11784,49 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - garray_T ga; - char_u *sep; - if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) + if (argvars[0].vval.v_list == NULL) { return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); rettv->v_type = VAR_STRING; if (sep != NULL) { + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); + tv_list_join(&ga, argvars[0].vval.v_list, sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; - } else + } else { rettv->vval.v_string = NULL; + } } /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char numbuf[NUMBUFLEN]; - char *s = NULL; + const char *s = NULL; char *tofree = NULL; size_t len; if (argvars[0].v_type == VAR_LIST) { - if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) { EMSG(_("E474: Failed to convert list to string")); return; } - tofree = s; + s = tofree; if (s == NULL) { assert(len == 0); s = ""; } } else { - s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + s = tv_get_string_buf_chk(&argvars[0], numbuf); if (s) { len = strlen(s); } else { @@ -12043,10 +12023,8 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; + char_u *keys_buf = NULL; + char_u *rhs; int mode; int abbr = FALSE; int get_dict = FALSE; @@ -12062,20 +12040,24 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } + char buf[NUMBUFLEN]; + const char *which; if (argvars[1].v_type != VAR_UNKNOWN) { - which = get_tv_string_buf_chk(&argvars[1], buf); + which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = tv_get_number(&argvars[2]); if (argvars[3].v_type != VAR_UNKNOWN) { get_dict = tv_get_number(&argvars[3]); } } - } else - which = (char_u *)""; - if (which == NULL) + } else { + which = ""; + } + if (which == NULL) { return; + } - mode = get_map_mode(&which, 0); + mode = get_map_mode((char_u **)&which, 0); keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, CPO_TO_CPO_FLAGS); @@ -12141,9 +12123,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) char_u *str = NULL; long len = 0; char_u *expr = NULL; - char_u *pat; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; long start = 0; long nth = 1; @@ -12183,9 +12163,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) len = (long)STRLEN(str); } - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) + char patbuf[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { goto theend; + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -12224,7 +12206,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; @@ -12336,18 +12318,20 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + int prio = 10; int id = -1; bool error = false; const char *conceal_char = NULL; rettv->vval.v_number = -1; - if (grp == NULL || pat == NULL) + if (grp == NULL || pat == NULL) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { @@ -12365,7 +12349,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - if (error == true) { + if (error) { return; } if (id >= 1 && id <= 3) { @@ -12373,17 +12357,16 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)grp, - (const char *)pat, prio, id, - NULL, conceal_char); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - - char_u buf[NUMBUFLEN]; - const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -12431,8 +12414,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, conceal_char); + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); } /* @@ -12833,11 +12816,11 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_tv_string_chk(&argvars[0]); - if (!rettv->vval.v_string) { + const char *const s = tv_get_string_chk(&argvars[0]); + if (!s) { return; } - rettv->vval.v_string = shorten_dir(vim_strsave(rettv->vval.v_string)); + rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s)); } /* @@ -13240,7 +13223,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - const char *key = (const char *)get_tv_string_chk(&argvars[1]); + const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); if (di == NULL) { @@ -13572,40 +13555,45 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; - char_u *flags; - char_u nbuf[NUMBUFLEN]; int mask; if (varp->v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf_chk(varp, nbuf); - if (flags == NULL) - return 0; /* type error; errmsg already given */ + char nbuf[NUMBUFLEN]; + const char *flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) { + return 0; // Type error; errmsg already given. + } while (*flags != NUL) { switch (*flags) { - case 'b': dir = BACKWARD; break; - case 'w': p_ws = true; break; - case 'W': p_ws = false; break; - default: mask = 0; - if (flagsp != NULL) - switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; - case 'z': mask = SP_COLUMN; break; + case 'b': dir = BACKWARD; break; + case 'w': p_ws = true; break; + case 'W': p_ws = false; break; + default: { + mask = 0; + if (flagsp != NULL) { + switch (*flags) { + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; + } } - if (mask == 0) { - EMSG2(_(e_invarg2), flags); - dir = 0; - } else - *flagsp |= mask; + if (mask == 0) { + emsgf(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; + } + } } - if (dir == 0) + if (dir == 0) { break; - ++flags; + } + flags++; } } return dir; @@ -13989,16 +13977,17 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* default: FAIL */ - char_u *name = get_tv_string_chk(&argvars[0]); + const char *const name = tv_get_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { locally = tv_get_number_chk(&argvars[1], &error) == 0; if (!error && argvars[2].v_type != VAR_UNKNOWN) { thisblock = tv_get_number_chk(&argvars[2], &error) != 0; } } - if (!error && name != NULL) - rettv->vval.v_number = find_decl(name, STRLEN(name), locally, + if (!error && name != NULL) { + rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally, thisblock, SEARCH_KEEP) == FAIL; + } } /* @@ -14006,49 +13995,53 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) { - char_u *spat, *mpat, *epat; - char_u *skip; bool save_p_ws = p_ws; int dir; int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ + int retval = 0; // default: FAIL long lnum_stop = 0; long time_limit = 0; - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_string_buf_chk(&argvars[2], nbuf2); - if (spat == NULL || mpat == NULL || epat == NULL) - goto theend; /* type error */ + // Get the three pattern arguments: start, middle, end. + char nbuf1[NUMBUFLEN]; + char nbuf2[NUMBUFLEN]; + char nbuf3[NUMBUFLEN]; + const char *spat = tv_get_string_chk(&argvars[0]); + const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2); + if (spat == NULL || mpat == NULL || epat == NULL) { + goto theend; // Type error. + } - /* Handle the optional fourth argument: flags */ - dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ - if (dir == 0) + // Handle the optional fourth argument: flags. + dir = get_search_arg(&argvars[3], &flags); // may set p_ws. + if (dir == 0) { goto theend; + } - /* Don't accept SP_END or SP_SUBPAT. - * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. - */ + // Don't accept SP_END or SP_SUBPAT. + // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } - /* Using 'r' implies 'W', otherwise it doesn't work. */ - if (flags & SP_REPEAT) + // Using 'r' implies 'W', otherwise it doesn't work. + if (flags & SP_REPEAT) { p_ws = false; + } - /* Optional fifth argument: skip expression */ + // Optional fifth argument: skip expression. + const char *skip; if (argvars[3].v_type == VAR_UNKNOWN - || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + || argvars[4].v_type == VAR_UNKNOWN) { + skip = ""; + } else { + skip = tv_get_string_buf_chk(&argvars[4], nbuf3); + if (skip == NULL) { + goto theend; // Type error. + } if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[5], NULL); if (lnum_stop < 0) { @@ -14062,11 +14055,10 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } } - if (skip == NULL) - goto theend; /* type error */ - retval = do_searchpair(spat, mpat, epat, dir, skip, flags, - match_pos, lnum_stop, time_limit); + retval = do_searchpair( + (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip, + flags, match_pos, lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -14341,14 +14333,12 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - if (check_restricted() || check_secure() || !tv_check_str_or_nr(&argvars[0])) { return; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); buf_T *const buf = get_buf_tv(&argvars[0], false); typval_T *varp = &argvars[2]; @@ -14363,7 +14353,8 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = tv_get_number_chk(varp, &error); - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -14438,17 +14429,17 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; - char_u *fname = get_tv_string_chk(&argvars[0]); + const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { return; } - char_u modebuf[NUMBUFLEN]; - char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); if (mode_str == NULL) { return; } - if (STRLEN(mode_str) != 9) { + if (strlen(mode_str) != 9) { EMSG2(_(e_invarg2), mode_str); return; } @@ -14469,26 +14460,28 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; linenr_T lnum = tv_get_lnum(&argvars[0]); + const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; - } else - line = get_tv_string_chk(&argvars[1]); + } else { + line = tv_get_string_chk(&argvars[1]); + } /* default result is zero == OK */ for (;; ) { if (l != NULL) { - /* list argument, get next string */ - if (li == NULL) + // List argument, get next string. + if (li == NULL) { break; - line = get_tv_string_chk(&li->li_tv); + } + line = tv_get_string_chk(&li->li_tv); li = li->li_next; } @@ -14504,18 +14497,20 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (lnum <= curbuf->b_ml.ml_line_count) { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { + // Existing line, replace it. + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char_u *)line, true) == OK) { changed_bytes(lnum, 0); if (lnum == curwin->w_cursor.lnum) check_cursor_col(); rettv->vval.v_number = 0; /* OK */ } } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ + // lnum is one past the last line, append the line. + added++; + if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } } if (l == NULL) /* only one string argument */ @@ -14544,7 +14539,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) { static char *e_invact = N_("E927: Invalid action: '%s'"); - char_u *title = NULL; + const char *title = NULL; int action = ' '; rettv->vval.v_number = -1; dict_T *d = NULL; @@ -14563,7 +14558,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) EMSG(_(e_stringreq)); return; } - char_u *act = get_tv_string_chk(action_arg); + const char *const act = tv_get_string_chk(action_arg); if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { action = *act; } else { @@ -14576,7 +14571,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) // Option argument was not given. goto skip_args; } else if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); + title = tv_get_string_chk(title_arg); if (!title) { // Type error. Error already printed by get_tv_string_chk(). return; @@ -14584,17 +14579,17 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_DICT) { d = title_arg->vval.v_dict; } else { - EMSG(_(e_dictreq)); + emsgf(_(e_dictreq)); return; } skip_args: if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + title = (wp ? "setloclist()" : "setqflist()"); } list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, title, d) == OK) { + if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14728,11 +14723,10 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - char_u *name; colnr_T curswant = -1; rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); + const char *const name = tv_get_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { if (--pos.col < 0) { @@ -14753,7 +14747,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark - if (setmark_pos(name[1], &pos, fnum) == OK) { + if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; } } else { @@ -14777,8 +14771,6 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int regname; - char_u *strregname; - char_u *stropt; bool append = false; MotionType yank_type; long block_len; @@ -14786,39 +14778,47 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) block_len = -1; yank_type = kMTUnknown; - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ + rettv->vval.v_number = 1; // FAIL is default. - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') + const char *const strregname = tv_get_string_chk(argvars); + if (strregname == NULL) { + return; // Type error; errmsg already given. + } + regname = (uint8_t)(*strregname); + if (regname == 0 || regname == '@') { regname = '"'; + } if (argvars[2].v_type != VAR_UNKNOWN) { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) + const char *stropt = tv_get_string_chk(&argvars[2]); + if (stropt == NULL) { + return; // Type error. + } + for (; *stropt != NUL; stropt++) { switch (*stropt) { - case 'a': case 'A': // append - append = true; - break; - case 'v': case 'c': // character-wise selection - yank_type = kMTCharWise; - break; - case 'V': case 'l': // line-wise selection - yank_type = kMTLineWise; - break; - case 'b': case Ctrl_V: // block-wise selection - yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - ++stropt; - block_len = getdigits_long(&stropt) - 1; - --stropt; + case 'a': case 'A': { // append + append = true; + break; + } + case 'v': case 'c': { // character-wise selection + yank_type = kMTCharWise; + break; + } + case 'V': case 'l': { // line-wise selection + yank_type = kMTLineWise; + break; + } + case 'b': case Ctrl_V: { // block-wise selection + yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + block_len = getdigits_long((char_u **)&stropt) - 1; + stropt--; + } + break; } - break; } + } } if (argvars[1].v_type == VAR_LIST) { @@ -14828,42 +14828,44 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2)); - char_u **curval = lstval; - char_u **allocval = lstval + len + 2; - char_u **curallocval = allocval; + char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + const char **curval = (const char **)lstval; + char **allocval = lstval + len + 2; + char **curallocval = allocval; - char_u buf[NUMBUFLEN]; for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; li != NULL; li = li->li_next) { - char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf); - if (strval == NULL) { + char buf[NUMBUFLEN]; + *curval = tv_get_string_buf_chk(&li->li_tv, buf); + if (*curval == NULL) { goto free_lstval; } - if (strval == buf) { + if (*curval == buf) { // Need to make a copy, - // next get_tv_string_buf_chk() will overwrite the string. - strval = vim_strsave(buf); - *curallocval++ = strval; + // next tv_get_string_buf_chk() will overwrite the string. + *curallocval = xstrdup(*curval); + *curval = *curallocval; + curallocval++; } - *curval++ = strval; + curval++; } *curval++ = NULL; - write_reg_contents_lst(regname, lstval, STRLEN(lstval), - append, yank_type, block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, + block_len); free_lstval: - while (curallocval > allocval) - xfree(*--curallocval); + while (curallocval > allocval) { + xfree(*--curallocval); + } xfree(lstval); } else { - char_u *strval = get_tv_string_chk(&argvars[1]); + const char *strval = tv_get_string_chk(&argvars[1]); if (strval == NULL) { return; } - write_reg_contents_ex(regname, strval, STRLEN(strval), + write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), append, yank_type, block_len); } rettv->vval.v_number = 0; @@ -14881,7 +14883,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; if (varname != NULL && varp != NULL && tp != NULL) { @@ -14935,7 +14937,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tp = curtab; } win_T *const win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { @@ -14950,8 +14952,8 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname++; numval = tv_get_number_chk(varp, &error); - char_u nbuf[NUMBUFLEN]; - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -15415,25 +15417,26 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *word = (char_u *)""; + const char *word = ""; hlf_T attr = HLF_COUNT; size_t len = 0; tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { - /* Find the start and length of the badly spelled word. */ - len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); - if (len != 0) - word = get_cursor_pos_ptr(); + // Find the start and length of the badly spelled word. + len = spell_move_to(curwin, FORWARD, true, true, &attr); + if (len != 0) { + word = (char *)get_cursor_pos_ptr(); + } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { - char_u *str = get_tv_string_chk(&argvars[0]); + const char *str = tv_get_string_chk(&argvars[0]); int capcol = -1; if (str != NULL) { - /* Check the argument for spelling. */ + // Check the argument for spelling. while (*str != NUL) { - len = spell_check(curwin, str, &attr, &capcol, false); + len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); if (attr != HLF_COUNT) { word = str; break; @@ -15444,7 +15447,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -15499,9 +15502,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = NULL; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; int match; colnr_T col = 0; @@ -15513,8 +15514,10 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) p_cpo = (char_u *)""; const char *str = tv_get_string(&argvars[0]); + const char *pat = NULL; + char patbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); + pat = tv_get_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { typeerr = true; } @@ -15522,15 +15525,16 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; + if (pat == NULL || *pat == NUL) { + pat = "[\\x01- ]\\+"; + } tv_list_alloc_ret(rettv); if (typeerr) return; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { @@ -15682,7 +15686,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - char_u *str = get_tv_string_chk(&argvars[0]); + const char *const str = tv_get_string_chk(&argvars[0]); if (str == NULL) { return; } @@ -15697,11 +15701,11 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) while (charidx >= 0 && byteidx < len) { if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); + rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx); break; } charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + byteidx += MB_CPTR2LEN((const char_u *)str + byteidx); } } @@ -15710,24 +15714,22 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *haystack = tv_get_string_buf_chk(&argvars[0], buf); + const char *const haystack_start = haystack; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = tv_get_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) { + const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], + &error); + if (error || start_idx >= (ptrdiff_t)strlen(haystack)) { return; } if (start_idx >= 0) { @@ -15735,9 +15737,10 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); + const char *pos = strstr(haystack, needle); + if (pos != NULL) { + rettv->vval.v_number = (varnumber_T)(pos - haystack_start); + } } /* @@ -15765,7 +15768,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); + int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { skipcc = tv_get_number_chk(&argvars[1], NULL); @@ -15775,7 +15778,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv((char_u **)&s); + func_mb_ptr2char_adv((const char_u **)&s); len++; } rettv->vval.v_number = len; @@ -15904,47 +15907,46 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } - haystack_len = (int)STRLEN(haystack); + const size_t haystack_len = STRLEN(haystack); + ptrdiff_t end_idx; if (argvars[2].v_type != VAR_UNKNOWN) { // Third argument: upper limit for index. - end_idx = tv_get_number_chk(&argvars[2], NULL); + end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL); if (end_idx < 0) { return; // Can never find a match. } } else { - end_idx = haystack_len; + end_idx = (ptrdiff_t)haystack_len; } + const char *lastmatch = NULL; if (*needle == NUL) { - /* Empty string matches past the end. */ + // Empty string matches past the end. lastmatch = haystack + end_idx; } else { - for (rest = haystack; *rest != NUL; ++rest) { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) + for (const char *rest = haystack; *rest != NUL; rest++) { + rest = strstr(rest, needle); + if (rest == NULL || rest > haystack + end_idx) { break; + } lastmatch = rest; } } - if (lastmatch == NULL) + if (lastmatch == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); + } } /* @@ -15994,20 +15996,20 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u patbuf[NUMBUFLEN]; - char_u subbuf[NUMBUFLEN]; - char_u flagsbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + char subbuf[NUMBUFLEN]; + char flagsbuf[NUMBUFLEN]; - char_u *str = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = NULL; - typval_T *expr = NULL; - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); + const char *const str = tv_get_string_chk(&argvars[0]); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + const char *sub = NULL; + const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); + typval_T *expr = NULL; if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { expr = &argvars[2]; } else { - sub = get_tv_string_buf_chk(&argvars[2], subbuf); + sub = tv_get_string_buf_chk(&argvars[2], subbuf); } rettv->v_type = VAR_STRING; @@ -16015,7 +16017,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) || flg == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); + rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat, + (char_u *)sub, expr, (char_u *)flg); } } @@ -16333,19 +16336,20 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - char_u *arg; if (argvars[0].v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(&argvars[0]); + const char *const arg = tv_get_string_chk(&argvars[0]); nr = 0; if (arg != NULL) { - if (STRCMP(arg, "$") == 0) + if (strcmp(arg, "$") == 0) { nr = tabpage_index(NULL) - 1; - else + } else { EMSG2(_(e_invexpr2), arg); + } } - } else + } else { nr = tabpage_index(curtab); + } rettv->vval.v_number = nr; } @@ -16359,19 +16363,19 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) win_T *twin; int nr = 1; win_T *wp; - char_u *arg; twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) + const char *const arg = tv_get_string_chk(argvar); + if (arg == NULL) { + nr = 0; // Type error; errmsg already given. + } else if (strcmp(arg, "$") == 0) { twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) { + } else if (strcmp(arg, "#") == 0) { twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) + if (twin == NULL) { nr = 0; + } } else { EMSG2(_(e_invexpr2), arg); nr = 0; @@ -16853,28 +16857,25 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int fromlen; - int tolen; - int idx; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; const char *in_str = tv_get_string(&argvars[0]); - const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); - const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf); + const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2); // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (fromstr == NULL || tostr == NULL) { - return; // type error; errmsg already given + return; // Type error; errmsg already given. } + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); if (!has_mbyte) { // Not multi-byte: fromstr and tostr must be the same length. - if (STRLEN(fromstr) != STRLEN(tostr)) { + if (strlen(fromstr) != strlen(tostr)) { goto error; } } @@ -16886,23 +16887,26 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *cpstr = in_str; const int inlen = (*mb_ptr2len)((const char_u *)in_str); int cplen = inlen; - idx = 0; - for (const char_u *p = fromstr; *p != NUL; p += fromlen) { - fromlen = (*mb_ptr2len)(p); + int idx = 0; + int fromlen; + for (const char *p = fromstr; *p != NUL; p += fromlen) { + fromlen = (*mb_ptr2len)((const char_u *)p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { + int tolen; for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + tolen = (*mb_ptr2len)((const char_u *)p); if (idx-- == 0) { cplen = tolen; cpstr = (char *)p; break; } } - if (*p == NUL) /* tostr is shorter than fromstr */ + if (*p == NUL) { // tostr is shorter than fromstr. goto error; + } break; } - ++idx; + idx++; } if (first && cpstr == in_str) { @@ -16910,8 +16914,9 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // (multi-byte) characters. Done only once when a character // of in_str doesn't appear in fromstr. first = false; - for (const char_u *p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + int tolen; + for (const char *p = tostr; *p != NUL; p += tolen) { + tolen = (*mb_ptr2len)((const char_u *)p); idx--; } if (idx != 0) { @@ -16926,7 +16931,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) in_str += inlen; } else { // When not using multi-byte chars we can do it faster. - char_u *p = vim_strchr(fromstr, *in_str); + const char *const p = strchr(fromstr, *in_str); if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); } else { @@ -17278,8 +17283,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, { int error = 0; for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = (const char *)get_tv_string_chk( - (typval_T *)&li->li_tv); + const char *const s = tv_get_string_chk(&li->li_tv); if (s == NULL) { return false; } @@ -17368,17 +17372,16 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return NULL; } - // For types other than list, let get_tv_string_buf_chk() get the value or + // For types other than list, let tv_get_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char *ret = (char *)get_tv_string_chk(tv); + const char *ret = tv_get_string_chk(tv); if (ret && (*len = strlen(ret))) { - ret = xstrdup(ret); + return xmemdupz(ret, (size_t)(*len)); } else { - ret = NULL; *len = -1; + return NULL; } - return ret; } // Pre-calculate the resulting length. @@ -17448,7 +17451,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const flags = (const char *)get_tv_string_chk(&argvars[2]); + const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } @@ -17460,9 +17463,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - const char buf[NUMBUFLEN]; - const char *const fname = (const char *)get_tv_string_buf_chk(&argvars[1], - (char_u *)buf); + char buf[NUMBUFLEN]; + const char *const fname = tv_get_string_buf_chk(&argvars[1], buf); if (fname == NULL) { return; } @@ -17510,7 +17512,6 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, int *const ret_fnum) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *name; static pos_T pos; pos_T *pp; @@ -17564,7 +17565,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &pos; } - name = get_tv_string_chk(tv); + const char *const name = tv_get_string_chk(tv); if (name == NULL) { return NULL; } @@ -17578,7 +17579,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &curwin->w_cursor; } if (name[0] == '\'') { // Mark. - pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } @@ -18403,58 +18404,11 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -// TODO(ZyX-I): move to eval/typval - -/// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! -char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf_chk(varp, mybuf); -} - -char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - switch (varp->v_type) { - case VAR_NUMBER: - sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number); - return buf; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E729: using Funcref as a String")); - break; - case VAR_LIST: - EMSG(_("E730: using List as a String")); - break; - case VAR_DICT: - EMSG(_("E731: using Dictionary as a String")); - break; - case VAR_FLOAT: - EMSG(_(e_float_as_string)); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - return varp->vval.v_string; - return (char_u *)""; - case VAR_SPECIAL: - STRCPY(buf, encode_special_var_names[varp->vval.v_special]); - return buf; - case VAR_UNKNOWN: - EMSG(_("E908: using an invalid value as a String")); - break; - } - return NULL; -} - -/* - * Find variable "name" in the list of variables. - * Return a pointer to it if found, NULL if not found. - * Careful: "a:0" variables don't have a name. - * When "htp" is not NULL we are writing to the variable, set "htp" to the - * hashtab_T used. - */ +// Find variable "name" in the list of variables. +// Return a pointer to it if found, NULL if not found. +// Careful: "a:0" variables don't have a name. +// When "htp" is not NULL we are writing to the variable, set "htp" to the +// hashtab_T used. static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ca635dcae9..78eca15fec 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -796,7 +796,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // of the string means it should match everything up to the '*' instead of the // whole string. const size_t len = strlen(watcher->key_pattern); - if (watcher->key_pattern[len - 1] == '*') { + if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; } else { return strcmp(key, watcher->key_pattern) == 0; @@ -2020,7 +2020,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// /// @return true if everything is OK, false otherwise. bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { switch (tv->v_type) { case VAR_NUMBER: @@ -2072,7 +2072,7 @@ static const char *const num_errors[] = { /// Check that given value is a number or can be converted to it /// -/// Error messages are compatible with tv_get_number() previously used for +/// Error messages are compatible with tv_get_number_chk() previously used for /// the same purpose. /// /// @param[in] tv Value to check. @@ -2101,6 +2101,50 @@ bool tv_check_num(const typval_T *const tv) return false; } +#define FUNC_ERROR "E729: using Funcref as a String" + +static const char *const str_errors[] = { + [VAR_PARTIAL]=N_(FUNC_ERROR), + [VAR_FUNC]=N_(FUNC_ERROR), + [VAR_LIST]=N_("E730: using List as a String"), + [VAR_DICT]=N_("E731: using Dictionary as a String"), + [VAR_FLOAT]=((const char *)e_float_as_string), + [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a string or can be converted to it +/// +/// Error messages are compatible with tv_get_string_chk() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get /// Get the number value of a VimL object @@ -2245,6 +2289,67 @@ float_T tv_get_float(const typval_T *const tv) return 0; } +/// Get the string value of a VimL object +/// +/// @param[in] tv Object to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: { + snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); + return buf; + } + case VAR_STRING: { + if (tv->vval.v_string != NULL) { + return (const char *)tv->vval.v_string; + } + return ""; + } + case VAR_SPECIAL: { + STRCPY(buf, encode_special_var_names[tv->vval.v_special]); + return buf; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + return NULL; +} + +/// Get the string value of a VimL object +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// tv_get_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_chk(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char mybuf[NUMBUFLEN]; + + return tv_get_string_buf_chk(tv, mybuf); +} + /// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It @@ -2252,7 +2357,7 @@ float_T tv_get_float(const typval_T *const tv) /// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2269,7 +2374,7 @@ const char *tv_get_string(const typval_T *const tv) /// Get the string value of a VimL object /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2285,8 +2390,7 @@ const char *tv_get_string(const typval_T *const tv) const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)tv, (char_u *)buf); + const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3c294b45b8..42581939e3 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -27,6 +27,7 @@ typedef double float_T; /// Mimimal possible value of varnumber_T variable #define VARNUMBER_MIN INT_MIN +#define PRIdVARNUMBER "d" /// %d printf format specifier for varnumber_T #define PRIdVARNUMBER "d" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9681527fee..9a847a4c0a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1008,8 +1008,8 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) AppendToRedobuffLit(cmd, -1); xfree(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; + AppendToRedobuff("\n"); + bangredo = false; } /* * Add quotes around the command, for shells that need them. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6b661cff11..73b81ac2d9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6794,7 +6794,7 @@ do_exedit ( int ms = msg_scroll; if (eap->nextcmd != NULL) { - stuffReadbuff(eap->nextcmd); + stuffReadbuff((const char *)eap->nextcmd); eap->nextcmd = NULL; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3bd8d580ab..a0981a42ce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2554,19 +2554,22 @@ void cmdline_paste_str(char_u *s, int literally) else while (*s != NUL) { cv = *s; - if (cv == Ctrl_V && s[1]) - ++s; - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (cv == Ctrl_V && s[1]) { + s++; + } + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (cv == Ctrl_V || c == ESC || c == Ctrl_C || c == CAR || c == NL || c == Ctrl_L #ifdef UNIX || c == intr_char #endif - || (c == Ctrl_BSL && *s == Ctrl_N)) + || (c == Ctrl_BSL && *s == Ctrl_N)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -4636,7 +4639,7 @@ in_history ( /// /// @return Any value from HistoryType enum, including HIST_INVALID. May not /// return HIST_DEFAULT unless return_default is true. -HistoryType get_histtype(const char_u *const name, const size_t len, +HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5029,7 +5032,7 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - histype1 = get_histtype(arg, end - arg, false); + histype1 = get_histtype((const char *)arg, end - arg, false); if (histype1 == HIST_INVALID) { if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; @@ -5288,18 +5291,18 @@ static int ex_window(void) cmdwin_result = Ctrl_C; /* Set the new command line from the cmdline buffer. */ xfree(ccline.cmdbuff); - if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { /* :qa[!] typed */ - char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; + if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed + const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; if (histtype == HIST_CMD) { - /* Execute the command directly. */ - ccline.cmdbuff = vim_strsave((char_u *)p); + // Execute the command directly. + ccline.cmdbuff = (char_u *)xstrdup(p); cmdwin_result = CAR; } else { - /* First need to cancel what we were doing. */ + // First need to cancel what we were doing. ccline.cmdbuff = NULL; stuffcharReadbuff(':'); - stuffReadbuff((char_u *)p); + stuffReadbuff(p); stuffcharReadbuff(CAR); } } else if (cmdwin_result == K_XF2) { /* :qa typed */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 67ac9f9957..127efda65c 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -614,10 +614,12 @@ readfile ( return FAIL; } #ifdef UNIX - /* Set swap file protection bits after creating it. */ + // Set swap file protection bits after creating it. if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL - && curbuf->b_ml.ml_mfp->mf_fname != NULL) - (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode); + && curbuf->b_ml.ml_mfp->mf_fname != NULL) { + (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname, + (long)swap_mode); + } #endif } @@ -2870,9 +2872,9 @@ buf_write ( xfree(backup); backup = NULL; } else { - /* set file protection same as original file, but - * strip s-bit */ - (void)os_setperm(backup, perm & 0777); + // set file protection same as original file, but + // strip s-bit. + (void)os_setperm((const char *)backup, perm & 0777); #ifdef UNIX /* @@ -2883,7 +2885,8 @@ buf_write ( */ if (file_info_new.stat.st_gid != file_info_old.stat.st_gid && os_fchown(bfd, -1, file_info_old.stat.st_gid) != 0) { - os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); + os_setperm((const char *)backup, + (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX mch_copy_sec(fname, backup); @@ -3037,8 +3040,8 @@ nobackup: && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; - (void)os_setperm(fname, perm); - made_writable = TRUE; + (void)os_setperm((const char *)fname, perm); + made_writable = true; } #endif @@ -3402,8 +3405,9 @@ restore_backup: || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (perm >= 0) /* set permission again, may have changed */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set permission again, may have changed. + (void)os_setperm((const char *)wfname, perm); + } } buf_set_file_id(buf); } else if (!buf->file_id_valid) { @@ -3421,8 +3425,9 @@ restore_backup: if (made_writable) perm &= ~0200; /* reset 'w' bit for security reasons */ #endif - if (perm >= 0) /* set perm. of new file same as old file */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set perm. of new file same as old file. + (void)os_setperm((const char *)wfname, perm); + } #ifdef HAVE_ACL /* Probably need to set the ACL before changing the user (can't set the * ACL on a file the user doesn't own). */ @@ -3628,7 +3633,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); + os_setperm(org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4690,8 +4695,8 @@ int vim_rename(const char_u *from, const char_u *to) errmsg = _("E210: Error reading \"%s\""); to = from; } -#ifndef UNIX /* for Unix os_open() already set the permission */ - os_setperm(to, perm); +#ifndef UNIX // For Unix os_open() already set the permission. + os_setperm((const char *)to, perm); #endif #ifdef HAVE_ACL mch_set_acl(to, acl); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b64f089b96..7143819e21 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -235,19 +235,18 @@ char_u *get_inserted(void) return get_buffcont(&redobuff, FALSE); } -/* - * Add string "s" after the current block of buffer "buf". - * K_SPECIAL and CSI should have been escaped already. - */ -static void -add_buff ( - buffheader_T *buf, - char_u *s, - ssize_t slen // length of "s" or -1 -) +/// Add string after the current block of the given buffer +/// +/// K_SPECIAL and CSI should have been escaped already. +/// +/// @param[out] buf Buffer to add to. +/// @param[in] s String to add. +/// @param[in] slen String length or -1 for NUL-terminated string. +static void add_buff(buffheader_T *const buf, const char *const s, + ptrdiff_t slen) { if (slen < 0) { - slen = (ssize_t)STRLEN(s); + slen = (ptrdiff_t)strlen(s); } if (slen == 0) { // don't add empty strings return; @@ -292,9 +291,8 @@ add_buff ( */ static void add_num_buff(buffheader_T *buf, long n) { - char_u number[32]; - - sprintf((char *)number, "%" PRId64, (int64_t)n); + char number[32]; + snprintf(number, sizeof(number), "%ld", n); add_buff(buf, number, -1L); } @@ -304,27 +302,29 @@ static void add_num_buff(buffheader_T *buf, long n) */ static void add_char_buff(buffheader_T *buf, int c) { - char_u bytes[MB_MAXBYTES + 1]; - int len; - int i; - char_u temp[4]; + char bytes[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) + int len; + if (IS_SPECIAL(c)) { len = 1; - else - len = (*mb_char2bytes)(c, bytes); - for (i = 0; i < len; ++i) { - if (!IS_SPECIAL(c)) + } else { + len = (*mb_char2bytes)(c, (char_u *)bytes); + } + + for (int i = 0; i < len; i++) { + if (!IS_SPECIAL(c)) { c = bytes[i]; + } + char temp[4]; if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - /* translate special key code into three byte sequence */ - temp[0] = K_SPECIAL; - temp[1] = (char_u)K_SECOND(c); - temp[2] = (char_u)K_THIRD(c); + // Translate special key code into three byte sequence. + temp[0] = (char)K_SPECIAL; + temp[1] = (char)K_SECOND(c); + temp[2] = (char)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = (char_u)c; + temp[0] = (char)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -479,16 +479,14 @@ static int save_level = 0; void saveRedobuff(void) { - char_u *s; - if (save_level++ == 0) { save_redobuff = redobuff; redobuff.bh_first.b_next = NULL; save_old_redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; - /* Make a copy, so that ":normal ." in a function works. */ - s = get_buffcont(&save_redobuff, FALSE); + // Make a copy, so that ":normal ." in a function works. + char *const s = (char *)get_buffcont(&save_redobuff, false); if (s != NULL) { add_buff(&redobuff, s, -1L); xfree(s); @@ -514,10 +512,11 @@ void restoreRedobuff(void) * Append "s" to the redo buffer. * K_SPECIAL and CSI should already have been escaped. */ -void AppendToRedobuff(char_u *s) +void AppendToRedobuff(const char *s) { - if (!block_redo) - add_buff(&redobuff, s, -1L); + if (!block_redo) { + add_buff(&redobuff, (const char *)s, -1L); + } } /* @@ -530,44 +529,47 @@ AppendToRedobuffLit ( int len /* length of "str" or -1 for up to the NUL */ ) { - char_u *s = str; - int c; - char_u *start; - - if (block_redo) + if (block_redo) { return; + } - while (len < 0 ? *s != NUL : s - str < len) { - /* Put a string of normal characters in the redo buffer (that's - * faster). */ - start = s; - while (*s >= ' ' && *s < DEL && (len < 0 || s - str < len)) - ++s; - - /* Don't put '0' or '^' as last character, just in case a CTRL-D is - * typed next. */ - if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) - --s; - if (s > start) + const char *s = (const char *)str; + while (len < 0 ? *s != NUL : s - (const char *)str < len) { + // Put a string of normal characters in the redo buffer (that's + // faster). + const char *start = s; + while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) { + s++; + } + + // Don't put '0' or '^' as last character, just in case a CTRL-D is + // typed next. + if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) { + s--; + } + if (s > start) { add_buff(&redobuff, start, (long)(s - start)); + } - if (*s == NUL || (len >= 0 && s - str >= len)) + if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) { break; + } - /* Handle a special or multibyte character. */ - if (has_mbyte) - /* Handle composing chars separately. */ - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) + // Handle a special or multibyte character. + // Composing chars separately are handled separately. + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&s) + : (uint8_t)(*s++)); + if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) { add_char_buff(&redobuff, Ctrl_V); + } - /* CTRL-V '0' must be inserted as CTRL-V 048 */ - if (*s == NUL && c == '0') - add_buff(&redobuff, (char_u *)"048", 3L); - else + // CTRL-V '0' must be inserted as CTRL-V 048. + if (*s == NUL && c == '0') { + add_buff(&redobuff, "048", 3L); + } else { add_char_buff(&redobuff, c); + } } } @@ -594,19 +596,19 @@ void AppendNumberToRedobuff(long n) * Append string "s" to the stuff buffer. * CSI and K_SPECIAL must already have been escaped. */ -void stuffReadbuff(char_u *s) +void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. /// @remark CSI and K_SPECIAL must already have been escaped. -void stuffRedoReadbuff(char_u *s) +void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); } -void stuffReadbuffLen(char_u *s, long len) +void stuffReadbuffLen(const char *s, long len) { add_buff(&readbuf1, s, len); } @@ -616,19 +618,18 @@ void stuffReadbuffLen(char_u *s, long len) * escaping other K_SPECIAL and CSI bytes. * Change CR, LF and ESC into a space. */ -void stuffReadbuffSpec(char_u *s) +void stuffReadbuffSpec(const char *s) { - int c; - while (*s != NUL) { - if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { - /* Insert special key literally. */ - stuffReadbuffLen(s, 3L); + if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { + // Insert special key literally. + stuffReadbuffLen(s, 3); s += 3; } else { - c = mb_ptr2char_adv(&s); - if (c == CAR || c == NL || c == ESC) + int c = mb_ptr2char_adv((const char_u **)&s); + if (c == CAR || c == NL || c == ESC) { c = ' '; + } stuffcharReadbuff(c); } } @@ -747,8 +748,8 @@ int start_redo(long count, int old_redo) /* copy the buffer name, if present */ if (c == '"') { - add_buff(&readbuf2, (char_u *)"\"", 1L); - c = read_redo(FALSE, old_redo); + add_buff(&readbuf2, "\"", 1L); + c = read_redo(false, old_redo); /* if a numbered buffer is used, increment the number */ if (c >= '1' && c < '9') @@ -1091,21 +1092,19 @@ static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; - char_u buf[2]; // remember how many chars were last recorded if (Recording) { last_recorded_len += len; } - buf[1] = NUL; while (len--) { // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = (char_u)c; + char buf[2] = { (char)c, NUL }; add_buff(&recordbuff, buf, 1L); } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index d87407f099..c15287aa38 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1199,6 +1199,7 @@ EXTERN char_u e_dirnotf[] INIT(= N_( "E919: Directory not found in '%s': \"%s\"")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); +EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 621fa6fefc..d96848754c 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -625,7 +625,7 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n) * Get character at **pp and advance *pp to the next character. * Note: composing characters are skipped! */ -int mb_ptr2char_adv(char_u **pp) +int mb_ptr2char_adv(const char_u **const pp) { int c; @@ -638,7 +638,7 @@ int mb_ptr2char_adv(char_u **pp) * Get character at **pp and advance *pp to the next character. * Note: composing characters are returned as separate characters. */ -int mb_cptr2char_adv(char_u **pp) +int mb_cptr2char_adv(const char_u **pp) { int c; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6ef929120e..d4919dc3b6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1155,7 +1155,7 @@ static void normal_check_stuff_buffer(NormalState *s) if (need_start_insertmode && goto_im() && !VIsual_active) { need_start_insertmode = false; - stuffReadbuff((uint8_t *)"i"); // start insert mode next + stuffReadbuff("i"); // start insert mode next // skip the fileinfo message now, because it would be shown // after insert mode finishes! need_fileinfo = false; @@ -1469,8 +1469,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * If 'cpoptions' does not contain 'r', insert the search * pattern to really repeat the same command. */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { AppendToRedobuffLit(cap->searchbuf, -1); + } AppendToRedobuff(NL_STR); } else if (cap->cmdchar == ':') { /* do_cmdline() has stored the first typed line in @@ -1853,10 +1854,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) - AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ - else - bangredo = true; /* do_bang() will put cmd in redo buffer */ + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } case OP_INDENT: case OP_COLON: @@ -2026,43 +2028,44 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) static void op_colon(oparg_T *oap) { stuffcharReadbuff(':'); - if (oap->is_VIsual) - stuffReadbuff((char_u *)"'<,'>"); - else { - /* - * Make the range look nice, so it can be repeated. - */ - if (oap->start.lnum == curwin->w_cursor.lnum) + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else + } else { stuffnumReadbuff((long)oap->start.lnum); + } if (oap->end.lnum != oap->start.lnum) { stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) + if (oap->end.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else if (oap->end.lnum == curbuf->b_ml.ml_line_count) + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { stuffcharReadbuff('$'); - else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff((char_u *)".+"); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); - } else + } else { stuffnumReadbuff((long)oap->end.lnum); + } } } - if (oap->op_type != OP_COLON) - stuffReadbuff((char_u *)"!"); + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } if (oap->op_type == OP_INDENT) { - stuffReadbuff(get_equalprg()); - stuffReadbuff((char_u *)"\n"); + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); } else if (oap->op_type == OP_FORMAT) { if (*curbuf->b_p_fp != NUL) { - stuffReadbuff(curbuf->b_p_fp); + stuffReadbuff((const char *)curbuf->b_p_fp); } else if (*p_fp != NUL) { - stuffReadbuff(p_fp); + stuffReadbuff((const char *)p_fp); } else { - stuffReadbuff((char_u *)"fmt"); + stuffReadbuff("fmt"); } - stuffReadbuff((char_u *)"\n']"); + stuffReadbuff("\n']"); } /* @@ -2304,7 +2307,7 @@ do_mouse ( if (VIsual_active) { if (VIsual_select) { stuffcharReadbuff(Ctrl_G); - stuffReadbuff((char_u *)"\"+p"); + stuffReadbuff("\"+p"); } else { stuffcharReadbuff('y'); stuffcharReadbuff(K_MIDDLEMOUSE); @@ -4476,7 +4479,7 @@ static void nv_colon(cmdarg_T *cap) /* translate "count:" into ":.,.+(count - 1)" */ stuffcharReadbuff('.'); if (cap->count0 > 1) { - stuffReadbuff((char_u *)",.+"); + stuffReadbuff(",.+"); stuffnumReadbuff(cap->count0 - 1L); } } @@ -6156,17 +6159,15 @@ static void nv_abbrev(cmdarg_T *cap) */ static void nv_optrans(cmdarg_T *cap) { - static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh", - (char_u *)"d$", (char_u *)"c$", - (char_u *)"cl", (char_u *)"cc", - (char_u *)"yy", (char_u *)":s\r"}; - static char_u *str = (char_u *)"xXDCsSY&"; + static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy", + ":s\r" }; + static const char *str = "xXDCsSY&"; if (!checkclearopq(cap->oap)) { if (cap->count0) { stuffnumReadbuff(cap->count0); } - stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); + stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]); } cap->opcount = 0; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index fd1e2d20f2..68ef27222c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1109,7 +1109,6 @@ int insert_reg( ) { int retval = OK; - char_u *arg; int allocated; /* @@ -1125,21 +1124,24 @@ int insert_reg( if (regname != NUL && !valid_yank_reg(regname, false)) return FAIL; - if (regname == '.') /* insert last inserted text */ - retval = stuff_inserted(NUL, 1L, TRUE); - else if (get_spec_reg(regname, &arg, &allocated, TRUE)) { - if (arg == NULL) + char_u *arg; + if (regname == '.') { // Insert last inserted text. + retval = stuff_inserted(NUL, 1L, true); + } else if (get_spec_reg(regname, &arg, &allocated, true)) { + if (arg == NULL) { return FAIL; - stuffescaped(arg, literally); - if (allocated) + } + stuffescaped((const char *)arg, literally); + if (allocated) { xfree(arg); - } else { /* name or number register */ + } + } else { // Name or number register. yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) { retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped(reg->y_array[i], literally); + stuffescaped((const char *)reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1156,29 +1158,29 @@ int insert_reg( * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. */ -static void stuffescaped(char_u *arg, int literally) +static void stuffescaped(const char *arg, int literally) { - int c; - char_u *start; - while (*arg != NUL) { - /* Stuff a sequence of normal ASCII characters, that's fast. Also - * stuff K_SPECIAL to get the effect of a special key when "literally" - * is TRUE. */ - start = arg; - while ((*arg >= ' ' && *arg < DEL) || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) + // Stuff a sequence of normal ASCII characters, that's fast. Also + // stuff K_SPECIAL to get the effect of a special key when "literally" + // is TRUE. + const char *const start = arg; + while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL + && !literally)) { + arg++; + } + if (arg > start) { stuffReadbuffLen(start, (long)(arg - start)); + } /* stuff a single special character */ if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&arg) + : (uint8_t)(*arg++)); + if (literally && ((c < ' ' && c != TAB) || c == DEL)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -2663,7 +2665,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // back to the previous line in the case of 'noautoindent' and // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U // to consume. - stuffReadbuff((char_u *)"\n "); + stuffReadbuff("\n "); stuffcharReadbuff(Ctrl_U); } } @@ -2675,7 +2677,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // character. Simulate it with motion commands after the insert. if (flags & PUT_CURSEND) { if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"j0"); + stuffReadbuff("j0"); } else { // Avoid ringing the bell from attempting to move into the space after // the current line. We can stuff the readbuffer with "l" if: @@ -2705,7 +2707,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } } else if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"g'["); + stuffReadbuff("g'["); } // So the 'u' command restores cursor position after ".p, save the cursor @@ -4981,7 +4983,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, int maxlen, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 1881b329ec..8a80c46cf2 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3419,15 +3419,18 @@ static char_u *set_chars_option(char_u **varp) && p[len] == ':' && p[len + 1] != NUL) { s = p + len + 1; - c1 = mb_ptr2char_adv(&s); - if (mb_char2cells(c1) > 1) + c1 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c1) > 1) { continue; + } if (tab[i].cp == &lcs_tab2) { - if (*s == NUL) + if (*s == NUL) { continue; - c2 = mb_ptr2char_adv(&s); - if (mb_char2cells(c2) > 1) + } + c2 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c2) > 1) { continue; + } } if (*s == ',' || *s == NUL) { if (round) { @@ -6887,8 +6890,8 @@ void set_fileformat(int eol_style, int opt_flags) need_maketitle = true; // Set window title later. } -/// Skip to next part of an option argument: Skip space and comma. -char_u *skip_to_option_part(char_u *p) +/// Skip to next part of an option argument: skip space and comma +char_u *skip_to_option_part(const char_u *p) { if (*p == ',') { p++; @@ -6896,7 +6899,7 @@ char_u *skip_to_option_part(char_u *p) while (*p == ' ') { p++; } - return p; + return (char_u *)p; } /// Isolate one part of a string option separated by `sep_chars`. diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 0372bc8a8c..3833a43f5f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -622,11 +622,11 @@ int32_t os_getperm(const char *name) /// Set the permission of a file. /// /// @return `OK` for success, `FAIL` for failure. -int os_setperm(const char_u *name, int perm) +int os_setperm(const char *const name, int perm) FUNC_ATTR_NONNULL_ALL { int r; - RUN_UV_FS_FUNC(r, uv_fs_chmod, (const char *)name, perm, NULL); + RUN_UV_FS_FUNC(r, uv_fs_chmod, name, perm, NULL); return (r == kLibuvSuccess ? OK : FAIL); } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 9c6f02f778..9baa53d2a2 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2221,10 +2221,11 @@ collection: if (*regparse == '[') endc = get_coll_element(®parse); if (endc == 0) { - if (has_mbyte) - endc = mb_ptr2char_adv(®parse); - else + if (has_mbyte) { + endc = mb_ptr2char_adv((const char_u **)®parse); + } else { endc = *regparse++; + } } /* Handle \o40, \x20 and \u20AC style sequences */ @@ -6271,8 +6272,8 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) str2 = s2; c1 = c2 = 0; while ((int)(str1 - s1) < *n) { - c1 = mb_ptr2char_adv(&str1); - c2 = mb_ptr2char_adv(&str2); + c1 = mb_ptr2char_adv((const char_u **)&str1); + c2 = mb_ptr2char_adv((const char_u **)&str2); /* decompose the character if necessary, into 'base' characters * because I don't care about Arabic, I will hard-code the Hebrew @@ -6586,7 +6587,6 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; int dummy; - char_u buf[NUMBUFLEN]; typval_T rettv; staticList10_T matchList; @@ -6616,7 +6616,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, clear_submatch_list(&matchList); } } - eval_result = get_tv_string_buf_chk(&rettv, buf); + char buf[NUMBUFLEN]; + eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 505d04ed56..cf460adb82 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3141,7 +3141,7 @@ win_line ( p_extra = extra; c = *p_extra; - mb_c = mb_ptr2char_adv(&p_extra); + mb_c = mb_ptr2char_adv((const char_u **)&p_extra); mb_utf8 = (c >= 0x80); n_extra = (int)STRLEN(p_extra); c_extra = NUL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 84bee9b97f..d4f49bffb2 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2309,10 +2309,11 @@ int captype(char_u *word, char_u *end) for (p = word; !spell_iswordp_nmw(p, curwin); mb_ptr_adv(p)) if (end == NULL ? *p == NUL : p >= end) return 0; // only non-word characters, illegal word - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_ptr2char_adv((const char_u **)&p); + } else { c = *p++; + } firstcap = allcap = SPELL_ISUPPER(c); // Need to check all letters to find a word with mixed upper/lower. @@ -2607,14 +2608,15 @@ static bool spell_iswordp(char_u *p, win_T *wp) // Returns true if "p" points to a word character. // Unlike spell_iswordp() this doesn't check for "midword" characters. -bool spell_iswordp_nmw(char_u *p, win_T *wp) +bool spell_iswordp_nmw(const char_u *p, win_T *wp) { int c; if (has_mbyte) { c = mb_ptr2char(p); - if (c > 255) + if (c > 255) { return spell_mb_isword_class(mb_get_class(p), wp); + } return spelltab.st_isw[c]; } return spelltab.st_isw[*p]; @@ -2675,7 +2677,7 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen) buf[outi] = NUL; return FAIL; } - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); } buf[outi] = NUL; @@ -2937,7 +2939,7 @@ void spell_suggest(int count) // For redo we use a change-word command. ResetRedobuff(); - AppendToRedobuff((char_u *)"ciw"); + AppendToRedobuff("ciw"); AppendToRedobuffLit(p + c, stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); @@ -3406,17 +3408,19 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) int l; p = word; - if (has_mbyte) - c = mb_cptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&p); + } else { c = *p++; - if (upper) + } + if (upper) { c = SPELL_TOUPPER(c); - else + } else { c = SPELL_TOFOLD(c); - if (has_mbyte) + } + if (has_mbyte) { l = mb_char2bytes(c, wcopy); - else { + } else { l = 1; wcopy[0] = c; } @@ -3433,10 +3437,11 @@ static void allcap_copy(char_u *word, char_u *wcopy) d = wcopy; for (s = word; *s != NUL; ) { - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (c == 0xdf) { c = 'S'; @@ -5938,12 +5943,12 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. for (s = inword; *s != NUL; ) { - c = mb_cptr2char_adv(&s); - if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) + c = mb_cptr2char_adv((const char_u **)&s); + if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { c = ' '; - else if (c < 256) + } else if (c < 256) { c = slang->sl_sal_first[c]; - else { + } else { ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; if (ip == NULL) // empty list, can't match c = NUL; @@ -6228,9 +6233,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) int word[MAXWLEN]; int wres[MAXWLEN]; int l; - char_u *s; int *ws; - char_u *t; int *pf; int i, j, z; int reslen; @@ -6250,9 +6253,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. wordlen = 0; - for (s = inword; *s != NUL; ) { - t = s; - c = mb_cptr2char_adv(&s); + for (const char_u *s = inword; *s != NUL; ) { + const char_u *t = s; + c = mb_cptr2char_adv((const char_u **)&s); if (slang->sl_rem_accents) { if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { if (did_white) @@ -6261,8 +6264,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) did_white = true; } else { did_white = false; - if (!spell_iswordp_nmw(t, curwin)) + if (!spell_iswordp_nmw(t, curwin)) { continue; + } } } word[wordlen++] = c; @@ -6309,7 +6313,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) continue; ++k; } - s = smp[n].sm_rules; + char_u *s = smp[n].sm_rules; pri = 5; // default priority p0 = *s; @@ -6708,25 +6712,30 @@ soundalike_score ( // support multi-byte characters. static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) { - int *cnt; - int badlen, goodlen; // lengths including NUL + int *cnt; int j, i; int t; int bc, gc; int pbc, pgc; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; const bool l_has_mbyte = has_mbyte; + // Lengths with NUL. + int badlen; + int goodlen; if (l_has_mbyte) { // Get the characters from the multi-byte strings and put them in an // int array for easy access. - for (p = badword, badlen = 0; *p != NUL; ) + badlen = 0; + for (const char_u *p = badword; *p != NUL; ) { wbadword[badlen++] = mb_cptr2char_adv(&p); + } wbadword[badlen++] = 0; - for (p = goodword, goodlen = 0; *p != NUL; ) + goodlen = 0; + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[goodlen++] = mb_cptr2char_adv(&p); + } wgoodword[goodlen++] = 0; } else { badlen = (int)STRLEN(badword) + 1; @@ -6960,19 +6969,20 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo int score_off; int minscore; int round; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; // Get the characters from the multi-byte strings and put them in an // int array for easy access. bi = 0; - for (p = badword; *p != NUL; ) + for (const char_u *p = badword; *p != NUL; ) { wbadword[bi++] = mb_cptr2char_adv(&p); + } wbadword[bi++] = 0; gi = 0; - for (p = goodword; *p != NUL; ) + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[gi++] = mb_cptr2char_adv(&p); + } wgoodword[gi++] = 0; // The idea is to go from start to end over the words. So long as diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 6ba2801203..4d7ff558ad 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1435,7 +1435,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // First count the number of items for each list. Temporarily use // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); mb_cptr_adv(s); if (c >= 256) ++lp->sl_sal_first[c & 0xff]; @@ -1455,8 +1455,8 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // list. memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); - i = mb_cptr2char_adv(&s); + c = mb_cptr2char_adv((const char_u **)&p); + i = mb_cptr2char_adv((const char_u **)&s); if (c >= 256) { // Append the from-to chars at the end of the list with // the low byte. @@ -1542,8 +1542,9 @@ static int *mb_str2wide(char_u *s) int i = 0; int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); - for (char_u *p = s; *p != NUL; ) - res[i++] = mb_ptr2char_adv(&p); + for (char_u *p = s; *p != NUL; ) { + res[i++] = mb_ptr2char_adv((const char_u **)&p); + } res[i] = NUL; return res; @@ -2486,13 +2487,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Check that every character appears only once. for (p = items[1]; *p != NUL; ) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); if ((!GA_EMPTY(&spin->si_map) && vim_strchr(spin->si_map.ga_data, c) != NULL) - || vim_strchr(p, c) != NULL) + || vim_strchr(p, c) != NULL) { smsg(_("Duplicate character in MAP in %s line %d"), fname, lnum); + } } // We simply concatenate all the MAP strings, separated by @@ -2717,12 +2719,12 @@ static unsigned get_affitem(int flagtype, char_u **pp) } res = getdigits_int(pp); } else { - res = mb_ptr2char_adv(pp); + res = mb_ptr2char_adv((const char_u **)pp); if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG && res >= 'A' && res <= 'Z')) { if (**pp == NUL) return 0; - res = mb_ptr2char_adv(pp) + (res << 16); + res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } return res; @@ -2823,12 +2825,14 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL; ) { - n = mb_ptr2char_adv(&p); + n = mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) - && *p != NUL) - n = mb_ptr2char_adv(&p) + (n << 16); - if (n == flag) + && *p != NUL) { + n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + } + if (n == flag) { return true; + } } break; @@ -5436,9 +5440,10 @@ static void init_spellfile(void) fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", - fname != NULL - && strstr((char *)path_tail(fname), ".ascii.") != NULL - ? (char_u *)"ascii" : spell_enc()); + ((fname != NULL + && strstr((char *)path_tail(fname), ".ascii.") != NULL) + ? "ascii" + : (const char *)spell_enc())); set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } @@ -5465,9 +5470,9 @@ static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp) EMSG(_(e_affform)); return FAIL; } - f = mb_ptr2char_adv(&pf); - l = mb_ptr2char_adv(&pl); - u = mb_ptr2char_adv(&pu); + f = mb_ptr2char_adv((const char_u **)&pf); + l = mb_ptr2char_adv((const char_u **)&pl); + u = mb_ptr2char_adv((const char_u **)&pu); // Every character that appears is a word character. if (f < 256) new_st.st_isw[f] = true; @@ -5532,7 +5537,7 @@ set_spell_charflags ( } if (*p != NUL) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); new_st.st_fold[i + 128] = c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) new_st.st_upper[c] = i + 128; @@ -5619,12 +5624,13 @@ static void set_map_str(slang_T *lp, char_u *map) // "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and // before the same slash. For characters above 255 sl_map_hash is used. for (p = map; *p != NUL; ) { - c = mb_cptr2char_adv(&p); - if (c == '/') + c = mb_cptr2char_adv((const char_u **)&p); + if (c == '/') { headc = 0; - else { - if (headc == 0) + } else { + if (headc == 0) { headc = c; + } // Characters above 255 don't fit in sl_map_array[], put them in // the hash table. Each entry is the char, a NUL the headchar and diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e20979c307..5dcffe00e0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -598,22 +598,21 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// free "*tofree". /// /// @return String value or NULL in case of error. -static char *tv_str(typval_T *tvs, int *idxp, char ** const tofree) +static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int idx = *idxp - 1; - char *s = NULL; + const char *s = NULL; if (tvs[idx].v_type == VAR_UNKNOWN) { EMSG(_(e_printf)); } else { (*idxp)++; if (tvs[idx].v_type == VAR_STRING || tvs[idx].v_type == VAR_NUMBER) { - s = (char *)get_tv_string_chk(&tvs[idx]); + s = tv_get_string_chk(&tvs[idx]); *tofree = NULL; } else { - s = encode_tv2echo(&tvs[idx], NULL); - *tofree = s; + s = *tofree = encode_tv2echo(&tvs[idx], NULL); } } return s; @@ -953,7 +952,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 's': case 'S': str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, char *); + : va_arg(ap, const char *); if (!str_arg) { str_arg = "[NULL]"; str_arg_l = 6; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 010ef2c8fa..4d4e8d9bb9 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1140,7 +1140,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, EMSG2(_(e_not_open), file_name); goto theend; } - (void)os_setperm((char_u *)file_name, perm); + (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(_("Writing undo file: %s"), file_name); @@ -1165,7 +1165,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, && os_fileinfo(file_name, &file_info_new) && file_info_old.stat.st_gid != file_info_new.stat.st_gid && os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) { - os_setperm((char_u *)file_name, (perm & 0707) | ((perm & 07) << 3)); + os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) -- cgit From 5df35297f832b3247c18253c916be6066c603739 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:50:50 +0300 Subject: eval: Remove eval_expr() completely --- src/nvim/eval.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3a0075e48a..51ffa8887d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1178,23 +1178,6 @@ int get_spellword(list_T *list, const char **pp) return tv_get_number(&li->li_tv); } -/* - * Top level evaluation function. - * Returns an allocated typval_T with the result. - * Returns NULL when there is an error. - */ -typval_T *eval_expr(char_u *arg, char_u **nextcmd) -{ - typval_T *tv = xmalloc(sizeof(typval_T)); - - if (eval0(arg, tv, nextcmd, TRUE) == FAIL) { - xfree(tv); - return NULL; - } - - return tv; -} - // Call some vimL function and return the result in "*rettv". // Uses argv[argc] for the function arguments. Only Number and String -- cgit From 31a3158d0b574385186ab55c074edf85356d1d6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 03:46:49 +0300 Subject: eval: Make sort always stable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should fix test failures on QB: 20:00:51,837 INFO - not ok 420 - sort() sorts “wrong” values between -0.0001 and 0.0001, preserving order 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua @ 21 20:00:51,837 INFO - # Failure message: test/functional/eval/sort_spec.lua:39: Expected objects to be the same. 20:00:51,837 INFO - # Passed in: 20:00:51,837 INFO - # (string) '[-1.0e-4, v:true, v:false, v:null, function('tr'), {'a': 42}, 'check', [], 1.0e-4]' 20:00:51,837 INFO - # Expected: 20:00:51,837 INFO - # (string) '[-1.0e-4, function('tr'), v:true, v:false, v:null, [], {'a': 42}, 'check', 1.0e-4]' 20:00:51,837 INFO - # stack traceback: 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua:39: in function 20:00:51,837 INFO - # --- src/nvim/eval.c | 58 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 51ffa8887d..2dc1cb50af 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15022,58 +15022,61 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; - char_u *p1; - char_u *p2; - char_u *tofree1 = NULL; - char_u *tofree2 = NULL; - int res; + sortItem_T *const si1 = (sortItem_T *)s1; + sortItem_T *const si2 = (sortItem_T *)s2; - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - typval_T *tv1 = &si1->item->li_tv; - typval_T *tv2 = &si2->item->li_tv; + typval_T *const tv1 = &si1->item->li_tv; + typval_T *const tv2 = &si2->item->li_tv; + + int res; if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); + const long v1 = tv_get_number(tv1); + const long v2 = tv_get_number(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } if (sortinfo->item_compare_float) { const float_T v1 = tv_get_float(tv1); const float_T v2 = tv_get_float(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + // encode_tv2string() puts quotes around a string and allocates memory. Don't // do that for string variables. Use a single quote when comparing with // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = (char_u *)"'"; + p1 = "'"; } else { - p1 = tv1->vval.v_string; + p1 = (char *)tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); + tofree1 = p1 = encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = (char_u *)"'"; + p2 = "'"; } else { - p2 = tv2->vval.v_string; + p2 = (char *)tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); + tofree2 = p2 = encode_tv2string(tv2, NULL); } if (p1 == NULL) { - p1 = (char_u *)""; + p1 = ""; } if (p2 == NULL) { - p2 = (char_u *)""; + p2 = ""; } if (!sortinfo->item_compare_numeric) { if (sortinfo->item_compare_ic) { @@ -15083,19 +15086,20 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } } else { double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; } + xfree(tofree1); + xfree(tofree2); + +item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { res = si1->idx > si2->idx ? 1 : -1; } - - xfree(tofree1); - xfree(tofree2); return res; } -- cgit From 6cc3d59ec8e4d6f32c8c3d9755c625e32512b8e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 04:29:56 +0300 Subject: misc1: Refactor ask_yesno() --- src/nvim/misc1.c | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index d751f13644..0b74b4437e 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2211,39 +2211,44 @@ change_warning ( } } -/* - * Ask for a reply from the user, a 'y' or a 'n'. - * No other characters are accepted, the message is repeated until a valid - * reply is entered or CTRL-C is hit. - * If direct is TRUE, don't use vgetc() but ui_inchar(), don't get characters - * from any buffers but directly from the user. - * - * return the 'y' or 'n' - */ -int ask_yesno(const char *str, bool direct) +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) { - int r = ' '; - int save_State = State; + const int save_State = State; no_wait_return++; - State = CONFIRM; // mouse behaves like with :confirm - setmouse(); // disables mouse for xterm + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. no_mapping++; + int r = ' '; while (r != 'y' && r != 'n') { - /* same highlighting as for wait_return */ - smsg_attr(hl_attr(HLF_R), - "%s (y/n)?", str); - if (direct) + // Same highlighting as for wait_return. + smsg_attr(hl_attr(HLF_R), "%s (y/n)?", str); + if (direct) { r = get_keystroke(); - else + } else { r = plain_vgetc(); - if (r == Ctrl_C || r == ESC) + } + if (r == Ctrl_C || r == ESC) { r = 'n'; - msg_putchar(r); /* show what you typed */ + } + msg_putchar(r); // Show what you typed. ui_flush(); } - --no_wait_return; + no_wait_return--; State = save_State; setmouse(); no_mapping--; -- cgit From c4fe656fef5ade20da4861a2f1e5d0f776b0df8f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 17:56:24 +0300 Subject: typval.h: Allow non-var expressions in TV_DICT_ITER first argument --- src/nvim/eval.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2dc1cb50af..a5b0a65497 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11248,26 +11248,26 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ -static void dict_list(typval_T *argvars, typval_T *rettv, int what) -{ - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void dict_list(typval_T *const tv, typval_T *const rettv, + const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsgf(_(e_dictreq)); return; } - dict_T *const d = argvars[0].vval.v_dict; - if (d == NULL) { + if (tv->vval.v_dict == NULL) { return; } tv_list_alloc_ret(rettv); - TV_DICT_ITER(d, di, { + TV_DICT_ITER(tv->vval.v_dict, di, { listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); -- cgit From 86fc4580b83f20211d7f85566e84a0e1dad0a136 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 18:40:19 +0300 Subject: eval: Fix max_min functions Found two bugs: 1. Multiple unneeded error messages, vim/vim#1039. 2. Unformatted error string, vim/vim#1040. --- src/nvim/eval.c | 76 ++++++++++++++++++++++++-------------------------- src/nvim/eval/typval.h | 4 +-- 2 files changed, 38 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a5b0a65497..c96abfa149 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12463,53 +12463,49 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, 4); } -static void max_min(typval_T *argvars, typval_T *rettv, int domax) +/// Get maximal/minimal number value in a list or dictionary +/// +/// @param[in] tv List or dictionary to work with. If it contains something +/// that is not an integer number (or cannot be coerced to +/// it) error is given. +/// @param[out] rettv Location where result will be saved. Only assigns +/// vval.v_number, type is not touched. Returns zero for +/// empty lists/dictionaries. +/// @param[in] domax Determines whether maximal or minimal value is desired. +static void max_min(const typval_T *const tv, typval_T *const rettv, + const bool domax) + FUNC_ATTR_NONNULL_ALL { - long n = 0; - long i; + varnumber_T n = 0; bool error = false; - if (argvars[0].v_type == VAR_LIST) { - list_T *l; - listitem_T *li; - - l = argvars[0].vval.v_list; - if (l != NULL) { - li = l->lv_first; - if (li != NULL) { - n = tv_get_number_chk(&li->li_tv, &error); - for (;; ) { - li = li->li_next; - if (li == NULL) { - break; - } - i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } + if (tv->v_type == VAR_LIST) { + const list_T *const l = tv->vval.v_list; + if (tv_list_len(l) != 0) { + n = tv_get_number_chk(&l->lv_first->li_tv, &error); + for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; + li = li->li_next) { + const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { + n = i; } } } - } else if (argvars[0].v_type == VAR_DICT) { - dict_T *d; - int first = TRUE; - hashitem_T *hi; - int todo; - - d = argvars[0].vval.v_dict; - if (d != NULL) { - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); - if (first) { - n = i; - first = FALSE; - } else if (domax ? i > n : i < n) - n = i; + } else if (tv->v_type == VAR_DICT) { + if (tv->vval.v_dict != NULL) { + bool first = true; + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + break; } - } + if (first) { + n = i; + first = true; + } else if (domax ? i > n : i < n) { + n = i; + } + }); } } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 42581939e3..3b41314b46 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -279,13 +279,13 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) { if (l == NULL) { return 0; -- cgit From 3a3816c990362e1ad8321e6b4748a33c94d1d797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 21:42:24 +0300 Subject: cmake: Use CMAKE_CURRENT_LIST_DIR and remove vars used only once --- src/nvim/CMakeLists.txt | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e752f5d4df..1c2932ed50 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -22,7 +22,6 @@ set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.genera set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) -set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) @@ -31,10 +30,6 @@ set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) set(FUNCS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/geneval.lua) set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) -set(EVENTS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua) -set(EX_CMDS_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua) -set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) -set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) @@ -112,7 +107,7 @@ set(CONV_SOURCES window.c) foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/src/nvim/${sfile}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() @@ -167,11 +162,11 @@ endfunction() # NVIM_GENERATED_SOURCES: generated source files # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} - "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" + "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) - file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^([.][.]|auto/)") + file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") + if(${d} MATCHES "^[.][.]|auto/") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(f ${sfile} NAME) @@ -239,8 +234,8 @@ list(APPEND NVIM_GENERATED_SOURCES add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} - DEPENDS ${EX_CMDS_GENERATOR} ${EX_CMDS_DEFS_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} + DEPENDS ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua ) if(NOT GPERF_PRG) @@ -248,24 +243,24 @@ if(NOT GPERF_PRG) endif() add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} COMMAND ${LUA_PRG} ${FUNCS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} COMMAND ${GPERF_PRG} - ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} - DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} + ${GENERATED_DIR}/funcs.generated.h.gperf --output-file=${GENERATED_FUNCS} + DEPENDS ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA} ) list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} COMMAND ${LUA_PRG} ${EVENTS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} - DEPENDS ${EVENTS_GENERATOR} ${EVENTS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) add_custom_command(OUTPUT ${GENERATED_OPTIONS} COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_OPTIONS} - DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} + DEPENDS ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) # NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. -- cgit From 40feac6efcc75395bb20da2b028b67a2b6e95bd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:32:14 +0300 Subject: message: Revise maxlen argument in msg_puts_attr_len `attr` argument is enough to forbid putting message in history. Also forbid strings with NUL before `len` just in case (it appears that this does not ever happen). --- src/nvim/message.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 423f5a27e7..1eab9a403b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1562,13 +1562,17 @@ void msg_puts_attr(const char *const s, const int attr) msg_puts_attr_len(s, -1, attr); } -/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). -/// When "maxlen" is -1 there is no maximum length. -/// When "maxlen" is >= 0 the message is not put in the history. -void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) +/// Write a message with highlight attributes +/// +/// @param[in] str NUL-terminated message string. +/// @param[in] len Length of the string or -1. +/// @param[in] attr Highlight attribute. +void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) + FUNC_ATTR_NONNULL_ALL { + assert(len < 0 || memchr(str, 0, len) == NULL); // If redirection is on, also write to the redirection file. - redir_write(str, maxlen); + redir_write(str, len); // Don't print anything when using ":silent cmd". if (msg_silent != 0) { @@ -1576,8 +1580,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) } // if MSG_HIST flag set, add message to history - if ((attr & MSG_HIST) && maxlen < 0) { - add_msg_hist(str, -1, attr); + if (attr & MSG_HIST) { + assert(len < 0); + add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } @@ -1596,9 +1601,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) // different, e.g. for Win32 console) or we just don't know where the // cursor is. if (msg_use_printf()) { - msg_puts_printf(str, maxlen); + msg_puts_printf(str, len); } else { - msg_puts_display((const char_u *)str, maxlen, attr, false); + msg_puts_display((const char_u *)str, len, attr, false); } } -- cgit From 8b0fa64ed3dbd4376cbfa195526ec97d8c944a97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:35:47 +0300 Subject: message: Remove incorrect assertion It was there only to prove that *now* `len` argument is not used to forbid putting message into history (i.e. that Neovim behaviour did not change). After this commit it should be possible to use msg_puts_attr_len with len>=0 to put message into history should new code need this. --- src/nvim/message.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 1eab9a403b..83f2735b50 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1581,7 +1581,6 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) // if MSG_HIST flag set, add message to history if (attr & MSG_HIST) { - assert(len < 0); add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } -- cgit From 2ad4fba46db26d0724106b194f7cb628fb9b02e8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:37:03 +0300 Subject: eval: Move copy_tv to eval/typval --- src/nvim/api/private/helpers.c | 2 +- src/nvim/eval.c | 215 +++++++++++++++++------------------------ src/nvim/eval/typval.c | 86 ++++++++++++++--- src/nvim/move.PVS-Studio.cfg | 10 ++ src/nvim/shada.c | 2 +- 5 files changed, 176 insertions(+), 139 deletions(-) create mode 100644 src/nvim/move.PVS-Studio.cfg (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2c8a56cc1a..b245132ba7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -179,7 +179,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, } // Update the value - copy_tv(&tv, &di->di_tv); + tv_copy(&tv, &di->di_tv); // Clear the temporary variable tv_clear(&tv); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c96abfa149..a2b6d12288 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -494,6 +494,13 @@ typedef enum ASSERT_OTHER, } assert_type_T; +/// Type for dict_list function +typedef enum { + kDictListKeys, ///< List dictionary keys. + kDictListValues, ///< List dictionary values. + kDictListItems, ///< List dictionary contents: [keys, values]. +} DictListType; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -2412,7 +2419,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); } else { tv_clear(&lp->ll_li->li_tv); - copy_tv(&ri->li_tv, &lp->ll_li->li_tv); + tv_copy(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) @@ -2452,7 +2459,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, lp->ll_tv = &di->di_tv; } else { if (watched) { - copy_tv(lp->ll_tv, &oldtv); + tv_copy(lp->ll_tv, &oldtv); } if (op != NULL && *op != '=') { @@ -2465,7 +2472,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, // Assign the value to the variable or list item. if (copy) { - copy_tv(rettv, lp->ll_tv); + tv_copy(rettv, lp->ll_tv); } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; @@ -2926,7 +2933,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) typval_T oldtv; if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); // need to save key because dictitem_remove will free it key = xstrdup((char *)di->di_key); } @@ -2998,7 +3005,7 @@ int do_unlet(char_u *name, int forceit) bool watched = tv_dict_is_watched(dict); if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); } delete_var(ht, hi); @@ -4532,7 +4539,7 @@ eval_index ( rettv->vval.v_list = l; l->lv_refcount++; } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); tv_clear(rettv); *rettv = var1; } @@ -4570,7 +4577,7 @@ eval_index ( return FAIL; } - copy_tv(&item->di_tv, &var1); + tv_copy(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; break; @@ -6298,7 +6305,7 @@ call_func( } if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) { - copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]); } for (int i = 0; i < argcount_in; i++) { argv[i + argv_clear] = argvars_in[i]; @@ -6552,7 +6559,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { tv_list_append_tv(l, &argvars[1]); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else EMSG(_(e_listreq)); @@ -7231,7 +7238,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /* Make a copy of each argument. This is needed to be able to set * v_lock to VAR_FIXED in the copy without changing the original list. */ - copy_tv(&item->li_tv, &argv[argc++]); + tv_copy(&item->li_tv, &argv[argc++]); } if (item == NULL) { @@ -8190,7 +8197,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) item = NULL; tv_list_extend(l1, l2, item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { @@ -8224,7 +8231,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_extend(d1, d2, action); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else { EMSG2(_(e_listdictarg), "extend()"); @@ -8441,7 +8448,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8451,7 +8458,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) int retval = FAIL; int dummy; - copy_tv(tv, &vimvars[VV_VAL].vv_tv); + tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { @@ -8859,13 +8866,13 @@ static void common_function(typval_T *argvars, typval_T *rettv, } int i = 0; for (; i < arg_len; i++) { - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &pt->pt_argv[i++]); + tv_copy(&li->li_tv, &pt->pt_argv[i++]); } } } @@ -9012,10 +9019,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (tv == NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } else - copy_tv(tv, rettv); + if (argvars[2].v_type != VAR_UNKNOWN) { + tv_copy(&argvars[2], rettv); + } + } else { + tv_copy(tv, rettv); + } } /// Returns information about signs placed in a buffer as list of dicts. @@ -9260,7 +9269,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *const v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, 'b', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -9273,7 +9282,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) f_getbufvar_end: if (!done && argvars[2].v_type != VAR_UNKNOWN) { // use the default value - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10094,7 +10103,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10104,7 +10113,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!done && argvars[2].v_type != VAR_UNKNOWN) { - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10315,7 +10324,7 @@ getwinvar ( v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10330,7 +10339,7 @@ getwinvar ( if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { // use the default return value - copy_tv(&argvars[off + 2], rettv); + tv_copy(&argvars[off + 2], rettv); } } @@ -11181,7 +11190,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (l != NULL) { tv_list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } } @@ -11271,31 +11280,36 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - // keys() - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - // values() - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - list_T *const l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - l2->lv_refcount++; - - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = 0; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - - sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - copy_tv(&di->di_tv, &sub_li->li_tv); + switch (what) { + case kDictListKeys: { + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + break; + } + case kDictListValues: { + tv_copy(&di->di_tv, &li->li_tv); + break; + } + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = sub_l; + sub_l->lv_refcount++; + + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = VAR_UNLOCKED; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); + + sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + tv_copy(&di->di_tv, &sub_li->li_tv); + break; + } } }); } @@ -12256,21 +12270,24 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } } else if (type == 2) { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } else if (l != NULL) + // Return matched string. + if (l != NULL) { + tv_copy(&li->li_tv, rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + } else if (l != NULL) { rettv->vval.v_number = idx; - else { - if (type != 0) + } else { + if (type != 0) { rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); - else + } else { rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); + } rettv->vval.v_number += (varnumber_T)(str - expr); } } @@ -15135,8 +15152,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); + tv_copy(&si1->item->li_tv, &argv[0]); + tv_copy(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -18203,7 +18220,7 @@ static int get_var_tv( } ret = FAIL; } else if (rettv != NULL) { - copy_tv(tv, rettv); + tv_copy(tv, rettv); } return ret; @@ -18375,7 +18392,7 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } else { pt->pt_argc = ret_pt->pt_argc; for (i = 0; i < pt->pt_argc; i++) { - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } } } @@ -18852,7 +18869,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (watched) { - copy_tv(&v->di_tv, &oldtv); + tv_copy(&v->di_tv, &oldtv); } tv_clear(&v->di_tv); } else { // Add a new variable. @@ -18877,7 +18894,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - copy_tv(tv, &v->di_tv); + tv_copy(tv, &v->di_tv); } else { v->di_tv = *tv; v->di_tv.v_lock = 0; @@ -18992,56 +19009,6 @@ bool valid_varname(const char *varname) return true; } -/* - * Copy the values from typval_T "from" to typval_T "to". - * When needed allocates string or increases reference count. - * Does not make a copy of a list or dict but copies the reference! - * It is OK for "from" and "to" to point to the same item. This is used to - * make a copy later. - */ -void copy_tv(typval_T *from, typval_T *to) -{ - to->v_type = from->v_type; - to->v_lock = 0; - memmove(&to->vval, &from->vval, sizeof(to->vval)); - switch (from->v_type) { - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_SPECIAL: - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string != NULL) { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) { - func_ref(to->vval.v_string); - } - } - break; - case VAR_PARTIAL: - if (from->vval.v_partial == NULL) { - to->vval.v_partial = NULL; - } else { - to->vval.v_partial = from->vval.v_partial; - (to->vval.v_partial->pt_refcount)++; - } - break; - case VAR_LIST: - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } - break; - case VAR_DICT: - if (from->vval.v_dict != NULL) { - to->vval.v_dict->dv_refcount++; - } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); - break; - } -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -19080,11 +19047,11 @@ int var_item_copy(const vimconv_T *const conv, case VAR_FUNC: case VAR_PARTIAL: case VAR_SPECIAL: - copy_tv(from, to); + tv_copy(from, to); break; case VAR_STRING: if (conv == NULL || conv->vc_type == CONV_NONE) { - copy_tv(from, to); + tv_copy(from, to); } else { to->v_type = VAR_STRING; to->v_lock = 0; @@ -20981,7 +20948,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (addlocal) { // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. - copy_tv(&v->di_tv, &v->di_tv); + tv_copy(&v->di_tv, &v->di_tv); tv_dict_add(&fc->l_vars, v); } else { tv_dict_add(&fc->l_avars, v); @@ -21188,13 +21155,13 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // Make a copy of the a: variables, since we didn't do that above. TV_DICT_ITER(&fc->l_avars, di, { - copy_tv(&di->di_tv, &di->di_tv); + tv_copy(&di->di_tv, &di->di_tv); }); // Make a copy of the a:000 items, since we didn't do that above. for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &li->li_tv); + tv_copy(&li->li_tv, &li->li_tv); } } @@ -21700,7 +21667,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = (const hashitem_T *) iter; } *name = (char *)TV_DICT_HI2DI(hi)->di_key; - copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); while ((size_t)(++hi - hifirst) < hinum) { if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 78eca15fec..8a407a4513 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1,6 +1,8 @@ +#include #include -#include +#include #include +#include #include "nvim/lib/queue.h" #include "nvim/eval/typval.h" @@ -276,7 +278,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, /// Insert VimL value into a list /// /// @param[out] l List to insert to. -/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an /// allocated listitem_T and inserted. /// @param[in] item Item to insert before. If NULL, inserts at the end of the /// list. @@ -285,7 +287,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, { listitem_T *const ni = tv_list_item_alloc(); - copy_tv(tv, &ni->li_tv); + tv_copy(tv, &ni->li_tv); tv_list_insert(l, ni, item); } @@ -313,13 +315,13 @@ void tv_list_append(list_T *const l, listitem_T *const item) /// Append VimL value to the end of list /// /// @param[out] l List to append to. -/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an /// allocated listitem_T. void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - copy_tv(tv, &li->li_tv); + tv_copy(tv, &li->li_tv); tv_list_append(l, li); } @@ -447,7 +449,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, break; } } else { - copy_tv(&item->li_tv, &ni->li_tv); + tv_copy(&item->li_tv, &ni->li_tv); } tv_list_append(copy, ni); } @@ -828,13 +830,13 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, if (newtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); - copy_tv(newtv, &v->di_tv); + tv_copy(newtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } if (oldtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); - copy_tv(oldtv, &v->di_tv); + tv_copy(oldtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } @@ -926,7 +928,7 @@ static dictitem_T *tv_dict_item_copy(dictitem_T *const di) FUNC_ATTR_MALLOC { dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); return new_di; } @@ -1161,7 +1163,7 @@ bool tv_dict_get_callback(dict_T *const d, } typval_T tv; - copy_tv(&di->di_tv, &tv); + tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); tv_clear(&tv); @@ -1338,11 +1340,11 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, } if (watched) { - copy_tv(&di1->di_tv, &oldtv); + tv_copy(&di1->di_tv, &oldtv); } tv_clear(&di1->di_tv); - copy_tv(&di2->di_tv, &di1->di_tv); + tv_copy(&di2->di_tv, &di1->di_tv); if (watched) { tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, @@ -1433,7 +1435,7 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, break; } } else { - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); } if (tv_dict_add(copy, new_di) == FAIL) { tv_dict_item_free(new_di); @@ -1782,6 +1784,64 @@ void tv_free(typval_T *tv) } } +//{{{3 Copy + +/// Copy typval from one location to another +/// +/// When needed allocates string or increases reference count. Does not make +/// a copy of a container, but copies its reference! +/// +/// It is OK for `from` and `to` to point to the same location; this is used to +/// make a copy later. +/// +/// @param[in] from Location to copy from. +/// @param[out] to Location to copy to. +void tv_copy(typval_T *const from, typval_T *const to) +{ + to->v_type = from->v_type; + to->v_lock = VAR_UNLOCKED; + memmove(&to->vval, &from->vval, sizeof(to->vval)); + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: { + break; + } + case VAR_STRING: + case VAR_FUNC: { + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + } + case VAR_PARTIAL: { + if (to->vval.v_partial != NULL) { + to->vval.v_partial->pt_refcount++; + } + break; + } + case VAR_LIST: { + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + } + case VAR_DICT: { + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + break; + } + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/move.PVS-Studio.cfg b/src/nvim/move.PVS-Studio.cfg new file mode 100644 index 0000000000..cb6da32f12 --- /dev/null +++ b/src/nvim/move.PVS-Studio.cfg @@ -0,0 +1,10 @@ + +source-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.c +i-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.i +language=C +skip-cl-exe=yes +preprocessor=gcc +platform=linux64 +lic-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.lic +output-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.log.x +analysis-mode=4 diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 0f04a5e9cf..f65fdaf1c0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2559,7 +2559,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (sd_writer->sd_conv.vc_type != CONV_NONE) { var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); } else { - copy_tv(&vartv, &tgttv); + tv_copy(&vartv, &tgttv); } ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { -- cgit From a32db8ed19c35847373d4a7fd56d7797a5a26897 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:54:13 +0300 Subject: eval/typval: Add missing includes, also add a script to find them Contains unfinished attempt to integrate IWYU (ref #549). To finish it different job should be done, specifically: - Instead of feeding IWYU with modified file a mirror source tree should be created with the help of CMake which will contain modified sources. This solves the problem with IWYU thinking that `*.generated.h` headers should be included in place of `*` headers. - Build IWYU as all other third-party utilities. - Make modified sources avoid problems with `nvim/func_attr.h` includes and various related tricks. Current script may only be used for manual checks like this: ./scripts/check-includes.py \ --generated-includes-dir build/include \ --generated-includes-dir build/src/nvim/auto \ --file src/nvim/eval/typval.c \ -- -Isrc -Ibuild/include -Ibuild/src/nvim/auto \ -DINCLUDE_GENERATED_DECLARATIONS (it is also somewhat fine with `--file src/nvim/eval/typval.h`). I have no idea why (I mean, why developer think that these lines are needed, why they are suggested is pretty obvious: because there is typedef which mentions them before structs are defined), but for typval.h it reports, among other things, that it should add lines struct dictvar_S; struct listitem_S; struct listvar_S; struct listwatch_S; --- src/nvim/eval/typval.c | 5 +++++ src/nvim/eval/typval.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8a407a4513..4777e00a76 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -20,6 +20,11 @@ #include "nvim/ascii.h" #include "nvim/pos.h" #include "nvim/charset.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3b41314b46..791b191009 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -3,9 +3,11 @@ #include #include -#include +#include #include +#include +#include "nvim/types.h" #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" -- cgit From a394167177390a66c275d32e3862c82c546970ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 13:41:11 +0300 Subject: unittests: Test tv_list_item_\* functions To check that memory is free()d correctly. --- src/nvim/eval/typval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4777e00a76..a26afb20c6 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -228,6 +228,7 @@ void tv_list_unref(list_T *const l) /// @param[in] item2 Last item to remove. void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL { // notify watchers for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { -- cgit From 9898f36aa306d2a7f53fbe08c64048260992d3bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Sep 2016 22:06:11 +0300 Subject: unittests: Test tv_list_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also found some bugs: 1. var_item_copy() always fails to copy v:_null_list and v:_null_dict. Fixing this should mean fixing `deepcopy(v:_null_list)` which should’ve been, but was not listed in #4615. This also fixes `deepcopy(v:_null_dict)`. 2. var_item_copy() crashes when trying to copy NULL string with `conv != NULL`. 3. `conv` argument is ignored when copying list unless `deep` is true, but it was not reflected in documentation. 4. `tv_dict_item_alloc_len()` allocated more memory then needed. 5. typvalt2lua was not able to handle self-referencing containers. --- src/nvim/eval.c | 11 +++++++---- src/nvim/eval/typval.c | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a2b6d12288..7c3754607e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19024,7 +19024,7 @@ bool valid_varname(const char *varname) /// list[1]`) var_item_copy with zero copyID will emit /// a copy with (`copy[0] isnot copy[1]`), with non-zero it /// will emit a copy with (`copy[0] is copy[1]`) like in the -/// original list. Not use when deep is false. +/// original list. Not used when deep is false. int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, @@ -19050,7 +19050,8 @@ int var_item_copy(const vimconv_T *const conv, tv_copy(from, to); break; case VAR_STRING: - if (conv == NULL || conv->vc_type == CONV_NONE) { + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { tv_copy(from, to); } else { to->v_type = VAR_STRING; @@ -19075,8 +19076,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } - if (to->vval.v_list == NULL) + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { ret = FAIL; + } break; case VAR_DICT: to->v_type = VAR_DICT; @@ -19090,8 +19092,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } - if (to->vval.v_dict == NULL) + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { ret = FAIL; + } break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a26afb20c6..ae4199697f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -424,6 +424,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) /// Make a copy of list /// /// @param[in] conv If non-NULL, then all internal strings will be converted. +/// Only used when `deep` is true. /// @param[in] orig Original list to copy. /// @param[in] deep If false, then shallow copy will be done. /// @param[in] copyID See var_item_copy(). -- cgit From f80a00469fdba8a3dec0edae30c911d050485055 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 18 Sep 2016 01:59:07 +0300 Subject: eval/typval: Make tv_list_concat handle NULL lists correctly Fixes some FIXMEs in eval/null_spec.lua. --- src/nvim/eval/typval.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ae4199697f..9dd1b62d7d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -498,20 +498,25 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - return FAIL; - } + list_T *l; - // make a copy of the first list. - list_T *const l = tv_list_copy(NULL, l1, false, 0); - if (l == NULL) { + tv->v_type = VAR_LIST; + + if (l1 == NULL && l2 == NULL) { + l = NULL; + } else if (l1 == NULL) { + l = tv_list_copy(NULL, l2, false, 0); + } else { + l = tv_list_copy(NULL, l1, false, 0); + if (l != NULL && l2 != NULL) { + tv_list_extend(l, l2, NULL); + } + } + if (l == NULL && !(l1 == NULL && l2 == NULL)) { return FAIL; } - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - // append all items from the second list - tv_list_extend(l, l2, NULL); + tv->vval.v_list = l; return OK; } -- cgit From 56e4c2f67e54b88f637d30e565313bc6b2c22f29 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 19 Sep 2016 00:50:07 +0300 Subject: unittests: Test tv_list_concat() --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 9dd1b62d7d..1cc195ddc4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -481,7 +481,7 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int todo = l2->lv_len; // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first + for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 ; item = item->li_next) { tv_list_insert_tv(l1, &item->li_tv, bef); -- cgit From 7ceebacb3fad49ba8321397cf839948caa55b3f5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 Sep 2016 00:51:34 +0300 Subject: eval/typval,tests: Fix extending list with itself, add tests Adds unit test for tv_list_extend and regression test for extend() VimL function. --- src/nvim/eval/typval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 1cc195ddc4..746cbbfe1c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -479,11 +479,13 @@ void tv_list_extend(list_T *const l1, list_T *const l2, FUNC_ATTR_NONNULL_ARG(1, 2) { int todo = l2->lv_len; + listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); + listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 - ; item = item->li_next) { + ; item = (item == befbef ? saved_next : item->li_next)) { tv_list_insert_tv(l1, &item->li_tv, bef); } } -- cgit From b3672ae2fced4715963442d2e19048f8fadbe0b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Oct 2016 04:34:30 +0300 Subject: eval/typval: Add tv_list_equal() tests, compare NULL lists equal --- src/nvim/eval/typval.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 746cbbfe1c..b0b95e955f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -623,13 +623,12 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - // FIXME? compare empty list with NULL list equal - return false; - } if (l1 == l2) { return true; } + if (l1 == NULL || l2 == NULL) { + return false; + } if (tv_list_len(l1) != tv_list_len(l2)) { return false; } -- cgit From e5edf07ec44f8d147d7482cae2997be62c30373f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Nov 2016 19:33:36 +0300 Subject: unittests: Add tests for tv_list_find*() functions Additional modifications: - More `const` qualifiers in tested functions. - `tv_list_find_str()` second argument is more in-line with other `tv_list_find*()` functions. --- src/nvim/eval.c | 2 +- src/nvim/eval/typval.c | 12 ++++++------ src/nvim/ex_docmd.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7c3754607e..d5624f354a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21783,7 +21783,7 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - const char *const p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b0b95e955f..ef64467ee5 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -731,7 +731,7 @@ listitem_T *tv_list_find(list_T *const l, int n) /// `*ret_error` is not touched. /// /// @return Integer value at the given index or -1. -varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) FUNC_ATTR_WARN_UNUSED_RESULT { const listitem_T *const li = tv_list_find(l, n); @@ -744,16 +744,16 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) return tv_get_number_chk(&li->li_tv, ret_error); } -/// Get list item l[n - 1] as a string +/// Get list item l[n] as a string /// /// @param[in] l List to index. /// @param[in] n Index in a list. /// -/// @return [allocated] Copy of the list item string value. -const char *tv_list_find_str(list_T *l, int n) - FUNC_ATTR_MALLOC +/// @return List item string value or NULL in case of error. +const char *tv_list_find_str(list_T *const l, const int n) + FUNC_ATTR_WARN_UNUSED_RESULT { - const listitem_T *const li = tv_list_find(l, n - 1); + const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { EMSGN(_(e_listidx), n); return NULL; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 73b81ac2d9..26cfec991f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8424,7 +8424,7 @@ eval_vars ( return NULL; } result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + i - 1); if (result == NULL) { *errormsg = (char_u *)""; return NULL; -- cgit From 56e51033abf00d66e9c6f9412e8f57c9a24b86ae Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:07:34 +0300 Subject: unittests: Add tests for tv_list_idx_of_item --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ef64467ee5..4adc31d10a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -774,7 +774,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) return -1; } long idx = 0; - listitem_T *li; + const listitem_T *li; for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { idx++; } -- cgit From 9ed9af7e11e3a707f65abfeb1d02b029e39241ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:26:33 +0300 Subject: eval/typval: More `const` qualifiers in `tv_dict*` function signatures --- src/nvim/eval/typval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4adc31d10a..57025250d4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1096,7 +1096,7 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, /// @param[in] key Key to find in dictionary. /// /// @return Dictionary item. -varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) +varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { dictitem_T *const di = tv_dict_find(d, key, -1); @@ -1116,7 +1116,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. If returned value is not NULL, it may /// be allocated depending on `save` argument. -char *tv_dict_get_string(dict_T *const d, const char *const key, +char *tv_dict_get_string(const dict_T *const d, const char *const key, const bool save) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -1136,11 +1136,11 @@ char *tv_dict_get_string(dict_T *const d, const char *const key, /// /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. -const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, +const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, char *const numbuf) FUNC_ATTR_WARN_UNUSED_RESULT { - dictitem_T *const di = tv_dict_find(d, key, -1); + const dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { return NULL; } -- cgit From 4bcee963471abd939bb9edd1709418e30be7290f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 01:03:44 +0300 Subject: *: Fix some Windows-specific warnings Also fixed an error in path_fnamecmp(). --- src/nvim/api/buffer.c | 4 ++-- src/nvim/event/rstream.c | 10 ++++++++-- src/nvim/ex_getln.c | 2 +- src/nvim/file_search.c | 5 ++++- src/nvim/fileio.c | 2 +- src/nvim/indent_c.c | 2 +- src/nvim/os/env.c | 1 + src/nvim/os/pty_process_win.h | 5 +++-- src/nvim/path.c | 26 +++++++++++++------------- 9 files changed, 34 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b75a2c7211..037a6ee1da 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -395,10 +395,10 @@ void nvim_buf_set_lines(uint64_t channel_id, mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra); } - changed_lines((linenr_T)start, 0, (linenr_T)end, extra); + changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); if (save_curbuf.br_buf == NULL) { - fix_cursor((linenr_T)start, (linenr_T)end, extra); + fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); } end: diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 92efc9fa2e..2737dad305 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -89,7 +89,10 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { Stream *stream = handle->data; - buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + buf->base = rbuffer_write_ptr(stream->buffer, &write_count); + buf->len = write_count; } // Callback invoked by libuv after it copies the data into the buffer provided @@ -136,7 +139,10 @@ static void fread_idle_cb(uv_idle_t *handle) uv_fs_t req; Stream *stream = handle->data; - stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); + stream->uvbuf.len = write_count; // the offset argument to uv_fs_read is int64_t, could someone really try // to read more than 9 quintillion (9e18) bytes? diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a0981a42ce..e140dfa886 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -579,7 +579,7 @@ static int command_line_execute(VimState *state, int key) } if (vim_ispathsep(ccline.cmdbuff[s->j]) #ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) + && strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) == NULL #endif ) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index f7932bc296..9592235905 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -322,8 +322,11 @@ vim_findfile_init ( drive[0] = path[0]; drive[1] = ':'; drive[2] = NUL; - if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) + if (vim_FullName((const char *)drive, (char *)ff_expand_buffer, MAXPATHL, + true) + == FAIL) { goto error_return; + } path += 2; } else #endif diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 127efda65c..bd632b2755 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5207,7 +5207,7 @@ void forward_slash(char_u *fname) { char_u *p; - if (path_with_url(fname)) { + if (path_with_url((const char *)fname)) { return; } for (p = fname; *p != NUL; p++) { diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 7b758b4dac..4a73fbaf61 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -174,7 +174,7 @@ static char_u *skip_string(char_u *p) char_u *paren = vim_strchr(delim, '('); if (paren != NULL) { - ptrdiff_t delim_len = paren - delim; + const ptrdiff_t delim_len = paren - delim; for (p += 3; *p; ++p) if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..ae69462055 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,6 +18,7 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" +#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 20cc589925..8e2b37a1c1 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,8 +12,9 @@ typedef struct pty_process { #define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) #define pty_process_close(job) libuv_process_close((LibuvProcess *)job) #define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) -#define pty_process_teardown(loop) +#define pty_process_resize(job, width, height) ( \ + (void)job, (void)width, (void)height, 0) +#define pty_process_teardown(loop) ((void)loop, 0) static inline PtyProcess pty_process_init(Loop *loop, void *data) { diff --git a/src/nvim/path.c b/src/nvim/path.c index e92261f4fd..d0248690d9 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -312,7 +312,7 @@ int path_fnamecmp(const char *fname1, const char *fname2) /// /// @return 0 if they are equal, non-zero otherwise. int path_fnamencmp(const char *const fname1, const char *const fname2, - const size_t len) + size_t len) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME @@ -322,16 +322,16 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, const char *p1 = fname1; const char *p2 = fname2; while (len > 0) { - c1 = PTR2CHAR(p1); - c2 = PTR2CHAR(p2); - if (c1 == NUL || c2 == NUL - || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) - || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { + c1 = PTR2CHAR((const char_u *)p1); + c2 = PTR2CHAR((const char_u *)p2); + if ((c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/')))) + && (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; } - len -= MB_PTR2LEN(p1); - p1 += MB_PTR2LEN(p1); - p2 += MB_PTR2LEN(p2); + len -= MB_PTR2LEN((const char_u *)p1); + p1 += MB_PTR2LEN((const char_u *)p1); + p2 += MB_PTR2LEN((const char_u *)p2); } return c1 - c2; #else @@ -819,7 +819,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); - buf[len] = PATHSEP; + buf[len] = (char_u)PATHSEP; simplify_filename(buf); } @@ -1333,12 +1333,12 @@ static int expand_backtick( /// When the path looks like a URL leave it unmodified. void slash_adjust(char_u *p) { - if (path_with_url(p)) { + if (path_with_url((const char *)p)) { return; } while (*p) { - if (*p == psepcN) { - *p = psepc; + if (*p == (char_u)psepcN) { + *p = (char_u)psepc; } mb_ptr_adv(p); } -- cgit From 1e3e302dc2bdced563ecd2c3fb101af31f72b3df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 21:58:19 +0300 Subject: eval: Move part of dictwatcher* functions to eval/typval --- src/nvim/eval.c | 57 ++++++-------------------------- src/nvim/eval/typval.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/nvim/eval/typval.h | 27 +++++++-------- src/nvim/os/env.c | 1 - 4 files changed, 111 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5624f354a..80278cf3bb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7706,6 +7706,11 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_DICT) { emsgf(_(e_invarg2), "dict"); return; + } else if (argvars[0].vval.v_dict == NULL) { + const char *const arg_errmsg = _("dictwatcheradd() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + emsgf(_(e_readonlyvar), (int)arg_errmsg_len, arg_errmsg); + return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { @@ -7725,11 +7730,8 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); - watcher->callback = callback; - watcher->busy = false; - QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); + tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len, + callback); } // dictwatcherdel(dict, key, funcref) function @@ -7759,28 +7761,12 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - dict_T *dict = argvars[0].vval.v_dict; - QUEUE *w = NULL; - DictWatcher *watcher = NULL; - bool matched = false; - QUEUE_FOREACH(w, &dict->watchers) { - watcher = tv_dict_watcher_node_data(w); - if (callback_equal(&watcher->callback, &callback) - && !strcmp(watcher->key_pattern, key_pattern)) { - matched = true; - break; - } - } - - callback_free(&callback); - - if (!matched) { + if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern, + strlen(key_pattern), callback)) { EMSG("Couldn't find a watcher matching key and callback"); - return; } - QUEUE_REMOVE(w); - tv_dict_watcher_free(watcher); + callback_free(&callback); } /* @@ -16576,7 +16562,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } - /// Unref/free callback void callback_free(Callback *const callback) FUNC_ATTR_NONNULL_ALL @@ -16601,28 +16586,6 @@ void callback_free(Callback *const callback) callback->type = kCallbackNone; } -static bool callback_equal(Callback *cb1, Callback *cb2) -{ - if (cb1->type != cb2->type) { - return false; - } - switch (cb1->type) { - case kCallbackFuncref: - return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; - - case kCallbackPartial: - // FIXME: this is inconsistent with tv_equal but is needed for precision - // maybe change dictwatcheradd to return a watcher id instead? - return cb1->data.partial == cb2->data.partial; - - case kCallbackNone: - return true; - - default: - abort(); - } -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 57025250d4..243e738c50 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -790,7 +790,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) /// Perform all necessary cleanup for a `DictWatcher` instance /// /// @param watcher Watcher to free. -void tv_dict_watcher_free(DictWatcher *watcher) +static void tv_dict_watcher_free(DictWatcher *watcher) FUNC_ATTR_NONNULL_ALL { callback_free(&watcher->callback); @@ -798,6 +798,91 @@ void tv_dict_watcher_free(DictWatcher *watcher) xfree(watcher); } +/// Add watcher to a dictionary +/// +/// @param[in] dict Dictionary to add watcher to. +/// @param[in] key_pattern Pattern to watch for. +/// @param[in] key_pattern_len Key pattern length. +/// @param callback Function to be called on events. +void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); + watcher->key_pattern_len = key_pattern_len; + watcher->callback = callback; + watcher->busy = false; + QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node); +} + +/// Check whether two callbacks are equal +/// +/// @param[in] cb1 First callback to check. +/// @param[in] cb2 Second callback to check. +/// +/// @return True if they are equal, false otherwise. +static bool tv_callback_equal(const Callback *const cb1, + const Callback *const cb2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (cb1->type != cb2->type) { + return false; + } + switch (cb1->type) { + case kCallbackFuncref: { + return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + } + case kCallbackPartial: { + // FIXME: this is inconsistent with tv_equal but is needed for precision + // maybe change dictwatcheradd to return a watcher id instead? + return cb1->data.partial == cb2->data.partial; + } + case kCallbackNone: { + return true; + } + } +} + +/// Remove watcher from a dictionary +/// +/// @param dict Dictionary to remove watcher from. +/// @param[in] key_pattern Pattern to remove watcher for. +/// @param[in] key_pattern_len Pattern length. +/// @param callback Callback to remove watcher for. +/// +/// @return True on success, false if relevant watcher was not found. +bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, + Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (dict == NULL) { + return false; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + bool matched = false; + QUEUE_FOREACH(w, &dict->watchers) { + watcher = tv_dict_watcher_node_data(w); + if (tv_callback_equal(&watcher->callback, &callback) + && watcher->key_pattern_len == key_pattern_len + && memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) { + matched = true; + break; + } + } + + if (!matched) { + return false; + } + + QUEUE_REMOVE(w); + tv_dict_watcher_free(watcher); + return true; +} + /// Test if `key` matches with with `watcher->key_pattern` /// /// @param[in] watcher Watcher to check key pattern from. @@ -810,7 +895,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // For now only allow very simple globbing in key patterns: a '*' at the end // of the string means it should match everything up to the '*' instead of the // whole string. - const size_t len = strlen(watcher->key_pattern); + const size_t len = watcher->key_pattern_len; if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; } else { diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 791b191009..fa0105197f 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -59,6 +59,7 @@ typedef struct { typedef struct dict_watcher { Callback callback; char *key_pattern; + size_t key_pattern_len; QUEUE node; bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; @@ -322,19 +323,6 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE - REAL_FATTR_WARN_UNUSED_RESULT; - -/// Compute the `DictWatcher` address from a QUEUE node. -/// -/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer -/// arithmetic). -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) -{ - return QUEUE_DATA(q, DictWatcher, node); -} - /// Initialize VimL object /// /// Initializes to unlocked VAR_UNKNOWN object. @@ -407,6 +395,19 @@ static inline bool tv_get_float_chk(const typval_T *const tv, return false; } +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index ae69462055..a73d753e46 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,7 +18,6 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" -#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 -- cgit From a56f2d27e3c09aaae00a58a70652ac5db3287dee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:35:10 +0300 Subject: eval: Make dictionary watchers work with empty keys Looks like dict_notifications_spec test used to depend on some state which should not be preserved. Changed all `setup()` calls to `before_each()` and added necessary state in addition to changes required to test empty keys. Note: unit tests for tv_dict_watcher* are still needed. --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80278cf3bb..bd88678f16 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -160,7 +160,6 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( "E712: Argument of %s must be a List or Dictionary"); -static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); static char *e_stringreq = N_("E928: String required"); @@ -2112,8 +2111,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) ; if (len == 0) { - if (!quiet) - EMSG(_(e_emptykey)); + if (!quiet) { + EMSG(_("E713: Cannot use empty key after .")); + } return NULL; } p = key + len; -- cgit From 3025431c81daac873e69a71aee695ebfd00504f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:59:25 +0300 Subject: eval: Make sure that v:_null_dict does not crash dictwatcher*() Ref #4615 --- src/nvim/eval/typval.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 243e738c50..8c11f4bd8f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -808,6 +808,9 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, const size_t key_pattern_len, Callback callback) FUNC_ATTR_NONNULL_ARG(2) { + if (dict == NULL) { + return; + } DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->key_pattern_len = key_pattern_len; -- cgit From 506b938947b7083fc8bef20eabc08ed033298add Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 Feb 2017 00:12:57 +0300 Subject: *: Make some more things const and with length --- src/nvim/buffer.c | 2 +- src/nvim/eval.c | 197 +++++++++++++++++++++++++------------------------ src/nvim/eval/typval.c | 2 +- src/nvim/ex_cmds2.c | 6 +- src/nvim/option.c | 2 +- src/nvim/syntax.c | 19 ++--- 6 files changed, 116 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0fb9a21354..c9101c5b53 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3504,7 +3504,7 @@ int build_stl_str_hl( curbuf = o_curbuf; // Remove the variable we just stored - do_unlet((char_u *)"g:actual_curbuf", true); + do_unlet(S_LEN("g:actual_curbuf"), true); // } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bd88678f16..67f6c8ba3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -137,25 +137,24 @@ * "newkey" is the key for the new item. */ typedef struct lval_S { - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ + const char *ll_name; ///< Start of variable name (can be NULL). + size_t ll_name_len; ///< Length of the .ll_name. + char *ll_exp_name; ///< NULL or expanded name in allocated memory. + typval_T *ll_tv; ///< Typeval of item being used. If "newkey" + ///< isn't NULL it's the Dict to which to add the item. + listitem_T *ll_li; ///< The list item or NULL. + list_T *ll_list; ///< The list or NULL. + int ll_range; ///< TRUE when a [i:j] range was used. + long ll_n1; ///< First index for list. + long ll_n2; ///< Second index for list range. + int ll_empty2; ///< Second index is empty: [i:]. + dict_T *ll_dict; ///< The Dictionary or NULL. + dictitem_T *ll_di; ///< The dictitem or NULL. + char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL. } lval_T; static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( @@ -729,7 +728,7 @@ void set_internal_string_var(char_u *name, char_u *value) .vval.v_string = value, }; - set_var((const char *)name, (typval_T *)&tv, true); + set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -768,8 +767,8 @@ var_redir_start ( // Parse the variable name (can be a dict or list entry). redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, 0, FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != - NUL) { + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { clear_lval(redir_lval); if (redir_endp != NULL && *redir_endp != NUL) /* Trailing characters are present after the variable name */ @@ -2021,15 +2020,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, const int flags, const int fne_flags) FUNC_ATTR_NONNULL_ARG(1, 3) { - char_u *p; - int cc; dictitem_T *v; typval_T var1; typval_T var2; int empty1 = FALSE; listitem_T *ni; - char_u *key = NULL; - int len; hashtab_T *ht; int quiet = flags & GLV_QUIET; @@ -2038,16 +2033,18 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (skip) { // When skipping just find the end of the name. - lp->ll_name = (char_u *)name; - return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + lp->ll_name = (const char *)name; + return (char_u *)find_name_end((const char_u *)name, NULL, NULL, + FNE_INCL_BR | fne_flags); } // Find the end of the name. char_u *expr_start; char_u *expr_end; - p = (char_u *)find_name_end(name, - (const char_u **)&expr_start, - (const char_u **)&expr_end, fne_flags); + char_u *p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, + fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2056,7 +2053,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, + (char_u *)p); if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2068,22 +2066,22 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; + lp->ll_name_len = strlen(lp->ll_name); } else { - lp->ll_name = (char_u *)name; + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); } - /* Without [idx] or .key we are done. */ - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { return p; + } - cc = *p; - *p = NUL; - v = find_var((const char *)lp->ll_name, STRLEN(lp->ll_name), &ht, - flags & GLV_NO_AUTOLOAD); + v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD); if (v == NULL && !quiet) { - EMSG2(_(e_undefvar), lp->ll_name); + emsgf(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); } - *p = cc; if (v == NULL) return NULL; @@ -2105,11 +2103,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - len = -1; + int len = -1; + char_u *key; if (*p == '.') { key = p + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { + } if (len == 0) { if (!quiet) { EMSG(_("E713: Cannot use empty key after .")); @@ -2380,12 +2379,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var((const char *)lp->ll_name, &tv, false); + set_var(lp->ll_name, lp->ll_name_len, &tv, false); } tv_clear(&tv); } } else { - set_var((const char *)lp->ll_name, rettv, copy); + set_var(lp->ll_name, lp->ll_name_len, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2886,17 +2885,17 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) *name_end = NUL; // Normal name or expanded name. - if (do_unlet(lp->ll_name, forceit) == FAIL) { + if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { ret = FAIL; } *name_end = cc; } else if ((lp->ll_list != NULL && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) + lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name)))) { + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2906,7 +2905,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) { + lp->ll_name_len)) { return false; } ll_li = li; @@ -2953,22 +2952,22 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) // TODO(ZyX-I): move to eval/ex_cmds -/* - * "unlet" a variable. Return OK if it existed, FAIL if not. - * When "forceit" is TRUE don't complain if the variable doesn't exist. - */ -int do_unlet(char_u *name, int forceit) +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] fonceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const int forceit) + FUNC_ATTR_NONNULL_ALL { - hashtab_T *ht; - hashitem_T *hi; - char_u *varname; - dict_T *d; - dictitem_T *di; + const char *varname; dict_T *dict; - ht = find_var_ht_dict((const char *)name, STRLEN(name), - (const char **)&varname, &dict); + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); if (ht != NULL && *varname != NUL) { + dict_T *d; if (ht == &globvarht) { d = &globvardict; } else if (current_funccal != NULL @@ -2977,19 +2976,19 @@ int do_unlet(char_u *name, int forceit) } else if (ht == &compat_hashtab) { d = &vimvardict; } else { - di = find_var_in_ht(ht, *name, "", 0, false); + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); d = di->di_tv.vval.v_dict; } if (d == NULL) { EMSG2(_(e_intern2), "do_unlet()"); return FAIL; } - hi = hash_find(ht, varname); + hashitem_T *hi = hash_find(ht, (const char_u *)varname); if (HASHITEM_EMPTY(hi)) { hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = TV_DICT_HI2DI(hi); + dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3011,7 +3010,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3041,9 +3040,8 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, if (lp->ll_tv == NULL) { // Normal name or expanded name. - const size_t name_len = (size_t)(name_end - lp->ll_name); dictitem_T *const di = find_var( - (const char *)lp->ll_name, name_len, NULL, + (const char *)lp->ll_name, lp->ll_name_len, NULL, true); if (di == NULL) { ret = FAIL; @@ -6561,8 +6559,9 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } - } else + } else { EMSG(_(e_listreq)); + } } /* @@ -11216,7 +11215,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_trailing)); } else { if (lv.ll_tv == NULL) { - di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); + di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true); if (di != NULL) { // Consider a variable locked when: // 1. the variable itself is locked @@ -14351,7 +14350,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varp, true); + set_var(bufvarname, varname_len + 2, varp, true); xfree(bufvarname); curbuf = save_curbuf; } @@ -14876,7 +14875,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *const tabvarname = xmalloc(varname_len + 3); memcpy(tabvarname, "t:", 2); memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varp, true); + set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); // Restore current tabpage. @@ -14944,7 +14943,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) char *const winvarname = xmalloc(varname_len + 3); memcpy(winvarname, "w:", 2); memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varp, true); + set_var(winvarname, varname_len + 2, varp, true); xfree(winvarname); } } @@ -18766,16 +18765,17 @@ static void list_one_var_a(const char *prefix, const char *name, /// is created. /// /// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. /// @param tv Variable value. /// @param[in] copy True if value in tv is to be copied. -static void set_var(const char *name, typval_T *const tv, const bool copy) +static void set_var(const char *name, const size_t name_len, typval_T *const tv, + const bool copy) FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; dict_T *dict; - const size_t name_len = strlen(name); const char *varname; ht = find_var_ht_dict(name, name_len, &varname, &dict); const bool watched = tv_dict_is_watched(dict); @@ -18799,8 +18799,8 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value - if (var_check_ro(v->di_flags, (const char *)name, name_len) - || tv_check_lock(v->di_tv.v_lock, (const char *)name, name_len)) { + if (var_check_ro(v->di_flags, name, name_len) + || tv_check_lock(v->di_tv.v_lock, name, name_len)) { return; } @@ -19852,7 +19852,6 @@ trans_function_name( const char_u *start; const char_u *end; int lead; - char_u sid_buf[20]; int len; lval_T lv; @@ -19938,10 +19937,10 @@ trans_function_name( /* Check if the name is a Funcref. If so, use the value. */ if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name((const char *)lv.ll_exp_name, &len, partial, + len = (int)strlen(lv.ll_exp_name); + name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) { + if ((const char *)name == lv.ll_exp_name) { name = NULL; } } else if (!(flags & TFN_NO_DEREF)) { @@ -19966,12 +19965,13 @@ trans_function_name( } if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); + len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) { + && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { /* When there was "s:" already or the name expanded to get a * leading "s:" then remove it. */ lv.ll_name += 2; + lv.ll_name_len -= 2; len -= 2; lead = 2; } @@ -19979,38 +19979,40 @@ trans_function_name( // Skip over "s:" and "g:". if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) { lv.ll_name += 2; + lv.ll_name_len -= 2; } - len = (int)(end - lv.ll_name); + len = (int)((const char *)end - lv.ll_name); } - /* - * Copy the function name to allocated memory. - * Accept name() inside a script, translate into 123_name(). - * Accept 123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) { + size_t sid_buf_len = 0; + char sid_buf[20]; + + // Copy the function name to allocated memory. + // Accept name() inside a script, translate into 123_name(). + // Accept 123_name() outside a script. + if (skip) { + lead = 0; // do nothing + } else if (lead > 0) { lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid((const char *)lv.ll_exp_name)) + if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid((const char *)(*pp))) { // It's "s:" or "". if (current_SID <= 0) { EMSG(_(e_usingsid)); goto theend; } - sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID); - lead += (int)STRLEN(sid_buf); + sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + lead += sid_buf_len; } } else if (!(flags & TFN_INT) - && builtin_function((const char *)lv.ll_name, len)) { + && builtin_function(lv.ll_name, lv.ll_name_len)) { EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), start); goto theend; } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) { - char_u *cp = vim_strchr(lv.ll_name, ':'); + char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); if (cp != NULL && cp < end) { EMSG2(_("E884: Function name cannot contain a colon: %s"), start); @@ -20023,10 +20025,11 @@ trans_function_name( name[0] = K_SPECIAL; name[1] = KS_EXTRA; name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "" */ - STRCPY(name + 3, sid_buf); + if (sid_buf_len > 0) { // If it's "" + memcpy(name + 3, sid_buf, sid_buf_len); + } } - memmove(name + lead, lv.ll_name, (size_t)len); + memmove(name + lead, lv.ll_name, len); name[lead + len] = NUL; *pp = (char_u *)end; @@ -21646,7 +21649,7 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var(name, &vartv, false); + set_var(name, strlen(name), &vartv, false); current_funccal = saved_current_funccal; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8c11f4bd8f..2f3415a59d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2041,7 +2041,7 @@ bool tv_islocked(const typval_T *const tv) /// /// @param[in] lock Lock status. /// @param[in] name Variable name, used in the error message. -/// @param[in] use_gettext True if variable name also is to be translated. +/// @param[in] name_len Variable name length. /// /// @return true if variable is locked, false otherwise. bool tv_check_lock(const VarLockStatus lock, const char *const name, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index b84834d351..213641667d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2256,8 +2256,8 @@ void ex_compiler(exarg_T *eap) } do_cmdline_cmd("command -nargs=* CompilerSet setlocal "); } - do_unlet((char_u *)"g:current_compiler", true); - do_unlet((char_u *)"b:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); + do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); if (source_runtime(buf, DIP_ALL) == FAIL) { @@ -2280,7 +2280,7 @@ void ex_compiler(exarg_T *eap) old_cur_comp); xfree(old_cur_comp); } else { - do_unlet((char_u *)"g:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); } } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 8a80c46cf2..9b31e14ea7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2582,7 +2582,7 @@ did_set_string_option ( // The color scheme must have set 'background' back to another // value, that's not what we want here. Disable the color // scheme and set the colors again. - do_unlet((char_u *)"g:colors_name", true); + do_unlet(S_LEN("g:colors_name"), true); free_string_option(p_bg); p_bg = vim_strsave((char_u *)(dark ? "dark" : "light")); check_string_option(&p_bg); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 3f84b8080f..c0adfb10fc 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3259,9 +3259,10 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) syntax_sync_clear(); else { syntax_clear(curwin->w_s); - if (curwin->w_s == &curwin->w_buffer->b_s) - do_unlet((char_u *)"b:current_syntax", TRUE); - do_unlet((char_u *)"w:current_syntax", TRUE); + if (curwin->w_s == &curwin->w_buffer->b_s) { + do_unlet(S_LEN("b:current_syntax"), true); + } + do_unlet(S_LEN("w:current_syntax"), true); } } else { /* @@ -3337,7 +3338,7 @@ static void syn_cmd_enable(exarg_T *eap, int syncing) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); syn_cmd_onoff(eap, "syntax"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } /* @@ -3350,7 +3351,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) if (!eap->skip) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); do_cmdline_cmd("runtime! syntax/syncolor.vim"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } } @@ -5538,9 +5539,9 @@ void ex_ownsyntax(exarg_T *eap) } /* restore value of b:current_syntax */ - if (old_value == NULL) - do_unlet((char_u *)"b:current_syntax", TRUE); - else { + if (old_value == NULL) { + do_unlet(S_LEN("b:current_syntax"), true); + } else { set_internal_string_var((char_u *)"b:current_syntax", old_value); xfree(old_value); } @@ -6231,7 +6232,7 @@ do_highlight ( */ line = linep; if (ends_excmd(*line)) { - do_unlet((char_u *)"colors_name", TRUE); + do_unlet(S_LEN("colors_name"), true); restore_cterm_colors(); /* -- cgit From 6aa6e5007596f1eaa23c45376e3a72d908c6688e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 21:35:23 +0300 Subject: eval: Fix linter errors --- src/nvim/eval.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67f6c8ba3a..937b56179d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2082,8 +2082,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, emsgf(_("E121: Undefined variable: %.*s"), (int)lp->ll_name_len, lp->ll_name); } - if (v == NULL) + if (v == NULL) { return NULL; + } /* * Loop until no more [idx] or .key is following. @@ -13431,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - strncat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len); xfree(p); p = cpy; @@ -19968,8 +19969,8 @@ trans_function_name( len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ + // When there was "s:" already or the name expanded to get a + // leading "s:" then remove it. lv.ll_name += 2; lv.ll_name_len -= 2; len -= 2; -- cgit From 78a0de2c1b722d5445dfc44cc0635e5ce4bcd23e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:10 +0300 Subject: eval/typval: Fix -Werror=return-type --- src/nvim/eval/typval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2f3415a59d..326334f149 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -845,6 +845,8 @@ static bool tv_callback_equal(const Callback *const cb1, return true; } } + assert(false); + return false; } /// Remove watcher from a dictionary -- cgit From c6c48e8672ab6adbf78453e2b133fe3bd2ee1eb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:42 +0300 Subject: syntax: Fix linter error --- src/nvim/syntax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index c0adfb10fc..a54e36a609 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5538,7 +5538,7 @@ void ex_ownsyntax(exarg_T *eap) set_internal_string_var((char_u *)"w:current_syntax", new_value); } - /* restore value of b:current_syntax */ + // Restore value of b:current_syntax. if (old_value == NULL) { do_unlet(S_LEN("b:current_syntax"), true); } else { -- cgit From 2c8ad276523489223d2d804075a19367280b0f10 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:14:11 +0300 Subject: ascii: Readd DEL_STR define --- src/nvim/ascii.h | 1 + src/nvim/tui/tui.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 37b83efb61..adde91f9ec 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -26,6 +26,7 @@ #define ESC '\033' #define ESC_STR "\033" #define DEL 0x7f +#define DEL_STR "\177" #define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 9fbbe8be92..55936ad58d 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1015,7 +1015,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, ILOG("libtermkey:kdch1=%s", value); // Vim: "If and are now the same, redefine ." if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { - return stty_erase[0] == DEL ? (char *)CTRL_H_STR : (char *)DEL_STR; + return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } -- cgit From cdb1aa3e47cb0ec19d2ae597c1d21b7e892d0d7e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:47:37 +0300 Subject: eval: Fix len argument to xstrlcat --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 937b56179d..cab9d07e56 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13432,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len + 1); xfree(p); p = cpy; -- cgit From faddd83db8a71623e78a4d919b2bb55e6a58439d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:37:16 +0300 Subject: eval: Fix SEGV in test49 --- src/nvim/eval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab9d07e56..882b2dfa74 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2055,6 +2055,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, (char_u *)p); + lp->ll_name = lp->ll_exp_name; if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2064,9 +2065,10 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, EMSG2(_(e_invarg2), name); return NULL; } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); } - lp->ll_name = lp->ll_exp_name; - lp->ll_name_len = strlen(lp->ll_name); } else { lp->ll_name = (const char *)name; lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); -- cgit From 38dd81c136c2edbda66614622a4747bf1785af94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:15:17 +0300 Subject: eval/typval: Fix SEGV in test_alot.vim test --- src/nvim/eval/typval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 326334f149..8d1ecf8f14 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1167,8 +1167,11 @@ void tv_dict_unref(dict_T *const d) /// @return found item or NULL if nothing was found. dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptrdiff_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + if (d == NULL) { + return NULL; + } hashitem_T *const hi = (len < 0 ? hash_find(&d->dv_hashtab, (const char_u *)key) : hash_find_len(&d->dv_hashtab, key, (size_t)len)); -- cgit From 3bf87a5a6b0e8b2e81534081196c20dddc45474b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:51:44 +0300 Subject: eval: Do not use S_LEN as snprintf argument --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 882b2dfa74..88ba0b95cc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20004,7 +20004,8 @@ trans_function_name( EMSG(_(e_usingsid)); goto theend; } - sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + sid_buf_len = snprintf(sid_buf, sizeof(sid_buf), + "%" PRIdSCID "_", current_SID); lead += sid_buf_len; } } else if (!(flags & TFN_INT) -- cgit From f830243ff7b78b9bb41e72fd602b99c2893b5437 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 03:10:34 +0300 Subject: mbyte: Include os_defs.h in mbyte.h --- src/nvim/mbyte.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index c20e6d47ff..3565202466 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -6,6 +6,7 @@ #include "nvim/iconv.h" #include "nvim/func_attr.h" +#include "nvim/os/os_defs.h" // For WCHAR, indirect /* * Return byte length of character that starts with byte "b". -- cgit From b222453c95dbe466b79abd578642fa6038770a55 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:38 +0300 Subject: eval/typval: Refactor errors a bit: use emsgf always --- src/nvim/eval/typval.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8d1ecf8f14..867330c137 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -755,7 +755,7 @@ const char *tv_list_find_str(list_T *const l, const int n) { const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { - EMSGN(_(e_listidx), n); + emsgf(_(e_listidx), (int64_t)n); return NULL; } return tv_get_string(&li->li_tv); @@ -1264,14 +1264,14 @@ bool tv_dict_get_callback(dict_T *const d, if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); + emsgf(_("E6000: Argument is not a function or function name")); return false; } typval_T tv; tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); + const bool res = callback_from_typval(result, &tv); tv_clear(&tv); return res; } @@ -1942,7 +1942,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + emsgf(_(e_intern2), "tv_copy(UNKNOWN)"); break; } } @@ -2259,7 +2259,7 @@ bool tv_check_num(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); return false; } } @@ -2303,7 +2303,7 @@ bool tv_check_str(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } @@ -2350,7 +2350,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); break; } case VAR_NUMBER: { @@ -2378,7 +2378,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); break; } } @@ -2428,27 +2428,27 @@ float_T tv_get_float(const typval_T *const tv) } case VAR_PARTIAL: case VAR_FUNC: { - EMSG(_("E891: Using a Funcref as a Float")); + emsgf(_("E891: Using a Funcref as a Float")); break; } case VAR_STRING: { - EMSG(_("E892: Using a String as a Float")); + emsgf(_("E892: Using a String as a Float")); break; } case VAR_LIST: { - EMSG(_("E893: Using a List as a Float")); + emsgf(_("E893: Using a List as a Float")); break; } case VAR_DICT: { - EMSG(_("E894: Using a Dictionary as a Float")); + emsgf(_("E894: Using a Dictionary as a Float")); break; } case VAR_SPECIAL: { - EMSG(_("E907: Using a special value as a Float")); + emsgf(_("E907: Using a special value as a Float")); break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } @@ -2490,7 +2490,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } -- cgit From bc87d23c28c10c70b4addaf18ae16bcbe0682c8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:57 +0300 Subject: unittests: Add tests for dictionary indexing --- src/nvim/vim.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/vim.h b/src/nvim/vim.h index e16ee00309..cc0587fb88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -30,7 +30,7 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) -#define NUMBUFLEN 65 +enum { NUMBUFLEN = 65 }; // flags for vim_str2nr() #define STR2NR_BIN 1 -- cgit From 270a3889af024485fa7b63f34c4dd3f92f6e0f98 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 01:45:44 +0300 Subject: unittests: Add tv_dict_add* unit tests Also fixes incorrect location of `tv_dict_add` function and three bugs in other functions: 1. `tv_dict_add_list` may free list it does not own (vim/vim#1555). 2. `tv_dict_add_dict` may free dictionary it does not own (vim/vim#1555). 3. `tv_dict_add_dict` ignores `key_len` argument. --- src/nvim/eval/typval.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 867330c137..d9feb2d88e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1009,18 +1009,6 @@ void tv_dict_item_free(dictitem_T *const item) } } -/// Add item to dictionary -/// -/// @param[out] d Dictionary to add to. -/// @param[in] item Item to add. -/// -/// @return FAIL if key already exists. -int tv_dict_add(dict_T *const d, dictitem_T *const item) - FUNC_ATTR_NONNULL_ALL -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - /// Make a copy of a dictionary item /// /// @param[in] di Item to copy. @@ -1278,6 +1266,18 @@ bool tv_dict_get_callback(dict_T *const d, //{{{2 dict_add* +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +int tv_dict_add(dict_T *const d, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + /// Add a list entry to dictionary /// /// @param[out] d Dictionary to add entry to. @@ -1295,11 +1295,11 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; + list->lv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - list->lv_refcount++; return OK; } @@ -1313,18 +1313,18 @@ int tv_dict_add_list(dict_T *const d, const char *const key, /// @return OK in case of success, FAIL when key already exists. int tv_dict_add_dict(dict_T *const d, const char *const key, const size_t key_len, dict_T *const dict) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL { - dictitem_T *const item = tv_dict_item_alloc(key); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; + dict->dv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - dict->dv_refcount++; return OK; } -- cgit From 43e9fad1c8835a3136f4db53c82608e034df2a5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 13:43:01 +0300 Subject: eval: Use tv_is_func in place of ==VAR_FUNC||==VAR_PARTIAL Also fixes same error as in vim/vim#1557 --- src/nvim/eval.c | 25 ++++++++----------------- src/nvim/eval/typval.c | 9 +++------ src/nvim/eval/typval.h | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 88ba0b95cc..cab22c599a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2225,7 +2225,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, prevval = 0; // Avoid compiler warning. } wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC + && tv_is_func(*rettv) && !var_check_func_name((const char *)key, lp->ll_di == NULL)) || !valid_varname((const char *)key)); if (len != -1) { @@ -3643,9 +3643,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) n1 = !n1; } } - } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL - || var2.v_type == VAR_PARTIAL) { + } else if (tv_is_func(*rettv) || tv_is_func(var2)) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); tv_clear(rettv); @@ -8957,8 +8955,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv = &di->di_tv; } } - } else if (argvars[0].v_type == VAR_PARTIAL - || argvars[0].v_type == VAR_FUNC) { + } else if (tv_is_func(argvars[0])) { partial_T *pt; partial_T fref_pt; @@ -15994,7 +15991,7 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); typval_T *expr = NULL; - if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { + if (tv_is_func(argvars[2])) { expr = &argvars[2]; } else { sub = tv_get_string_buf_chk(&argvars[2], subbuf); @@ -18229,8 +18226,7 @@ handle_subscript( while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL))) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) && !ascii_iswhite(*(*arg - 1))) { if (**arg == '(') { partial_T *pt = NULL; @@ -18286,9 +18282,7 @@ handle_subscript( } // Turn "dict.Func" into a partial for "Func" bound to "dict". - if (selfdict != NULL - && (rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL)) { + if (selfdict != NULL && tv_is_func(*rettv)) { set_selfdict(rettv, selfdict); } @@ -18794,8 +18788,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, v = find_var_in_scoped_ht((const char *)name, name_len, true); } - if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && !var_check_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { return; } @@ -19463,9 +19456,7 @@ void ex_function(exarg_T *eap) arg = name; else arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || (fudi.fd_di->di_tv.v_type != VAR_FUNC - && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) { + if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { int j = (*arg == K_SPECIAL) ? 3 : 0; while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index d9feb2d88e..620da0032e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1250,8 +1250,7 @@ bool tv_dict_get_callback(dict_T *const d, return true; } - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { + if (!tv_is_func(di->di_tv) && di->di_tv.v_type != VAR_STRING) { emsgf(_("E6000: Argument is not a function or function name")); return false; } @@ -1418,7 +1417,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, // Disallow replacing a builtin function in l: and g:. // Check the key to be valid when adding to any scope. if (d1->dv_scope == VAR_DEF_SCOPE - && di2->di_tv.v_type == VAR_FUNC + && tv_is_func(di2->di_tv) && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { break; } @@ -2101,9 +2100,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, // TODO(ZyX-I): Make this not recursive static int recursive_cnt = 0; // Catch recursive loops. - if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) - && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) - && tv1->v_type != tv2->v_type) { + if (!(tv_is_func(*tv1) && tv_is_func(*tv2)) && tv1->v_type != tv2->v_type) { return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index fa0105197f..7eab22bc12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -408,6 +408,21 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) return QUEUE_DATA(q, DictWatcher, node); } +static inline bool tv_is_func(const typval_T tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + +/// Check whether given typval_T contains a function +/// +/// That is, whether it contains VAR_FUNC or VAR_PARTIAL. +/// +/// @param[in] tv Typval to check. +/// +/// @return True if it is a function or a partial, false otherwise. +static inline bool tv_is_func(const typval_T tv) +{ + return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif -- cgit From fa852e7cdc365b6fcd39d677f4067963274c44c3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:17:35 +0300 Subject: eval: Fix extend() behaviour with NULL lists and dictionaries Ref #4615 Ref vim/vim#768 --- src/nvim/eval.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab22c599a..9950f8b196 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8155,15 +8155,20 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - list_T *l1, *l2; - listitem_T *item; long before; bool error = false; - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) - && l2 != NULL) { + list_T *const l1 = argvars[0].vval.v_list; + list_T *const l2 = argvars[1].vval.v_list; + if (l1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (l2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)) { + listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); if (error) { @@ -8187,13 +8192,16 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1; - dict_T *d2; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) - && d2 != NULL) { + dict_T *const d1 = argvars[0].vval.v_dict; + dict_T *const d2 = argvars[1].vval.v_dict; + if (d1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (d2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { -- cgit From 218fa1d8069c1f1d8c5154b1abc2b686adc0c742 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:39:34 +0300 Subject: charset: Remove useless condition from vim_iswordc_tab --- src/nvim/charset.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 6c22108853..ad0efa2c28 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -787,10 +787,9 @@ bool vim_iswordc(int c) bool vim_iswordc_tab(const int c, const uint64_t *const chartab) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; + return (c >= 0x100 + ? (utf_class(c) >= 2) + : (c > 0 && GET_CHARTAB_TAB(chartab, c) != 0)); } /// Check that "c" is a keyword character: -- cgit From f0bbd1e825841c55a1f75d66a9caeaa50cc2259c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:35:12 +0300 Subject: unittests: Add tests for tv_clear() --- src/nvim/eval/typval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 620da0032e..c245b9222e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1840,10 +1840,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// /// @param[in,out] tv Value to free. -void tv_clear(typval_T *tv) +void tv_clear(typval_T *const tv) { if (tv != NULL && tv->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); + const int evn_ret = encode_vim_to_nothing(NULL, tv, + _("tv_clear() argument")); (void)evn_ret; assert(evn_ret == OK); } -- cgit From 630ff33dc144a64b5488b4132c0fc4351a5c84db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 19:52:20 +0300 Subject: unittests: Test locks section --- src/nvim/eval/typval.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index c245b9222e..185dd0e86c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1956,6 +1956,7 @@ void tv_copy(typval_T *const from, typval_T *const to) /// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. /// @param[in] lock True if it is needed to lock an item, false to unlock. void tv_item_lock(typval_T *const tv, const int deep, const bool lock) + FUNC_ATTR_NONNULL_ALL { // TODO(ZyX-I): Make this not recursive static int recurse = 0; @@ -2031,13 +2032,13 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) bool tv_islocked(const typval_T *const tv) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return ((tv->v_lock & VAR_LOCKED) + return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + && (tv->vval.v_list->lv_lock == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); + && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); } /// Return true if typval is locked -- cgit From e08b27ba4acf22fe180c2a4a6b00f0ea0cfc3a79 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:46:39 +0300 Subject: unittests: Add tv_get number tests --- src/nvim/eval/typval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 185dd0e86c..48ff3ab623 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2447,7 +2447,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_float(UNKNOWN)"); break; } } -- cgit From 29bad04f9e2e70b7c1c198ca0d86d23282bee8a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:35:34 +0300 Subject: eval: Do not supply S_LEN to strncmp It may be a macro as well. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9950f8b196..0d7c585099 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7967,7 +7967,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (s == NULL) { return; } - if (strncmp(s, S_LEN("silent")) == 0) { + if (strncmp(s, "silent", 6) == 0) { msg_silent++; } if (strcmp(s, "silent!") == 0) { -- cgit From f4256243dbdbd2858be716f9f52763bd9a76be4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:49:23 +0300 Subject: eval: Fix -Werror=unitialized from QB --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0d7c585099..6890bdc522 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2107,7 +2107,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } int len = -1; - char_u *key; + char_u *key = NULL; if (*p == '.') { key = p + 1; for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { -- cgit From 58e34e8d99b01bf3937824fc50502e39a8c39eba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 03:42:23 +0300 Subject: eval/typval: Allow NULL dict as tv_dict_get_callback() argument Also removes NULL key input: tv_dict_find() does not allow this. --- src/nvim/eval/typval.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 48ff3ab623..da58cf5ca9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -825,8 +825,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, /// @param[in] cb2 Second callback to check. /// /// @return True if they are equal, false otherwise. -static bool tv_callback_equal(const Callback *const cb1, - const Callback *const cb2) +bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (cb1->type != cb2->type) { @@ -1240,7 +1239,7 @@ const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_t key_len, Callback *const result) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2, 4) FUNC_ATTR_WARN_UNUSED_RESULT { result->type = kCallbackNone; -- cgit From 114eaa15f009cbd3e3deb177a2a67affa430fbb8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 01:07:34 +0300 Subject: eval/typval,api/buffer: Fix review comments --- src/nvim/api/buffer.c | 6 +++--- src/nvim/eval/typval.c | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 037a6ee1da..26f9a6f592 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -296,7 +296,7 @@ void nvim_buf_set_lines(uint64_t channel_id, tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); - ssize_t extra = 0; // lines added to text, can be negative + ptrdiff_t extra = 0; // lines added to text, can be negative char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { @@ -342,8 +342,8 @@ void nvim_buf_set_lines(uint64_t channel_id, } } - if ((ssize_t)to_delete > 0) { - extra -= (ssize_t)to_delete; + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; } // For as long as possible, replace the existing old_len with the diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index da58cf5ca9..779bb18175 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -164,9 +164,8 @@ void tv_list_free_contents(list_T *const l) } l->lv_len = 0; l->lv_idx_item = NULL; - for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - lw->lw_item = NULL; - } + l->lv_last = NULL; + assert(l->lv_watch == NULL); } /// Free a list itself, ignoring items it contains @@ -230,13 +229,10 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + // Notify watchers. + for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; tv_list_watch_fix(l, ip); - if (ip == item2) { - break; - } } if (item2->li_next == NULL) { -- cgit From a1d590a08bd9d40d0e20a9907381573c2d069738 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 02:17:51 +0300 Subject: *: Use const char * in set_one_cmd_context Also renames functions added in master and renamed here. --- src/nvim/charset.c | 23 +-- src/nvim/eval.c | 8 +- src/nvim/ex_cmds2.c | 12 +- src/nvim/ex_docmd.c | 444 +++++++++++++++++++++++++++------------------------ src/nvim/ex_getln.c | 9 +- src/nvim/if_cscope.c | 29 ++-- src/nvim/syntax.c | 69 ++++---- 7 files changed, 311 insertions(+), 283 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index ad0efa2c28..99d3e2dd88 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1397,7 +1397,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, /// /// @return Pointer to character after the skipped whitespace. char_u *skipwhite(const char_u *q) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { const char_u *p = q; while (ascii_iswhite(*p)) { @@ -1406,19 +1407,21 @@ char_u *skipwhite(const char_u *q) return (char_u *)p; } -/// skip over digits +/// Skip over digits /// -/// @param q +/// @param[in] q String to skip digits in. /// /// @return Pointer to the character after the skipped digits. -char_u* skipdigits(char_u *q) +char_u *skipdigits(const char_u *q) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { - char_u *p = q; + const char_u *p = q; while (ascii_isdigit(*p)) { // skip to next non-digit p++; } - return p; + return (char_u *)p; } /// skip over binary digits @@ -1564,17 +1567,17 @@ int vim_tolower(int c) return TOLOWER_LOC(c); } -/// skiptowhite: skip over text until ' ' or '\t' or NUL. +/// Skip over text until ' ' or '\t' or NUL /// -/// @param p +/// @param[in] p Text to skip over. /// /// @return Pointer to the next whitespace or NUL character. -char_u* skiptowhite(char_u *p) +char_u *skiptowhite(const char_u *p) { while (*p != ' ' && *p != '\t' && *p != NUL) { p++; } - return p; + return (char_u *)p; } /// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6890bdc522..80c2fe10d7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9462,8 +9462,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (STRCMP(get_tv_string(&argvars[1]), "cmdline") == 0) { - set_one_cmd_context(&xpc, get_tv_string(&argvars[0])); + if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); goto theend; } @@ -9484,7 +9484,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (xpc.xp_context == EXPAND_CSCOPE) { - set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); + set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } @@ -14562,7 +14562,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_STRING) { title = tv_get_string_chk(title_arg); if (!title) { - // Type error. Error already printed by get_tv_string_chk(). + // Type error. Error already printed by tv_get_string_chk(). return; } } else if (title_arg->v_type == VAR_DICT) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 213641667d..eeace789b2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -957,23 +957,21 @@ char_u *get_profile_name(expand_T *xp, int idx) } /// Handle command line completion for :profile command. -void set_context_in_profile_cmd(expand_T *xp, char_u *arg) +void set_context_in_profile_cmd(expand_T *xp, const char *arg) { - char_u *end_subcmd; - // Default: expand subcommands. xp->xp_context = EXPAND_PROFILE; pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; - end_subcmd = skiptowhite(arg); + char_u *const end_subcmd = skiptowhite((const char_u *)arg); if (*end_subcmd == NUL) { return; } - if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { + if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) { xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite(end_subcmd); + xp->xp_pattern = skipwhite((const char_u *)end_subcmd); return; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 26cfec991f..0fd4ae48be 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2633,32 +2633,27 @@ int cmd_exists(const char *const name) * perfectly compatible with each other, but then the command line syntax * probably won't change that much -- webb. */ -char_u * -set_one_cmd_context ( +const char * set_one_cmd_context( expand_T *xp, - char_u *buff /* buffer for command string */ + const char *buff // buffer for command string ) { - char_u *p; - char_u *cmd, *arg; - int len = 0; + size_t len = 0; exarg_T ea; - int compl = EXPAND_NOTHING; - int delim; - int forceit = FALSE; - int usefilter = FALSE; /* filter instead of file name */ + int context = EXPAND_NOTHING; + int forceit = false; + int usefilter = false; // Filter instead of file name. ExpandInit(xp); - xp->xp_pattern = buff; - xp->xp_context = EXPAND_COMMANDS; /* Default until we get past command */ + xp->xp_pattern = (char_u *)buff; + xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; - /* - * 2. skip comment lines and leading space, colons or bars - */ - for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) - ; - xp->xp_pattern = cmd; + // 2. skip comment lines and leading space, colons or bars + const char *cmd; + for (cmd = buff; strchr(" \t:|", *cmd) != NULL; cmd++) { + } + xp->xp_pattern = (char_u *)cmd; if (*cmd == NUL) return NULL; @@ -2670,14 +2665,15 @@ set_one_cmd_context ( /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. */ - cmd = skip_range(cmd, &xp->xp_context); + cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context); /* * 4. parse command */ - xp->xp_pattern = cmd; - if (*cmd == NUL) + xp->xp_pattern = (char_u *)cmd; + if (*cmd == NUL) { return NULL; + } if (*cmd == '"') { xp->xp_context = EXPAND_NOTHING; return NULL; @@ -2693,6 +2689,7 @@ set_one_cmd_context ( * do accept "keepmarks", "keepalt" and "keepjumps". * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' */ + const char *p; if (*cmd == 'k' && cmd[1] != 'e') { ea.cmdidx = CMD_k; p = cmd + 1; @@ -2715,20 +2712,21 @@ set_one_cmd_context ( } } // check for non-alpha command - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + if (p == cmd && strchr("@*!=><&~#", *p) != NULL) { p++; } - len = (int)(p - cmd); + len = (size_t)(p - cmd); if (len == 0) { xp->xp_context = EXPAND_UNSUCCESSFUL; return NULL; } for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < (int)CMD_SIZE; - ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) - if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, - (size_t)len) == 0) + ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) { + if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) { break; + } + } if (cmd[0] >= 'A' && cmd[0] <= 'Z') { while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card @@ -2745,16 +2743,15 @@ set_one_cmd_context ( return NULL; if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) { + if (*cmd == 's' && strchr("cgriI", cmd[1]) != NULL) { ea.cmdidx = CMD_substitute; p = cmd + 1; } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = cmd; - p = find_ucmd(&ea, p, NULL, xp, - &compl - ); - if (p == NULL) - ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + ea.cmd = (char_u *)cmd; + p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context); + if (p == NULL) { + ea.cmdidx = CMD_SIZE; // Ambiguous user command. + } } } if (ea.cmdidx == CMD_SIZE) { @@ -2777,16 +2774,17 @@ set_one_cmd_context ( ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - arg = skipwhite(p); + const char *arg = (const char *)skipwhite((const char_u *)p); if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*arg == '>') { /* append */ - if (*++arg == '>') - ++arg; - arg = skipwhite(arg); - } else if (*arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++arg; - usefilter = TRUE; + if (*arg == '>') { // Append. + if (*++arg == '>') { + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); + } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter + arg++; + usefilter = true; } } @@ -2799,23 +2797,24 @@ set_one_cmd_context ( } if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - while (*arg == *cmd) /* allow any number of '>' or '<' */ - ++arg; - arg = skipwhite(arg); + while (*arg == *cmd) { // allow any number of '>' or '<' + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); } /* Does command allow "+command"? */ if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { /* Check if we're in the +command */ p = arg + 1; - arg = skip_cmd_arg(arg, FALSE); + arg = (const char *)skip_cmd_arg((char_u *)arg, false); /* Still touching the command after '+'? */ if (*arg == NUL) return p; - /* Skip space(s) after +command to get to the real argument */ - arg = skipwhite(arg); + // Skip space(s) after +command to get to the real argument. + arg = (const char *)skipwhite((const char_u *)arg); } /* @@ -2844,19 +2843,18 @@ set_one_cmd_context ( } // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL - && vim_strchr((char_u *)"|\"", *arg) == NULL) { + if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { return NULL; } /* Find start of last argument (argument just before cursor): */ p = buff; - xp->xp_pattern = p; - len = (int)STRLEN(buff); + xp->xp_pattern = (char_u *)p; + len = strlen(buff); while (*p && p < buff + len) { if (*p == ' ' || *p == TAB) { - /* argument starts after a space */ - xp->xp_pattern = ++p; + // Argument starts after a space. + xp->xp_pattern = (char_u *)++p; } else { if (*p == '\\' && *(p + 1) != NUL) ++p; /* skip over escaped character */ @@ -2866,25 +2864,26 @@ set_one_cmd_context ( if (ea.argt & XFILE) { int c; - int in_quote = FALSE; - char_u *bow = NULL; /* Beginning of word */ + int in_quote = false; + const char *bow = NULL; // Beginning of word. /* * Allow spaces within back-quotes to count as part of the argument * being expanded. */ - xp->xp_pattern = skipwhite(arg); - p = xp->xp_pattern; + xp->xp_pattern = skipwhite((const char_u *)arg); + p = (const char *)xp->xp_pattern; while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '\\' && p[1] != NUL) - ++p; - else if (c == '`') { + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { + c = (uint8_t)(*p); + } + if (c == '\\' && p[1] != NUL) { + p++; + } else if (c == '`') { if (!in_quote) { - xp->xp_pattern = p; + xp->xp_pattern = (char_u *)p; bow = p + 1; } in_quote = !in_quote; @@ -2897,22 +2896,26 @@ set_one_cmd_context ( || ascii_iswhite(c)) { len = 0; /* avoid getting stuck when space is in 'isfname' */ while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { c = *p; - if (c == '`' || vim_isfilec_or_wc(c)) + } + if (c == '`' || vim_isfilec_or_wc(c)) { break; - if (has_mbyte) - len = (*mb_ptr2len)(p); - else + } + if (has_mbyte) { + len = (size_t)(*mb_ptr2len)((const char_u *)p); + } else { len = 1; + } mb_ptr_adv(p); } - if (in_quote) + if (in_quote) { bow = p; - else - xp->xp_pattern = p; + } else { + xp->xp_pattern = (char_u *)p; + } p -= len; } mb_ptr_adv(p); @@ -2922,8 +2925,9 @@ set_one_cmd_context ( * If we are still inside the quotes, and we passed a space, just * expand from there. */ - if (bow != NULL && in_quote) - xp->xp_pattern = bow; + if (bow != NULL && in_quote) { + xp->xp_pattern = (char_u *)bow; + } xp->xp_context = EXPAND_FILES; /* For a shell command more chars need to be escaped. */ @@ -2931,33 +2935,36 @@ set_one_cmd_context ( #ifndef BACKSLASH_IN_FILENAME xp->xp_shell = TRUE; #endif - /* When still after the command name expand executables. */ - if (xp->xp_pattern == skipwhite(arg)) + // When still after the command name expand executables. + if (xp->xp_pattern == skipwhite((const char_u *)arg)) { xp->xp_context = EXPAND_SHELLCMD; + } } - /* Check for environment variable */ - if (*xp->xp_pattern == '$' - ) { - for (p = xp->xp_pattern + 1; *p != NUL; ++p) - if (!vim_isIDc(*p)) + // Check for environment variable. + if (*xp->xp_pattern == '$') { + for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) { + if (!vim_isIDc((uint8_t)(*p))) { break; + } + } if (*p == NUL) { xp->xp_context = EXPAND_ENV_VARS; - ++xp->xp_pattern; - /* Avoid that the assignment uses EXPAND_FILES again. */ - if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) - compl = EXPAND_ENV_VARS; + xp->xp_pattern++; + // Avoid that the assignment uses EXPAND_FILES again. + if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) { + context = EXPAND_ENV_VARS; + } } } /* Check for user names */ if (*xp->xp_pattern == '~') { - for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) - ; - /* Complete ~user only if it partially matches a user name. - * A full match ~user will be replaced by user's home - * directory i.e. something like ~user -> /home/user/ */ - if (*p == NUL && p > xp->xp_pattern + 1 + for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { + } + // Complete ~user only if it partially matches a user name. + // A full match ~user will be replaced by user's home + // directory i.e. something like ~user -> /home/user/ + if (*p == NUL && p > (const char *)xp->xp_pattern + 1 && match_user(xp->xp_pattern + 1) == 1) { xp->xp_context = EXPAND_USER; ++xp->xp_pattern; @@ -2987,7 +2994,7 @@ set_one_cmd_context ( break; case CMD_help: xp->xp_context = EXPAND_HELP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; /* Command modifiers: return the argument. @@ -3030,13 +3037,14 @@ set_one_cmd_context ( if (*arg == NUL || !ends_excmd(*arg)) { /* also complete "None" */ set_context_in_echohl_cmd(xp, arg); - arg = skipwhite(skiptowhite(arg)); + arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); + arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg), + p_magic, NULL); } } - return find_nextcmd(arg); + return (const char *)find_nextcmd((char_u *)arg); /* * All completion for the +cmdline_compl feature goes here. @@ -3045,15 +3053,15 @@ set_one_cmd_context ( case CMD_command: /* Check for attributes */ while (*arg == '-') { - arg++; /* Skip "-" */ - p = skiptowhite(arg); + arg++; // Skip "-". + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { - /* Cursor is still in the attribute */ - p = vim_strchr(arg, '='); + // Cursor is still in the attribute. + p = strchr(arg, '='); if (p == NULL) { - /* No "=", so complete attribute names */ + // No "=", so complete attribute names. xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; return NULL; } @@ -3061,73 +3069,81 @@ set_one_cmd_context ( // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "nargs", p - arg) == 0) { xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "addr", p - arg) == 0) { xp->xp_context = EXPAND_USER_ADDR_TYPE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } return NULL; } - arg = skipwhite(p); + arg = (const char *)skipwhite((char_u *)p); } - /* After the attributes comes the new command name */ - p = skiptowhite(arg); + // After the attributes comes the new command name. + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; } - /* And finally comes a normal command */ - return skipwhite(p); + // And finally comes a normal command. + return (const char *)skipwhite((const char_u *)p); case CMD_delcommand: xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_global: - case CMD_vglobal: - delim = *arg; /* get the delimiter */ - if (delim) - ++arg; /* skip delimiter if there is one */ + case CMD_vglobal: { + const int delim = (uint8_t)(*arg); // Get the delimiter. + if (delim) { + arg++; // Skip delimiter if there is one. + } - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } if (arg[0] != NUL) return arg + 1; break; + } case CMD_and: - case CMD_substitute: - delim = *arg; + case CMD_substitute: { + const int delim = (uint8_t)(*arg); if (delim) { - /* skip "from" part */ - ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); + // Skip "from" part. + arg++; + arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); } - /* skip "to" part */ - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + // Skip "to" part. + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } - if (arg[0] != NUL) /* skip delimiter */ - ++arg; - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) + if (arg[0] != NUL) { // Skip delimiter. + arg++; + } + while (arg[0] && strchr("|\"#", arg[0]) == NULL) { + arg++; + } + if (arg[0] != NUL) { return arg; + } break; + } case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -3137,36 +3153,40 @@ set_one_cmd_context ( case CMD_djump: case CMD_isplit: case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); /* skip count */ - if (*arg == '/') { /* Match regexp, not just whole words */ - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) + // Skip count. + arg = (const char *)skipwhite(skipdigits((const char_u *)arg)); + if (*arg == '/') { // Match regexp, not just whole words. + for (++arg; *arg && *arg != '/'; arg++) { + if (*arg == '\\' && arg[1] != NUL) { arg++; + } + } if (*arg) { - arg = skipwhite(arg + 1); + arg = (const char *)skipwhite((const char_u *)arg + 1); - /* Check for trailing illegal characters */ - if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL) + // Check for trailing illegal characters. + if (*arg && strchr("|\"\n", *arg) == NULL) { xp->xp_context = EXPAND_NOTHING; - else + } else { return arg; + } } } break; case CMD_autocmd: - return set_context_in_autocmd(xp, arg, FALSE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false); case CMD_doautocmd: case CMD_doautoall: - return set_context_in_autocmd(xp, arg, TRUE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true); case CMD_set: - set_context_in_set_cmd(xp, arg, 0); + set_context_in_set_cmd(xp, (char_u *)arg, 0); break; case CMD_setglobal: - set_context_in_set_cmd(xp, arg, OPT_GLOBAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL); break; case CMD_setlocal: - set_context_in_set_cmd(xp, arg, OPT_LOCAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL); break; case CMD_tag: case CMD_stag: @@ -3178,15 +3198,16 @@ set_one_cmd_context ( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) + if (*p_wop != NUL) { xp->xp_context = EXPAND_TAGS_LISTFILES; - else + } else { xp->xp_context = EXPAND_TAGS; - xp->xp_pattern = arg; + } + xp->xp_pattern = (char_u *)arg; break; case CMD_augroup: xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntax: set_context_in_syntax_cmd(xp, arg); @@ -3203,20 +3224,21 @@ set_one_cmd_context ( case CMD_echoerr: case CMD_call: case CMD_return: - set_context_for_expression(xp, arg, ea.cmdidx); + set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); break; case CMD_unlet: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_echohl: @@ -3231,33 +3253,37 @@ set_one_cmd_context ( set_context_in_cscope_cmd(xp, arg, ea.cmdidx); break; case CMD_sign: - set_context_in_sign_cmd(xp, arg); + set_context_in_sign_cmd(xp, (char_u *)arg); break; case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - /*FALLTHROUGH*/ + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } + // FALLTHROUGH case CMD_buffer: case CMD_sbuffer: case CMD_checktime: xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_USER: case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) { - /* XFILE: file names are handled above */ + if (context != EXPAND_NOTHING) { + // XFILE: file names are handled above. if (!(ea.argt & XFILE)) { - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - if (compl == EXPAND_COMMANDS) + if (context == EXPAND_MENUS) { + return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, + (char_u *)arg, forceit); + } else if (context == EXPAND_COMMANDS) { return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - /* Find start of last argument. */ + } else if (context == EXPAND_MAPPINGS) { + return (const char *)set_context_in_map_cmd( + xp, (char_u *)"map", (char_u *)arg, forceit, false, false, + CMD_map); + } + // Find start of last argument. p = arg; while (*p) { if (*p == ' ') @@ -3267,9 +3293,9 @@ set_one_cmd_context ( ++p; /* skip over escaped character */ mb_ptr_adv(p); } - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } - xp->xp_context = compl; + xp->xp_context = context; } break; case CMD_map: case CMD_noremap: @@ -3281,8 +3307,8 @@ set_one_cmd_context ( case CMD_lmap: case CMD_lnoremap: case CMD_smap: case CMD_snoremap: case CMD_xmap: case CMD_xnoremap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, false, ea.cmdidx); case CMD_unmap: case CMD_nunmap: case CMD_vunmap: @@ -3292,18 +3318,18 @@ set_one_cmd_context ( case CMD_lunmap: case CMD_sunmap: case CMD_xunmap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); case CMD_abbreviate: case CMD_noreabbrev: case CMD_cabbrev: case CMD_cnoreabbrev: case CMD_iabbrev: case CMD_inoreabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, false, ea.cmdidx); case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, true, ea.cmdidx); case CMD_menu: case CMD_noremenu: case CMD_unmenu: case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: @@ -3313,47 +3339,49 @@ set_one_cmd_context ( case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return set_context_in_menu_cmd(xp, cmd, arg, forceit); + return (const char *)set_context_in_menu_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_compiler: xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_ownsyntax: xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_setfiletype: xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_packadd: xp->xp_context = EXPAND_PACKADD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; #ifdef HAVE_WORKING_LIBINTL case CMD_language: - p = skiptowhite(arg); + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } else { - if ( STRNCMP(arg, "messages", p - arg) == 0 - || STRNCMP(arg, "ctype", p - arg) == 0 - || STRNCMP(arg, "time", p - arg) == 0) { + if (strncmp(arg, "messages", p - arg) == 0 + || strncmp(arg, "ctype", p - arg) == 0 + || strncmp(arg, "time", p - arg) == 0) { xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite(p); - } else + xp->xp_pattern = skipwhite((const char_u *)p); + } else { xp->xp_context = EXPAND_NOTHING; + } } break; #endif @@ -3362,16 +3390,16 @@ set_one_cmd_context ( break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_history: xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntime: xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; @@ -3390,10 +3418,9 @@ set_one_cmd_context ( * Also skip white space and ":" characters. * Returns the "cmd" pointer advanced to beyond the range. */ -char_u * -skip_range ( - char_u *cmd, - int *ctx /* pointer to xp_context or NULL */ +char_u *skip_range( + const char_u *cmd, + int *ctx // pointer to xp_context or NULL ) { unsigned delim; @@ -3418,7 +3445,7 @@ skip_range ( while (*cmd == ':') cmd = skipwhite(cmd + 1); - return cmd; + return (char_u *)cmd; } /* @@ -4585,14 +4612,15 @@ int ends_excmd(int c) FUNC_ATTR_CONST * Return the next command, after the first '|' or '\n'. * Return NULL if not found. */ -char_u *find_nextcmd(char_u *p) +char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { - if (*p == NUL) + if (*p == NUL) { return NULL; - ++p; + } + p++; } - return p + 1; + return (char_u *)p + 1; } /* diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e140dfa886..8810204c03 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3636,7 +3636,6 @@ set_cmd_context ( ) { int old_char = NUL; - char_u *nextcomm; /* * Avoid a UMR warning from Purify, only save the character if it has been @@ -3645,7 +3644,7 @@ set_cmd_context ( if (col < len) old_char = str[col]; str[col] = NUL; - nextcomm = str; + const char *nextcomm = (const char *)str; if (use_ccline && ccline.cmdfirstc == '=') { // pass CMD_SIZE because there is no real command @@ -3654,9 +3653,11 @@ set_cmd_context ( xp->xp_context = ccline.xp_context; xp->xp_pattern = ccline.cmdbuff; xp->xp_arg = ccline.xp_arg; - } else - while (nextcomm != NULL) + } else { + while (nextcomm != NULL) { nextcomm = set_one_cmd_context(xp, nextcomm); + } + } /* Store the string here so that call_user_expand_func() can get to them * easily. */ diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 550d256de5..b647b8146a 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -140,31 +140,30 @@ char_u *get_cscope_name(expand_T *xp, int idx) /* * Handle command line completion for :cscope command. */ -void set_context_in_cscope_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx) +void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_CSCOPE; - xp->xp_pattern = arg; - expand_what = (cmdidx == CMD_scscope) - ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD; + xp->xp_pattern = (char_u *)arg; + expand_what = ((cmdidx == CMD_scscope) + ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD); /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "add", p - arg) == 0) + } else if (STRNICMP(arg, "add", p - arg) == 0) { xp->xp_context = EXPAND_FILES; - else if (STRNICMP(arg, "kill", p - arg) == 0) + } else if (STRNICMP(arg, "kill", p - arg) == 0) { expand_what = EXP_CSCOPE_KILL; - else if (STRNICMP(arg, "find", p - arg) == 0) + } else if (STRNICMP(arg, "find", p - arg) == 0) { expand_what = EXP_CSCOPE_FIND; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a54e36a609..4a7b4a0eac 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5574,43 +5574,42 @@ void reset_expand_highlight(void) * Handle command line completion for :match and :echohl command: Add "None" * as highlight group. */ -void set_context_in_echohl_cmd(expand_T *xp, char_u *arg) +void set_context_in_echohl_cmd(expand_T *xp, const char *arg) { xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_none = 1; } /* * Handle command line completion for :syntax command. */ -void set_context_in_syntax_cmd(expand_T *xp, char_u *arg) +void set_context_in_syntax_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_SYNTAX; expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 0; include_default = 0; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "case", p - arg) == 0) + } else if (STRNICMP(arg, "case", p - arg) == 0) { expand_what = EXP_CASE; - else if ( STRNICMP(arg, "keyword", p - arg) == 0 + } else if (STRNICMP(arg, "keyword", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0 - || STRNICMP(arg, "list", p - arg) == 0) + || STRNICMP(arg, "list", p - arg) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } @@ -7474,41 +7473,41 @@ int highlight_changed(void) /* * Handle command line completion for :highlight command. */ -void set_context_in_highlight_cmd(expand_T *xp, char_u *arg) +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand group names */ + // Default: expand group names. xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 2; include_default = 1; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past "default" or group name */ + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. include_default = 0; - if (STRNCMP("default", arg, p - arg) == 0) { - arg = skipwhite(p); - xp->xp_pattern = arg; - p = skiptowhite(arg); + if (strncmp("default", arg, p - arg) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); } if (*p != NUL) { /* past group name */ include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') + if (arg[1] == 'i' && arg[0] == 'N') { highlight_list(); - if (STRNCMP("link", arg, p - arg) == 0 - || STRNCMP("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); - if (*p != NUL) { /* past first group name */ - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); + } + if (strncmp("link", arg, p - arg) == 0 + || strncmp("clear", arg, p - arg) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); } } - if (*p != NUL) /* past group name(s) */ + if (*p != NUL) { // Past group name(s). xp->xp_context = EXPAND_NOTHING; + } } } } -- cgit From b9603218be3b7bf3fbc30eee6c7c458b01902584 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 08:12:09 +0300 Subject: eval/executor: Fix check-single-includes --- src/nvim/eval/executor.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h index 19e2a75914..3d789f76a5 100644 --- a/src/nvim/eval/executor.h +++ b/src/nvim/eval/executor.h @@ -1,6 +1,8 @@ #ifndef NVIM_EVAL_EXECUTOR_H #define NVIM_EVAL_EXECUTOR_H +#include "nvim/eval/typval.h" + extern char *e_listidx; #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 2846d508b24eb9f8ab59d21a060fd8130906392f Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 29 Mar 2017 22:48:50 +0800 Subject: vim-patch:7.4.2276 (#6393) * vim-patch:7.4.2276 Problem: Command line test fails on Windows when run twice. Solution: Wipe the buffer so that the directory can be deleted. https://github.com/vim/vim/commit/1773ddfdcd106fa3bbf479c9b62ccde03c2a86ba * version.c: mark vim-patch 7.4.2269 as included (#5659) --- src/nvim/testdir/test_cmdline.vim | 1 + src/nvim/version.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index f56227250c..0c9d1297d6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -201,5 +201,6 @@ func Test_expand_star_star() call writefile(['asdfasdf'], 'a/b/fileXname') call feedkeys(":find **/fileXname\\", 'xt') call assert_equal('find a/b/fileXname', getreg(':')) + bwipe! call delete('a', 'rf') endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index dd583f6ffd..020737ac8c 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -165,14 +165,14 @@ static int included_patches[] = { 2279, // 2278 NA 2277, - // 2276, + 2276, 2275, 2274, 2273, 2272, // 2271 NA // 2270 NA - // 2269, + 2269, // 2268, // 2267 NA 2266, -- cgit From 05b74399aa40c813b195d595aa3878ff42e61ab8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 17:39:06 +0200 Subject: build: remove unused get_preproc_output() call ref https://github.com/neovim/neovim/pull/6375#discussion_r108573471 --- src/nvim/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e752f5d4df..bad43e7f72 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -461,10 +461,8 @@ foreach(hfile ${NVIM_HEADERS}) if(NOT ${hfile} MATCHES "[.]c[.]h$") set(tsource "${GENERATED_DIR}/${r}.test-include.c") - set(tresult "${GENERATED_DIR}/${r}.test-include.i") string(REPLACE "/" "-" texe "test-incl-${r}") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") - get_preproc_output(PREPROC_OUTPUT ${tresult}) add_executable( ${texe} EXCLUDE_FROM_ALL -- cgit From 6964b67c00f9aa029791137f44f30ed4aef20ef4 Mon Sep 17 00:00:00 2001 From: relnod Date: Thu, 30 Mar 2017 00:50:11 +0200 Subject: refactor/single-include: buffer.h (#6396) --- src/nvim/CMakeLists.txt | 1 - src/nvim/buffer.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index c5bf79c0e5..a3bacaa9d2 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -394,7 +394,6 @@ target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) set(NO_SINGLE_CHECK_HEADERS - buffer cursor_shape diff digraph diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index c915d373aa..016c5ce3b7 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -1,6 +1,7 @@ #ifndef NVIM_BUFFER_H #define NVIM_BUFFER_H +#include "nvim/vim.h" #include "nvim/window.h" #include "nvim/pos.h" // for linenr_T #include "nvim/ex_cmds_defs.h" // for exarg_T -- cgit From 1f478cebeb929332e90c1b50de4b8a4f311a0df2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 29 Mar 2017 18:29:46 +0200 Subject: win: tempname(): Use $TMPDIR if defined. --- src/nvim/os/win_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 827fb2f247..7c980c3768 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -19,7 +19,7 @@ #define NAME_MAX _MAX_PATH -#define TEMP_DIR_NAMES { "$TMP", "$TEMP", "$USERPROFILE", "" } +#define TEMP_DIR_NAMES { "$TMPDIR", "$TMP", "$TEMP", "$USERPROFILE", "" } #define TEMP_FILE_PATH_MAXLEN _MAX_PATH #define FNAME_ILLEGAL "\"*?><|" -- cgit From 3116f870ba274862fa6d6643d9fa0870215fed12 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 16:30:06 -0400 Subject: coverity/161195: Increase scope of exe_name Since exe_name is a stack allocated array, we need it to be in scope for the lifetime that vim_path points to it. --- src/nvim/os/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..a10c835591 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -615,9 +615,9 @@ char *vim_getenv(const char *name) vim_path = (char *)p_hf; } + char exe_name[MAXPATHL]; // Find runtime path relative to the nvim binary: ../share/nvim/runtime if (vim_path == NULL) { - char exe_name[MAXPATHL]; size_t exe_name_len = MAXPATHL; if (os_exepath(exe_name, &exe_name_len) == 0) { char *path_end = (char *)path_tail_with_sep((char_u *)exe_name); -- cgit From 1c6ae58fd1301bfe2b27ed168b5a117e92c9c4cd Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 16:51:46 -0400 Subject: coverity/161194: Restore check for 'keywordprg' being ":help" 998d0ffc09d5c7358db62dc88c2e2b87622f60b5 removed the explicit check for ":help", relying instead on whether the user was in a help buffer. However, this breaks escaping the identifier for use in the lookup command. 2f54d6927cc02484b528a5e8b25b64c8d6580ddd tried to fix this by removing "!kp_ex" in "if (cmdchar == 'K' && !kp_ex)", but that causes shell escaping to be used instead of escaping for tag lookup. --- src/nvim/normal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d4919dc3b6..388ddfc8bb 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4670,6 +4670,7 @@ static void nv_ident(cmdarg_T *cap) char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg' assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty. bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command + bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0); size_t buf_size = n * 2 + 30 + STRLEN(kp); char *buf = xmalloc(buf_size); buf[0] = NUL; @@ -4692,7 +4693,9 @@ static void nv_ident(cmdarg_T *cap) break; case 'K': - if (kp_ex) { + if (kp_help) { + STRCPY(buf, "he! "); + } else if (kp_ex) { if (cap->count0 != 0) { // Send the count to the ex command. snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0)); } @@ -4755,7 +4758,7 @@ static void nv_ident(cmdarg_T *cap) } // Now grab the chars in the identifier - if (cmdchar == 'K') { + if (cmdchar == 'K' && !kp_help) { ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command -- cgit From 91dfebf0506c4389af77071323798fdd7360c589 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 20:45:22 -0400 Subject: ci: Update Coverity model for typval refactoring [ci skip] --- src/coverity-model.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/coverity-model.c b/src/coverity-model.c index a01ea6d316..3c38e4ae4d 100644 --- a/src/coverity-model.c +++ b/src/coverity-model.c @@ -64,7 +64,7 @@ void *je_realloc(void *ptr, size_t size) // of the memory allocated for item. typedef struct {} dictitem_T; typedef struct {} dict_T; -int dict_add(dict_T *d, dictitem_T *item) +int tv_dict_add(dict_T *const d, dictitem_T *const item) { __coverity_escape__(item); } -- cgit From 1222c82799b9a853c5adaf8761309b616e664c95 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 29 Mar 2017 21:34:04 -0400 Subject: coverity/16127: Verify lang is non-NULL before calling strlen --- src/nvim/option.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 9b31e14ea7..69c12e2cc7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1054,13 +1054,15 @@ void set_init_3(void) */ void set_helplang_default(const char *lang) { - int idx; + if (lang == NULL) { + return; + } const size_t lang_len = strlen(lang); - if (lang == NULL || lang_len < 2) { // safety check + if (lang_len < 2) { // safety check return; } - idx = findoption("hlg"); + int idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); -- cgit From 75b98f7c3f23b18e30c362028af3bf3dae663a02 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 10:09:33 -0400 Subject: Remove PVS-Studio cruft [ci skip] --- src/nvim/move.PVS-Studio.cfg | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/nvim/move.PVS-Studio.cfg (limited to 'src') diff --git a/src/nvim/move.PVS-Studio.cfg b/src/nvim/move.PVS-Studio.cfg deleted file mode 100644 index cb6da32f12..0000000000 --- a/src/nvim/move.PVS-Studio.cfg +++ /dev/null @@ -1,10 +0,0 @@ - -source-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.c -i-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.i -language=C -skip-cl-exe=yes -preprocessor=gcc -platform=linux64 -lic-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.lic -output-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.log.x -analysis-mode=4 -- cgit From eb0e94f71b1f44cebf7ae5c1bcff348264af6cef Mon Sep 17 00:00:00 2001 From: Jakob Schnitzer Date: Thu, 30 Mar 2017 22:03:52 +0200 Subject: api: {get,set}_option should {get,set} global value of local options (#6405) - nvim_get_option should return the global default of a local option. - nvim_set_option should set the global default of a local option. --- src/nvim/api/private/helpers.c | 2 +- src/nvim/api/vim.c | 2 +- src/nvim/option.c | 24 +++++++++++++----------- src/nvim/option_defs.h | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index b245132ba7..fe15b28041 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -289,7 +289,7 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) } } - int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL); + int opt_flags = (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; if (flags & SOPT_BOOL) { if (value.type != kObjectTypeBoolean) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 975446057c..6926436d2f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -440,7 +440,7 @@ Object nvim_get_vvar(String name, Error *err) /// /// @param name Option name /// @param[out] err Error details, if any -/// @return Option value +/// @return Option value (global) Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 9b31e14ea7..0bf81b4d3a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4619,14 +4619,13 @@ int get_option_value_strict(char *name, } char_u *varp = NULL; - vimoption_T *p; int rv = 0; int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } - p = &(options[opt_idx]); + vimoption_T *p = &options[opt_idx]; // Hidden option if (p->var == NULL) { @@ -4642,26 +4641,25 @@ int get_option_value_strict(char *name, } if (p->indir == PV_NONE) { - if (opt_type == SREQ_GLOBAL) + if (opt_type == SREQ_GLOBAL) { rv |= SOPT_GLOBAL; - else - return 0; // Did not request global-only option + } else { + return 0; // Did not request global-only option + } } else { if (p->indir & PV_BOTH) { rv |= SOPT_GLOBAL; - } else if (opt_type == SREQ_GLOBAL) { - return 0; // Requested global option } if (p->indir & PV_WIN) { if (opt_type == SREQ_BUF) { - return 0; // Did not request window-local option + return 0; // Requested buffer-local, not window-local option } else { rv |= SOPT_WIN; } } else if (p->indir & PV_BUF) { if (opt_type == SREQ_WIN) { - return 0; // Did not request buffer-local option + return 0; // Requested window-local, not buffer-local option } else { rv |= SOPT_BUF; } @@ -4673,7 +4671,11 @@ int get_option_value_strict(char *name, } if (opt_type == SREQ_GLOBAL) { - varp = p->var; + if (p->var == VAR_WIN) { + return 0; + } else { + varp = p->var; + } } else { if (opt_type == SREQ_BUF) { // Special case: 'modified' is b_changed, but we also want to @@ -4720,7 +4722,7 @@ int get_option_value_strict(char *name, /// @param[in] name Option name. /// @param[in] number New value for the number or boolean option. /// @param[in] string New value for string option. -/// @param[in] opt_flags Flags: OPT_LOCAL or 0 (both). +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). /// /// @return NULL on success, error message on error. char *set_option_value(const char *const name, const long number, diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 94c6361236..2475a0b6a1 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -16,9 +16,9 @@ #define SOPT_UNSET 0x40 // Option does not have local value set // Option types for various functions in option.c -#define SREQ_GLOBAL 0 // Request global option -#define SREQ_WIN 1 // Request window-local option -#define SREQ_BUF 2 // Request buffer-local option +#define SREQ_GLOBAL 0 // Request global option value +#define SREQ_WIN 1 // Request window-local option value +#define SREQ_BUF 2 // Request buffer-local option value /* * Default values for 'errorformat'. -- cgit From 831eb2a9bf9fdffc1b6942e4c43bc2458f4af794 Mon Sep 17 00:00:00 2001 From: Michael Ennen Date: Thu, 30 Mar 2017 16:07:39 -0700 Subject: vim-patch:7.4.2104 (#6332) Problem: Code duplication when unreferencing a function. Solution: De-duplicate. https://github.com/vim/vim/commit/97baee80f0906ee2f651ee1215ec033e84f866ad --- src/nvim/eval.c | 8 +------- src/nvim/version.c | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80c2fe10d7..a83e93090a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20711,13 +20711,7 @@ void func_unref(char_u *name) abort(); #endif } - if (fp != NULL && --fp->uf_refcount <= 0) { - // Only delete it when it's not being used. Otherwise it's done - // when "uf_calls" becomes zero. - if (fp->uf_calls == 0) { - func_clear_free(fp, false); - } - } + func_ptr_unref(fp); } /// Unreference a Function: decrement the reference count and free it when it diff --git a/src/nvim/version.c b/src/nvim/version.c index 9275a2e5bd..fdf5436a98 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -337,7 +337,7 @@ static const int included_patches[] = { 2107, 2106, // 2105 NA - // 2104, + 2104, 2103, // 2102 NA 2101, -- cgit From 3a9dd13f9e6471215a738cf22bf180e60a2e0bcd Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Fri, 31 Mar 2017 00:21:26 +0100 Subject: fold.c: more edge-cases when updating (#6207) When foldUpdateIEMSRecurse() re-uses an existing fold, it misses the case where the existing fold spans from before startlnum to after firstlnum, the new fold does not span this range, and there is no "forced start" of a fold. We add a case for this in. Ensure that if there was no forced break in folds, we merge folds that now touch each other. Include testing for a tricky foldmethod=expr case that has never been a bug. This case works at the moment because of some effects that are not obvious when reading the code. A test for this could be useful to ensure a regression doesn't happen. vim-patch:8.0.0408 --- src/nvim/fold.c | 71 +++++++++++++++++-------- src/nvim/testdir/test_fold.vim | 118 +++++++++++++++++++++++++++++++++++------ 2 files changed, 152 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 36a5b0efd7..d810aee0ce 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2232,32 +2232,51 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, * before where we started looking, extend it. If it * starts at another line, update nested folds to keep * their position, compensating for the new fd_top. */ - if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) { - if (fp->fd_top > firstlnum) - /* like lines are inserted */ + if (fp->fd_top == firstlnum) { + // We have found a fold beginning exactly where we want one. + } else if (fp->fd_top >= startlnum) { + if (fp->fd_top > firstlnum) { + // We will move the start of this fold up, hence we move all + // nested folds (with relative line numbers) down. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum), 0L); - else - /* like lines are deleted */ + (linenr_T)0, (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum), 0L); + } else { + // Will move fold down, move nested folds relatively up. foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, - (long)(firstlnum - fp->fd_top - 1), - (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum)); + (linenr_T)0, + (long)(firstlnum - fp->fd_top - 1), + (linenr_T)MAXLNUM, + (long)(fp->fd_top - firstlnum)); + } fp->fd_len += fp->fd_top - firstlnum; fp->fd_top = firstlnum; - fold_changed = TRUE; - } else if (flp->start != 0 && lvl == level - && fp->fd_top != firstlnum) { - /* Existing fold that includes startlnum must stop - * if we find the start of a new fold at the same - * level. Split it. Delete contained folds at - * this point to split them too. */ - foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top, - flp->lnum - fp->fd_top); + fold_changed = true; + } else if ((flp->start != 0 && lvl == level) + || (firstlnum != startlnum)) { + // Before there was a fold spanning from above startlnum to below + // firstlnum. This fold is valid above startlnum (because we are + // not updating that range), but there is now a break in it. + // If the break is because we are now forced to start a new fold + // at the level "level" at line fline->lnum, then we need to + // split the fold at fline->lnum. + // If the break is because the range [startlnum, firstlnum) is + // now at a lower indent than "level", we need to split the fold + // in this range. + // Any splits have to be done recursively. + linenr_T breakstart; + linenr_T breakend; + if (firstlnum != startlnum) { + breakstart = startlnum; + breakend = firstlnum; + } else { + breakstart = flp->lnum; + breakend = flp->lnum; + } + foldRemove(&fp->fd_nested, breakstart - fp->fd_top, + breakend - fp->fd_top); i = (int)(fp - (fold_T *)gap->ga_data); - foldSplit(gap, i, flp->lnum, flp->lnum - 1); + foldSplit(gap, i, breakstart, breakend - 1); fp = (fold_T *)gap->ga_data + i + 1; /* If using the "marker" or "syntax" method, we * need to continue until the end of the fold is @@ -2267,6 +2286,16 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, || getlevel == foldlevelSyntax) finish = TRUE; } + if (fp->fd_top == startlnum && concat) { + i = (int)(fp - (fold_T *)gap->ga_data); + if (i != 0) { + fp2 = fp - 1; + if (fp2->fd_top + fp2->fd_len == fp->fd_top) { + foldMerge(fp2, gap, fp); + fp = fp2; + } + } + } break; } if (fp->fd_top >= startlnum) { diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 976c6b5cd1..46c54e8614 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -100,22 +100,6 @@ func! Test_indent_fold2() bw! endfunc -func Test_folds_marker_in_comment() - new - call setline(1, ['" foo', 'bar', 'baz']) - setl fen fdm=marker - setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s - norm! zf2j - setl nofen - :1y - call assert_equal(['" foo{{{'], getreg(0,1,1)) - :+2y - call assert_equal(['baz"}}}'], getreg(0,1,1)) - - set foldmethod& - bwipe! -endfunc - func Test_manual_fold_with_filter() if !executable('cat') return @@ -138,6 +122,108 @@ func Test_manual_fold_with_filter() endfor endfunc +func! Test_indent_fold_with_read() + new + set foldmethod=indent + call setline(1, repeat(["\a"], 4)) + for n in range(1, 4) + call assert_equal(1, foldlevel(n)) + endfor + + call writefile(["a", "", "\a"], 'Xfile') + foldopen + 2read Xfile + %foldclose + call assert_equal(1, foldlevel(1)) + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(1, foldlevel(5)) + call assert_equal(7, foldclosedend(5)) + + bwipe! + set foldmethod& + call delete('Xfile') +endfunc + +func Test_combining_folds_indent() + new + let one = "\a" + let zero = 'a' + call setline(1, [one, one, zero, zero, zero, one, one, one]) + set foldmethod=indent + 3,5d + %foldclose + call assert_equal(5, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + +func Test_combining_folds_marker() + new + call setline(1, ['{{{', '}}}', '', '', '', '{{{', '', '}}}']) + set foldmethod=marker + 3,5d + %foldclose + call assert_equal(2, foldclosedend(1)) + + set foldmethod& + bwipe! +endfunc + +func Test_folds_marker_in_comment() + new + call setline(1, ['" foo', 'bar', 'baz']) + setl fen fdm=marker + setl com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\" cms=\"%s + norm! zf2j + setl nofen + :1y + call assert_equal(['" foo{{{'], getreg(0,1,1)) + :+2y + call assert_equal(['baz"}}}'], getreg(0,1,1)) + + set foldmethod& + bwipe! +endfunc + +func s:TestFoldExpr(lnum) + let thisline = getline(a:lnum) + if thisline == 'a' + return 1 + elseif thisline == 'b' + return 0 + elseif thisline == 'c' + return '<1' + elseif thisline == 'd' + return '>1' + endif + return 0 +endfunction + +func Test_update_folds_expr_read() + new + call setline(1, ['a', 'a', 'a', 'a', 'a', 'a']) + set foldmethod=expr + set foldexpr=s:TestFoldExpr(v:lnum) + 2 + foldopen + call writefile(['b', 'b', 'a', 'a', 'd', 'a', 'a', 'c'], 'Xfile') + read Xfile + %foldclose + call assert_equal(2, foldclosedend(1)) + call assert_equal(0, foldlevel(3)) + call assert_equal(0, foldlevel(4)) + call assert_equal(6, foldclosedend(5)) + call assert_equal(10, foldclosedend(7)) + call assert_equal(14, foldclosedend(11)) + + call delete('Xfile') + bwipe! + set foldmethod& foldexpr& +endfunc + func! Test_move_folds_around_manual() new let input = PrepIndent("a") + PrepIndent("b") + PrepIndent("c") -- cgit From f4a3a96b6852f2eb5cf68d26b2bf58123c39c602 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 11:06:26 -0400 Subject: Add handling for MSGPACK_OBJECT_FLOAT{32,64} msgpack-c previously only had MSGPACK_OBJECT_FLOAT, which was a 64-bit value. Now, 32-bit and 64-bit floats are supported as distinct types, but we'll simply continue to treat everything as 64-bit types. --- src/nvim/eval/decode.c | 8 +++++++- src/nvim/msgpack_rpc/helpers.c | 13 ++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 3cb68e093b..fb31a65971 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -973,7 +973,13 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { *rettv = (typval_T) { .v_type = VAR_FLOAT, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 808bb863fd..4d8a9984e1 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -114,7 +114,13 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) } break; } - case MSGPACK_OBJECT_FLOAT: { +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else + case MSGPACK_OBJECT_FLOAT: +#endif + { STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), "Msgpack floating-point size does not match API integer"); *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); @@ -181,7 +187,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) case MSGPACK_OBJECT_BOOLEAN: case MSGPACK_OBJECT_POSITIVE_INTEGER: case MSGPACK_OBJECT_NEGATIVE_INTEGER: +#ifdef NVIM_MSGPACK_HAS_FLOAT32 + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: +#else case MSGPACK_OBJECT_FLOAT: +#endif case MSGPACK_OBJECT_EXT: case MSGPACK_OBJECT_MAP: case MSGPACK_OBJECT_ARRAY: { -- cgit From 338da727cdb19a15a0b001707e8778e10977e65c Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 30 Mar 2017 22:11:40 -0400 Subject: coverity/161216: Ensure buf is valid for lifetime of defstr Depending on the type of argument for input()/inputdialog()'s {text} argument, defstr may point to buf. Therefore it needs to be in scope for the lifetime of defstr. Also, use a different buffer for the handling of the 3rd argument to input()/inputdialog(). Although the buffer defstr points to is used immediately, it avoids potential mishaps if the code changes. --- src/nvim/eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80c2fe10d7..c683fe4e10 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11008,18 +11008,19 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmdline_row = msg_row; const char *defstr = ""; + char buf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - char buf[NUMBUFLEN]; defstr = tv_get_string_buf_chk(&argvars[1], buf); if (defstr != NULL) { stuffReadbuffSpec(defstr); } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { + char buf2[NUMBUFLEN]; // input() with a third argument: completion rettv->vval.v_string = NULL; - const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf); + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf2); if (xp_name == NULL) { return; } -- cgit From 030c0588a04c7b201ed21ab6bcbc38c040e3d5b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 10:51:41 +0300 Subject: cmake: Add `clint` target to build Makefile Allows linting only modified files and linting multiple files in parallel. In the current state is rather slow because errors.json is a 6 MiB file and needs to be reparsed each time. Results on my system (6-core): # In build dir, actually parallel make -j5 clint 241.24s user 8.39s system 334% cpu 1:14.74 total # In root, one process make -j5 clint 60.69s user 0.37s system 93% cpu 1:05.19 total In both cases download time included. That is not well for travis (though I would keep travis as-is because new variant will fail before checking all files), but already good enough for regular development: total times are nearly identical and this is the *full* build, further `make -C build clint` will check only modified files. --- src/nvim/CMakeLists.txt | 147 ++++++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index a3bacaa9d2..68c0339dc5 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -10,6 +10,7 @@ if(USE_GCOV) endif() endif() +set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua) file(GLOB API_HEADERS api/*.h) @@ -34,11 +35,16 @@ set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) +set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) +set(LINT_SUPPRESS_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json") +set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) +set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) +file(MAKE_DIRECTORY ${TOUCHES_DIR}) file(MAKE_DIRECTORY ${GENERATED_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) @@ -73,6 +79,8 @@ file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) list(SORT NVIM_SOURCES) list(SORT NVIM_HEADERS) +list(APPEND LINT_NVIM_SOURCES ${NVIM_SOURCES} ${NVIM_HEADERS}) + foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") @@ -393,76 +401,82 @@ add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NVIM_GENERATED_FOR_SOURCES} ${NV target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) -set(NO_SINGLE_CHECK_HEADERS - cursor_shape - diff - digraph - ex_cmds - ex_getln - file_search - fold - getchar - hardcopy - if_cscope - if_cscope_defs - mark - mbyte - memfile_defs - memline - memline_defs - menu - misc2 - move - msgpack_rpc/server - ops - option - os/shell - os_unix - os/win_defs - popupmnu - quickfix - regexp - regexp_defs - screen - search - sha256 - sign_defs - spell - spellfile - syntax - syntax_defs - tag - terminal - tui/tui - ugrid - ui - ui_bridge - undo - undo_defs - version - window -) -foreach(hfile ${NVIM_HEADERS}) - get_filename_component(full_d ${hfile} PATH) +function(get_test_target prefix sfile relative_path_var target_var) + get_filename_component(full_d "${sfile}" PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") if(${d} MATCHES "^[.][.]") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() - get_filename_component(r ${hfile} NAME_WE) + get_filename_component(r "${sfile}" NAME) if(NOT ${d} EQUAL ".") set(r "${d}/${r}") endif() + string(REGEX REPLACE "[/.]" "-" suffix "${r}") + set(${relative_path_var} ${r} PARENT_SCOPE) + set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) +endfunction() + +set(NO_SINGLE_CHECK_HEADERS + cursor_shape.h + diff.h + digraph.h + ex_cmds.h + ex_getln.h + file_search.h + fold.h + getchar.h + hardcopy.h + if_cscope.h + if_cscope_defs.h + mark.h + mbyte.h + memfile_defs.h + memline.h + memline_defs.h + menu.h + misc2.h + move.h + msgpack_rpc/server.h + ops.h + option.h + os/shell.h + os_unix.h + os/win_defs.h + popupmnu.h + quickfix.h + regexp.h + regexp_defs.h + screen.h + search.h + sha256.h + sign_defs.h + spell.h + spellfile.h + syntax.h + syntax_defs.h + tag.h + terminal.h + tui/tui.h + ugrid.h + ui.h + ui_bridge.h + undo.h + undo_defs.h + version.h + window.h +) +foreach(hfile ${NVIM_HEADERS}) + get_test_target(test-includes "${hfile}" relative_path texe) if(NOT ${hfile} MATCHES "[.]c[.]h$") - set(tsource "${GENERATED_DIR}/${r}.test-include.c") - string(REPLACE "/" "-" texe "test-incl-${r}") + set(tsource "${GENERATED_DIR}/${relative_path}.test-include.c") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") add_executable( ${texe} EXCLUDE_FROM_ALL ${tsource} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_HEADERS}) - list(FIND NO_SINGLE_CHECK_HEADERS "${r}" hfile_exclude_idx) + list(FIND NO_SINGLE_CHECK_HEADERS "${relative_path}" hfile_exclude_idx) if(${hfile_exclude_idx} EQUAL -1) list(APPEND HEADER_CHECK_TARGETS ${texe}) endif() @@ -470,4 +484,27 @@ foreach(hfile ${NVIM_HEADERS}) endforeach() add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) +function(add_download output url) + add_custom_command( + OUTPUT "${output}" + COMMAND ${CMAKE_COMMAND} -DURL=${url} -DFILE=${output} -P ${DOWNLOAD_SCRIPT} + DEPENDS ${DOWNLOAD_SCRIPT} + ) +endfunction() + +add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) + +foreach(sfile ${LINT_NVIM_SOURCES}) + get_test_target("${TOUCHES_DIR}/ran-clint" "${sfile}" r touch_file) + add_custom_command( + OUTPUT ${touch_file} + COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} src/nvim/${r} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} + DEPENDS ${sfile} ${LINT_SUPPRESS_FILE} + ) + list(APPEND LINT_TARGETS ${touch_file}) +endforeach() +add_custom_target(clint DEPENDS ${LINT_TARGETS}) + add_subdirectory(po) -- cgit From 0b528fc4b55a8875579d0a1a5dde852b6c3f55dc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 11:38:08 +0300 Subject: cmake: Use file-specific supprresses `make -C build clint` time is now make -j5 clint 95.29s user 1.86s system 409% cpu 23.751 total *without* downloading anything (much worse if something was not cached, still a bit better then top-level `make clint`). But since without neovim/bot-ci#95 it is downloading each file one-by-one total time with download (download also parallel!) is make -j5 -B clint 99.29s user 2.98s system 258% cpu 39.634 total Top-level makefile still gives make -j5 clint 59.33s user 0.28s system 95% cpu 1:02.41 total --- src/nvim/CMakeLists.txt | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 68c0339dc5..b4bc3afc05 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -36,9 +36,12 @@ set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) -set(LINT_SUPPRESS_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json") +set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") +set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json") set(LINT_PRG ${PROJECT_SOURCE_DIR}/src/clint.py) set(DOWNLOAD_SCRIPT ${PROJECT_SOURCE_DIR}/cmake/Download.cmake) +set(LINT_SUPPRESSES_ROOT ${PROJECT_BINARY_DIR}/errors) +set(LINT_SUPPRESSES_URL "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.tar.gz") include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) @@ -404,16 +407,20 @@ set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTIN function(get_test_target prefix sfile relative_path_var target_var) get_filename_component(full_d "${sfile}" PATH) file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^[.][.]") + if(d MATCHES "^[.][.]") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(r "${sfile}" NAME) - if(NOT ${d} EQUAL ".") + if(NOT d MATCHES "^[.]?$") set(r "${d}/${r}") endif() string(REGEX REPLACE "[/.]" "-" suffix "${r}") set(${relative_path_var} ${r} PARENT_SCOPE) - set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) + if(prefix STREQUAL "") + set(${target_var} "${suffix}" PARENT_SCOPE) + else() + set(${target_var} "${prefix}-${suffix}" PARENT_SCOPE) + endif() endfunction() set(NO_SINGLE_CHECK_HEADERS @@ -495,13 +502,17 @@ endfunction() add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) foreach(sfile ${LINT_NVIM_SOURCES}) - get_test_target("${TOUCHES_DIR}/ran-clint" "${sfile}" r touch_file) + get_test_target("" "${sfile}" r suffix) + set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) + set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") + add_download(${suppress_file} ${suppress_url}) + set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} - COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} src/nvim/${r} + COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} src/nvim/${r} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} - DEPENDS ${sfile} ${LINT_SUPPRESS_FILE} + DEPENDS ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) endforeach() -- cgit From 24fd125893203c667789cb574b289fc1c9eaf6bc Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 11:49:18 +0300 Subject: cmake: Allow failing to download small suppress files --- src/nvim/CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index b4bc3afc05..f5f4173879 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -491,21 +491,25 @@ foreach(hfile ${NVIM_HEADERS}) endforeach() add_custom_target(check-single-includes DEPENDS ${HEADER_CHECK_TARGETS}) -function(add_download output url) +function(add_download output url allow_failure) add_custom_command( OUTPUT "${output}" - COMMAND ${CMAKE_COMMAND} -DURL=${url} -DFILE=${output} -P ${DOWNLOAD_SCRIPT} + COMMAND + ${CMAKE_COMMAND} + -DURL=${url} -DFILE=${output} + -DALLOW_FAILURE=${allow_failure} + -P ${DOWNLOAD_SCRIPT} DEPENDS ${DOWNLOAD_SCRIPT} ) endfunction() -add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL}) +add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) foreach(sfile ${LINT_NVIM_SOURCES}) get_test_target("" "${sfile}" r suffix) set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") - add_download(${suppress_file} ${suppress_url}) + add_download(${suppress_file} ${suppress_url} on) set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} -- cgit From c61858a9978504c645f09de60daf4f9786c2220e Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:02:59 +0300 Subject: cmake: Replace RunLint.cmake with code in src/nvim/CMakeLists.txt This also removes LINT_FILE environment variable, other then that functionality is kept. It is expected that developers needing partial linting will use `make lint`, touching interesting file before (if not done already by writing to them). --- src/nvim/CMakeLists.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index f5f4173879..b19bdf2326 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -505,21 +505,32 @@ endfunction() add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off) +set(LINT_NVIM_REL_SOURCES) foreach(sfile ${LINT_NVIM_SOURCES}) get_test_target("" "${sfile}" r suffix) set(suppress_file ${LINT_SUPPRESSES_ROOT}/${suffix}.json) set(suppress_url "${LINT_SUPPRESS_URL_BASE}/${suffix}.json") + set(rsfile src/nvim/${r}) add_download(${suppress_file} ${suppress_url} on) set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}") add_custom_command( OUTPUT ${touch_file} - COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} src/nvim/${r} + COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} DEPENDS ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) + list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) endforeach() add_custom_target(clint DEPENDS ${LINT_TARGETS}) +add_custom_target( + clint-full + COMMAND + ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} +) + add_subdirectory(po) -- cgit From 4fc2be490cd075d08ef8532f954424051fe49300 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:27:13 +0300 Subject: clint: Do not report zero errors --- src/clint.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/clint.py b/src/clint.py index 61c53d128e..5174521fb8 100755 --- a/src/clint.py +++ b/src/clint.py @@ -571,7 +571,8 @@ class _CppLintState(object): for category, count in self.errors_by_category.items(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) + if self.error_count: + sys.stderr.write('Total errors found: %d\n' % self.error_count) def SuppressErrorsFrom(self, fname): """Open file and read a list of suppressed errors from it""" -- cgit From 4d0f90f94db6040841b9987d02a021b785cfe0f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 31 Mar 2017 12:28:10 +0300 Subject: cmake: Also depend on LINT_PRG --- src/nvim/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index b19bdf2326..5a5ebc4415 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -518,7 +518,7 @@ foreach(sfile ${LINT_NVIM_SOURCES}) COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} - DEPENDS ${sfile} ${suppress_file} + DEPENDS ${LINT_PRG} ${sfile} ${suppress_file} ) list(APPEND LINT_TARGETS ${touch_file}) list(APPEND LINT_NVIM_REL_SOURCES ${rsfile}) @@ -530,7 +530,7 @@ add_custom_target( COMMAND ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - DEPENDS ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} + DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} ) add_subdirectory(po) -- cgit From 53da57d27a8ee47fe42604ad07bb7c956d9012f5 Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 12:24:07 +0800 Subject: vim-patch:7.4.2236 Problem: The 'langnoremap' option leads to double negatives. And it does not work for the last character of a mapping. Solution: Add 'langremap' with the opposite value. Keep 'langnoremap' for backwards compatibility. Make it work for the last character of a mapping. Make the test work. https://github.com/vim/vim/commit/920694c1b60fac8017b8909efcc24f189804a9bb --- src/nvim/macros.h | 2 +- src/nvim/option.c | 6 ++++ src/nvim/option_defs.h | 5 +-- src/nvim/options.lua | 6 ++++ src/nvim/testdir/test_mapping.vim | 74 +++++++++++++++++++++++++++++++-------- src/nvim/version.c | 2 +- 6 files changed, 76 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 5042663041..a8df6322cf 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -94,7 +94,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (!p_lnr || (p_lnr && typebuf_maplen() == 0)) \ + && (p_lrm || (!p_lrm && KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ diff --git a/src/nvim/option.c b/src/nvim/option.c index 0b4d9aae5d..b3b4dc1e0a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3630,6 +3630,12 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, } else if ((int *)varp == &p_force_off && p_force_off == true) { p_force_off = false; return (char *)e_unsupportedoption; + } else if ((int *)varp == &p_lrm) { + // 'langremap' -> !'langnoremap' + p_lnr = !p_lrm; + } else if ((int *)varp == &p_lnr) { + // 'langnoremap' -> !'langremap' + p_lrm = !p_lnr; // 'undofile' } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { // Only take action when the option was set. When reset we do not diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 2475a0b6a1..4ee0f4f225 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -476,8 +476,9 @@ EXTERN char_u *p_isp; // 'isprint' EXTERN int p_js; // 'joinspaces' EXTERN char_u *p_kp; // 'keywordprg' EXTERN char_u *p_km; // 'keymodel' -EXTERN char_u *p_langmap; // 'langmap'*/ -EXTERN int p_lnr; // 'langnoremap'*/ +EXTERN char_u *p_langmap; // 'langmap' +EXTERN int p_lnr; // 'langnoremap' +EXTERN int p_lrm; // 'langremap' EXTERN char_u *p_lm; // 'langmenu' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index ee2b8a563d..9dff3410d6 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1346,6 +1346,12 @@ return { varname='p_lnr', defaults={if_true={vi=false, vim=true}} }, + { + full_name='langremap', abbreviation='lrm', + type='bool', scope={'global'}, + varname='p_lrm', + defaults={if_true={vi=true, vim=false}} + }, { full_name='laststatus', abbreviation='ls', type='number', scope={'global'}, diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index d937565ce5..3b6dcdccf5 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -35,29 +35,73 @@ func Test_map_ctrl_c_visual() endfunc func Test_map_langmap() - " langmap should not get remapped in insert mode - inoremap { FAIL_ilangmap - set langmap=+{ langnoremap + if !has('langmap') + return + endif + + " check langmap applies in normal mode + set langmap=+- nolangremap + new + call setline(1, ['a', 'b', 'c']) + 2 + call assert_equal('b', getline('.')) + call feedkeys("+", "xt") + call assert_equal('a', getline('.')) + + " check no remapping + map x + + 2 + call feedkeys("x", "xt") + call assert_equal('c', getline('.')) + + " check with remapping + set langremap + 2 + call feedkeys("x", "xt") + call assert_equal('a', getline('.')) + + unmap x + bwipe! + + " 'langnoremap' follows 'langremap' and vise versa + set langremap + set langnoremap + call assert_equal(0, &langremap) + set langremap + call assert_equal(0, &langnoremap) + set nolangremap + call assert_equal(1, &langnoremap) + + " langmap should not apply in insert mode, 'langremap' doesn't matter + set langmap=+{ nolangremap call feedkeys("Go+\", "xt") call assert_equal('+', getline('$')) - - " Insert-mode expr mapping with langmap - inoremap { "FAIL_iexplangmap" + set langmap=+{ langremap call feedkeys("Go+\", "xt") call assert_equal('+', getline('$')) - iunmap { - " langmap should not get remapped in Command-line mode - cnoremap { FAIL_clangmap + " langmap used for register name in insert mode. + call setreg('a', 'aaaa') + call setreg('b', 'bbbb') + call setreg('c', 'cccc') + set langmap=ab langremap + call feedkeys("Go\a\", "xt") + call assert_equal('bbbb', getline('$')) + call feedkeys("Go\\a\", "xt") + call assert_equal('bbbb', getline('$')) + " mapping does not apply + imap c a + call feedkeys("Go\c\", "xt") + call assert_equal('cccc', getline('$')) + imap a c + call feedkeys("Go\a\", "xt") + call assert_equal('bbbb', getline('$')) + + " langmap should not apply in Command-line mode + set langmap=+{ nolangremap call feedkeys(":call append(line('$'), '+')\", "xt") call assert_equal('+', getline('$')) - cunmap { - " Command-line mode expr mapping with langmap - cnoremap { "FAIL_cexplangmap" - call feedkeys(":call append(line('$'), '+')\", "xt") - call assert_equal('+', getline('$')) - cunmap { set nomodified endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index fdf5436a98..ba1ebc8f2c 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -205,7 +205,7 @@ static const int included_patches[] = { // 2239, // 2238 NA 2237, - // 2236, + 2236, 2235, // 2234 NA 2233, -- cgit From 45a13c4bbc4a7c4f62a299c832ecc192d8f3cd0a Mon Sep 17 00:00:00 2001 From: lonerover Date: Wed, 22 Mar 2017 12:52:15 +0800 Subject: vim-patch:7.4.2306 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Default value for 'langremap' is wrong. Solution: Set the right value. (Jürgen Krämer) Add a test. https://github.com/vim/vim/commit/da9ce2cde11ddd0e16cdfbab6d4ac4e8110218e1 --- src/nvim/testdir/test_mapping.vim | 8 ++++++++ src/nvim/version.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 3b6dcdccf5..6b313ff54f 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -72,6 +72,14 @@ func Test_map_langmap() set nolangremap call assert_equal(1, &langnoremap) + " check default values + set langnoremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + set langremap& + call assert_equal(1, &langnoremap) + call assert_equal(0, &langremap) + " langmap should not apply in insert mode, 'langremap' doesn't matter set langmap=+{ nolangremap call feedkeys("Go+\", "xt") diff --git a/src/nvim/version.c b/src/nvim/version.c index ba1ebc8f2c..0ee0419849 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -135,7 +135,7 @@ static const int included_patches[] = { 2309, // 2308 NA 2307, - // 2306, + 2306, 2305, // 2304 NA 2303, -- cgit From cc4523013f8e693f92de3b96ff36065895c60974 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 21:13:21 +0300 Subject: eval,fileio: Omit additional fsync() call Fixes #6420 --- src/nvim/eval.c | 2 +- src/nvim/os/fileio.c | 31 +++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 15b712e7de..a6774a3a0b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17289,7 +17289,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if ((error = file_fsync(fp)) != 0) { + if ((error = file_flush(fp)) != 0) { goto write_list_error; } return true; diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 775f2bd449..3c928363cc 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -143,21 +143,36 @@ int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL /// @param[in,out] fp File to work with. /// /// @return 0 or error code. -int file_fsync(FileDescriptor *const fp) +int file_flush(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL { if (!fp->wr) { return 0; } file_rb_write_full_cb(fp->rv, fp); - if (fp->_error != 0) { - const int error = fp->_error; - fp->_error = 0; - return error; + const int error = fp->_error; + fp->_error = 0; + return error; +} + +/// Flush file modifications to disk and run fsync() +/// +/// @param[in,out] fp File to work with. +/// +/// @return 0 or error code. +int file_fsync(FileDescriptor *const fp) + FUNC_ATTR_NONNULL_ALL +{ + if (!fp->wr) { + return 0; } - const int error = os_fsync(fp->fd); - if (error != UV_EINVAL && error != UV_EROFS) { - return error; + const int flush_error = file_flush(fp); + if (flush_error != 0) { + return flush_error; + } + const int fsync_error = os_fsync(fp->fd); + if (fsync_error != UV_EINVAL && fsync_error != UV_EROFS) { + return fsync_error; } return 0; } -- cgit From 19690d4a25f15dfa752ac3723384f1d33f06329a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Apr 2017 22:26:50 +0300 Subject: eval: Do not allocate FileDescriptor --- src/nvim/eval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a6774a3a0b..f0d78a2508 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17439,21 +17439,21 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (fname == NULL) { return; } - FileDescriptor *fp; + FileDescriptor fp; int error; rettv->vval.v_number = -1; if (*fname == NUL) { EMSG(_("E482: Can't open file with an empty name")); - } else if ((fp = file_open_new(&error, fname, - ((append ? kFileAppend : kFileTruncate) - | kFileCreate), 0666)) == NULL) { + } else if ((error = file_open(&fp, fname, + ((append ? kFileAppend : kFileTruncate) + | kFileCreate), 0666)) != 0) { emsgf(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error)); } else { - if (write_list(fp, argvars[0].vval.v_list, binary)) { + if (write_list(&fp, argvars[0].vval.v_list, binary)) { rettv->vval.v_number = 0; } - if ((error = file_free(fp)) != 0) { + if ((error = file_close(&fp)) != 0) { emsgf(_("E80: Error when closing file %s: %s"), fname, os_strerror(error)); } -- cgit From 337b6179df852350b52409fd3806e4b47ab2875b Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Sat, 1 Apr 2017 20:50:29 +0100 Subject: 'pastetoggle': support value >1 char (#6421) If we `set pastetoggle=abcde`, and manually type it, then `vgetorpeek()` sees part of the option before it has all been inserted into the typebuffer. To signify this it sets `keylen = KEYLEN_PART_KEY`, but the condition about whether to return the current key from `vgetorpeek()` only checks for `keylen = KEYLEN_PART_MAP`. Add a check for `KEYLEN_PART_KEY` to account for the `'pastetoggle'` option. --- src/nvim/getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7143819e21..b83681ad01 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1903,7 +1903,7 @@ static int vgetorpeek(int advance) } if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { + && keylen != KEYLEN_PART_MAP && keylen != KEYLEN_PART_KEY) { // No matching mapping found or found a non-matching mapping that // matches at least what the matching mapping matched keylen = 0; -- cgit From dd4a5fcbb65ade08b5d2c7951b2924d2d04dc99e Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Mon, 20 Mar 2017 22:56:58 +0100 Subject: tui: 'guicursor' shape #6044 Closes #2583 --- src/nvim/api/ui.c | 10 +++ src/nvim/cursor_shape.c | 140 +++++++++++++++++++++++++++------------ src/nvim/cursor_shape.h | 73 +++++++++++---------- src/nvim/syntax.c | 53 +++++++-------- src/nvim/tui/tui.c | 170 +++++++++++++++++++++++++++++++++++------------- src/nvim/tui/tui.h | 2 + src/nvim/ui.c | 9 +++ src/nvim/ui.h | 1 + src/nvim/ui_bridge.c | 19 ++++++ 9 files changed, 330 insertions(+), 147 deletions(-) (limited to 'src') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 625bcc6b4b..a95be0fabb 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/popupmnu.h" +#include "nvim/cursor_shape.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -69,6 +70,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->clear = remote_ui_clear; ui->eol_clear = remote_ui_eol_clear; ui->cursor_goto = remote_ui_cursor_goto; + ui->cursor_style_set = remote_ui_cursor_style_set; ui->update_menu = remote_ui_update_menu; ui->busy_start = remote_ui_busy_start; ui->busy_stop = remote_ui_busy_stop; @@ -298,6 +300,14 @@ static void remote_ui_scroll(UI *ui, int count) push_call(ui, "scroll", args); } +static void remote_ui_cursor_style_set(UI *ui, Dictionary styles) +{ + Array args = ARRAY_DICT_INIT; + Object copy = copy_object(DICTIONARY_OBJ(styles)); + ADD(args, copy); + push_call(ui, "cursor_style_set", args); +} + static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) { Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index b50462664c..9f0eb215ef 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -7,40 +7,84 @@ #include "nvim/charset.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ui.h" -/* - * Handling of cursor and mouse pointer shapes in various modes. - */ - +/// Handling of cursor and mouse pointer shapes in various modes. static cursorentry_T shape_table[SHAPE_IDX_COUNT] = { - /* The values will be filled in from the 'guicursor' and 'mouseshape' - * defaults when Vim starts. - * Adjust the SHAPE_IDX_ defines when making changes! */ - {0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE}, - {0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR}, + // The values will be filled in from the 'guicursor' and 'mouseshape' + // defaults when Vim starts. + // Adjust the SHAPE_IDX_ defines when making changes! + { "normal", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, + { "insert", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, + { "replace", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_normal", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_insert", 0, + 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_replace", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, + { "pending", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual_select", + 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmd_line", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, + { "statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, + { "drag_statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, + { "vsep", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, + { "vdrag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, + { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE }, + { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE }, + { "match_paren", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, }; -/* - * Parse the 'guicursor' option ("what" is SHAPE_CURSOR) or 'mouseshape' - * ("what" is SHAPE_MOUSE). - * Returns error message for an illegal option, NULL otherwise. - */ +/// Converts cursor_shapes into a Dictionary of dictionaries +/// @return a dictionary of the form {"normal" : { "cursor_shape": ... }, ...} +Dictionary cursor_shape_dict(void) +{ + Dictionary all = ARRAY_DICT_INIT; + + for (int i = 0; i < SHAPE_IDX_COUNT; i++) { + Dictionary dic = ARRAY_DICT_INIT; + cursorentry_T *cur = &shape_table[i]; + if (cur->used_for & SHAPE_MOUSE) { + PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); + } + if (cur->used_for & SHAPE_CURSOR) { + String shape_str; + switch (cur->shape) { + case SHAPE_BLOCK: shape_str = cstr_to_string("block"); break; + case SHAPE_VER: shape_str = cstr_to_string("vertical"); break; + case SHAPE_HOR: shape_str = cstr_to_string("horizontal"); break; + default: shape_str = cstr_to_string("unknown"); + } + PUT(dic, "cursor_shape", STRING_OBJ(shape_str)); + PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); + PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); + PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); + PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); + PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); + PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); + } + PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); + + PUT(all, cur->full_name, DICTIONARY_OBJ(dic)); + } + + return all; +} + +/// Parse the 'guicursor' option +/// +/// @param what either SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') +/// +/// @returns error message for an illegal option, NULL otherwise. char_u *parse_shape_opt(int what) { char_u *modep; @@ -71,19 +115,18 @@ char_u *parse_shape_opt(int what) return (char_u *)N_("E546: Illegal mode"); commap = vim_strchr(modep, ','); - /* - * Repeat for all mode's before the colon. - * For the 'a' mode, we loop to handle all the modes. - */ + // Repeat for all mode's before the colon. + // For the 'a' mode, we loop to handle all the modes. all_idx = -1; assert(modep < colonp); while (modep < colonp || all_idx >= 0) { if (all_idx < 0) { - /* Find the mode. */ - if (modep[1] == '-' || modep[1] == ':') + // Find the mode + if (modep[1] == '-' || modep[1] == ':') { len = 1; - else + } else { len = 2; + } if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') { all_idx = SHAPE_IDX_COUNT - 1; @@ -100,11 +143,11 @@ char_u *parse_shape_opt(int what) modep += len + 1; } - if (all_idx >= 0) + if (all_idx >= 0) { idx = all_idx--; - else if (round == 2) { + } else if (round == 2) { { - /* Set the defaults, for the missing parts */ + // Set the defaults, for the missing parts shape_table[idx].shape = SHAPE_BLOCK; shape_table[idx].blinkwait = 700L; shape_table[idx].blinkon = 400L; @@ -208,6 +251,23 @@ char_u *parse_shape_opt(int what) shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm; } } - + ui_cursor_style_set(); return NULL; } + + +/// Map cursor mode from string to integer +/// +/// @param mode Fullname of the mode whose id we are looking for +/// @return -1 in case of failure, else the matching SHAPE_ID* integer +int cursor_mode_str2int(const char *mode) +{ + for (int current_mode = 0; current_mode < SHAPE_IDX_COUNT; current_mode++) { + if (strcmp(shape_table[current_mode].full_name, mode) == 0) { + return current_mode; + } + } + ELOG("Unknown mode %s", mode); + return -1; +} + diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 9ce1b6e0a0..127d0df555 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -1,32 +1,34 @@ #ifndef NVIM_CURSOR_SHAPE_H #define NVIM_CURSOR_SHAPE_H -/* - * struct to store values from 'guicursor' and 'mouseshape' - */ -/* Indexes in shape_table[] */ -#define SHAPE_IDX_N 0 /* Normal mode */ -#define SHAPE_IDX_V 1 /* Visual mode */ -#define SHAPE_IDX_I 2 /* Insert mode */ -#define SHAPE_IDX_R 3 /* Replace mode */ -#define SHAPE_IDX_C 4 /* Command line Normal mode */ -#define SHAPE_IDX_CI 5 /* Command line Insert mode */ -#define SHAPE_IDX_CR 6 /* Command line Replace mode */ -#define SHAPE_IDX_O 7 /* Operator-pending mode */ -#define SHAPE_IDX_VE 8 /* Visual mode with 'selection' exclusive */ -#define SHAPE_IDX_CLINE 9 /* On command line */ -#define SHAPE_IDX_STATUS 10 /* A status line */ -#define SHAPE_IDX_SDRAG 11 /* dragging a status line */ -#define SHAPE_IDX_VSEP 12 /* A vertical separator line */ -#define SHAPE_IDX_VDRAG 13 /* dragging a vertical separator line */ -#define SHAPE_IDX_MORE 14 /* Hit-return or More */ -#define SHAPE_IDX_MOREL 15 /* Hit-return or More in last line */ -#define SHAPE_IDX_SM 16 /* showing matching paren */ -#define SHAPE_IDX_COUNT 17 +/// struct to store values from 'guicursor' and 'mouseshape' +/// Indexes in shape_table[] +typedef enum { +SHAPE_IDX_N = 0, ///< Normal mode +SHAPE_IDX_V = 1, ///< Visual mode +SHAPE_IDX_I = 2, ///< Insert mode +SHAPE_IDX_R = 3, ///< Replace mode +SHAPE_IDX_C = 4, ///< Command line Normal mode +SHAPE_IDX_CI = 5, ///< Command line Insert mode +SHAPE_IDX_CR = 6, ///< Command line Replace mode +SHAPE_IDX_O = 7, ///< Operator-pending mode +SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive +SHAPE_IDX_CLINE = 9, ///< On command line +SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_SDRAG = 11, ///< dragging a status line +SHAPE_IDX_VSEP = 12, ///< A vertical separator line +SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line +SHAPE_IDX_MORE = 14, ///< Hit-return or More +SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line +SHAPE_IDX_SM = 16, ///< showing matching paren +SHAPE_IDX_COUNT = 17 +} MouseMode; -#define SHAPE_BLOCK 0 /* block cursor */ -#define SHAPE_HOR 1 /* horizontal bar cursor */ -#define SHAPE_VER 2 /* vertical bar cursor */ +typedef enum { +SHAPE_BLOCK = 0, ///< block cursor +SHAPE_HOR = 1, ///< horizontal bar cursor +SHAPE_VER = 2 ///< vertical bar cursor +} CursorShape; #define MSHAPE_NUMBERED 1000 /* offset for shapes identified by number */ #define MSHAPE_HIDE 1 /* hide mouse pointer */ @@ -35,16 +37,17 @@ #define SHAPE_CURSOR 2 /* used for text cursor shape */ typedef struct cursor_entry { - int shape; /* one of the SHAPE_ defines */ - int mshape; /* one of the MSHAPE defines */ - int percentage; /* percentage of cell for bar */ - long blinkwait; /* blinking, wait time before blinking starts */ - long blinkon; /* blinking, on time */ - long blinkoff; /* blinking, off time */ - int id; /* highlight group ID */ - int id_lm; /* highlight group ID for :lmap mode */ - char *name; /* mode name (fixed) */ - char used_for; /* SHAPE_MOUSE and/or SHAPE_CURSOR */ + char *full_name; ///< mode full name + CursorShape shape; ///< cursor shape: one of the SHAPE_ defines + int mshape; ///< mouse shape: one of the MSHAPE defines + int percentage; ///< percentage of cell for bar + long blinkwait; ///< blinking, wait time before blinking starts + long blinkon; ///< blinking, on time + long blinkoff; ///< blinking, off time + int id; ///< highlight group ID + int id_lm; ///< highlight group ID for :lmap mode + char *name; ///< mode short name + char used_for; ///< SHAPE_MOUSE and/or SHAPE_CURSOR } cursorentry_T; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4a7b4a0eac..4f2f44ff86 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -42,29 +42,29 @@ static bool did_syntax_onoff = false; -// Structure that stores information about a highlight group. -// The ID of a highlight group is also called group ID. It is the index in -// the highlight_ga array PLUS ONE. +/// Structure that stores information about a highlight group. +/// The ID of a highlight group is also called group ID. It is the index in +/// the highlight_ga array PLUS ONE. struct hl_group { - char_u *sg_name; // highlight group name - char_u *sg_name_u; // uppercase of sg_name - int sg_attr; // Screen attr - int sg_link; // link to this highlight group ID - int sg_set; // combination of SG_* flags - scid_T sg_scriptID; // script in which the group was last set + char_u *sg_name; ///< highlight group name + char_u *sg_name_u; ///< uppercase of sg_name + int sg_attr; ///< Screen attr + int sg_link; ///< link to this highlight group ID + int sg_set; ///< combination of SG_* flags + scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs - int sg_cterm; // "cterm=" highlighting attr - int sg_cterm_fg; // terminal fg color number + 1 - int sg_cterm_bg; // terminal bg color number + 1 - int sg_cterm_bold; // bold attr was set for light color + int sg_cterm; ///< "cterm=" highlighting attr + int sg_cterm_fg; ///< terminal fg color number + 1 + int sg_cterm_bg; ///< terminal bg color number + 1 + int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs - int sg_gui; // "gui=" highlighting attributes - RgbValue sg_rgb_fg; // RGB foreground color - RgbValue sg_rgb_bg; // RGB background color - RgbValue sg_rgb_sp; // RGB special color - uint8_t *sg_rgb_fg_name; // RGB foreground color name - uint8_t *sg_rgb_bg_name; // RGB background color name - uint8_t *sg_rgb_sp_name; // RGB special color name + int sg_gui; ///< "gui=" highlighting attributes + RgbValue sg_rgb_fg; ///< RGB foreground color + RgbValue sg_rgb_bg; ///< RGB background color + RgbValue sg_rgb_sp; ///< RGB special color + uint8_t *sg_rgb_fg_name; ///< RGB foreground color name + uint8_t *sg_rgb_bg_name; ///< RGB background color name + uint8_t *sg_rgb_sp_name; ///< RGB special color name }; #define SG_CTERM 2 // cterm has been set @@ -7165,12 +7165,13 @@ int syn_namen2id(char_u *linep, int len) return id; } -/* - * Find highlight group name in the table and return it's ID. - * The argument is a pointer to the name and the length of the name. - * If it doesn't exist yet, a new entry is created. - * Return 0 for failure. - */ +/// Find highlight group name in the table and return it's ID. +/// If it doesn't exist yet, a new entry is created. +/// +/// @param pp Highlight group name +/// @param len length of \p pp +/// +/// @return 0 for failure else the id of the group int syn_check_group(char_u *pp, int len) { char_u *name = vim_strnsave(pp, len); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index ebdfb1e7a1..ebcef33fa1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -31,6 +31,8 @@ #include "nvim/ugrid.h" #include "nvim/tui/input.h" #include "nvim/tui/tui.h" +#include "nvim/cursor_shape.h" +#include "nvim/syntax.h" // Space reserved in the output buffer to restore the cursor to normal when // flushing. No existing terminal will require 32 bytes to do that. @@ -69,12 +71,12 @@ typedef struct { bool can_use_terminal_scroll; bool mouse_enabled; bool busy; + cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs print_attrs; int showing_mode; struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block; int set_rgb_foreground, set_rgb_background; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; @@ -97,6 +99,7 @@ UI *tui_start(void) ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; ui->cursor_goto = tui_cursor_goto; + ui->cursor_style_set = tui_cursor_style_set; ui->update_menu = tui_update_menu; ui->busy_start = tui_busy_start; ui->busy_stop = tui_busy_stop; @@ -131,9 +134,6 @@ static void terminfo_start(UI *ui) data->unibi_ext.disable_mouse = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.set_cursor_shape_bar = -1; - data->unibi_ext.set_cursor_shape_ul = -1; - data->unibi_ext.set_cursor_shape_block = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; data->out_fd = 1; @@ -147,7 +147,6 @@ static void terminfo_start(UI *ui) } fix_terminfo(data); // Initialize the cursor shape. - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear @@ -434,6 +433,64 @@ static void tui_cursor_goto(UI *ui, int row, int col) unibi_goto(ui, row, col); } +CursorShape tui_cursor_decode_shape(const char *shape_str) +{ + CursorShape shape = 0; + if (strcmp(shape_str, "block") == 0) { + shape = SHAPE_BLOCK; + } else if (strcmp(shape_str, "vertical") == 0) { + shape = SHAPE_VER; + } else if (strcmp(shape_str, "horizontal") == 0) { + shape = SHAPE_HOR; + } else { + EMSG2(_(e_invarg2), shape_str); + } + return shape; +} + +static cursorentry_T decode_cursor_entry(Dictionary args) +{ + cursorentry_T r; + + for (size_t i = 0; i < args.size; i++) { + char *keyStr = args.items[i].key.data; + Object value = args.items[i].value; + + if (strcmp(keyStr, "cursor_shape") == 0) { + r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data); + } else if (strcmp(keyStr, "blinkon") == 0) { + r.blinkon = (int)value.data.integer; + } else if (strcmp(keyStr, "blinkoff") == 0) { + r.blinkoff = (int)value.data.integer; + } else if (strcmp(keyStr, "hl_id") == 0) { + r.id = (int)value.data.integer; + } + } + return r; +} + +static void tui_cursor_style_set(UI *ui, Dictionary args) +{ + TUIData *data = ui->data; + + for (size_t i = 0; i < args.size; i++) { + char *mode_name = args.items[i].key.data; + const int mode_id = cursor_mode_str2int(mode_name); + + if (mode_id < 0) { + WLOG("Unknown mode '%s'", mode_name); + continue; + } + cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary); + r.full_name = mode_name; + data->cursor_shapes[mode_id] = r; + } + + // force redrawal + MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); + tui_set_cursor(ui, cursor_mode); +} + static void tui_update_menu(UI *ui) { // Do nothing; menus are for GUI only @@ -467,33 +524,90 @@ static void tui_mouse_off(UI *ui) } } +/// @param mode one of SHAPE_XXX +static void tui_set_cursor(UI *ui, MouseMode mode) +{ + TUIData *data = ui->data; + cursorentry_T c = data->cursor_shapes[mode]; + int shape = c.shape; + bool inside_tmux = os_getenv("TMUX") != NULL; + unibi_var_t vars[26 + 26] = { { 0 } }; + +# define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) + // Support changing cursor shape on some popular terminals. + const char *term_prog = os_getenv("TERM_PROGRAM"); + const char *vte_version = os_getenv("VTE_VERSION"); + + if ((term_prog && !strcmp(term_prog, "Konsole")) + || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { + // Konsole uses a proprietary escape code to set the cursor shape + // and does not support DECSCUSR. + switch (shape) { + case SHAPE_BLOCK: shape = 0; break; + case SHAPE_VER: shape = 1; break; + case SHAPE_HOR: shape = 3; break; + default: WLOG("Unknown shape value %d", shape); break; + } + printf(TMUX_WRAP("\x1b]50;CursorShape=%d;BlinkingCursorEnabled=%d\x07"), + shape, (c.blinkon !=0)); + } else if (!vte_version || atoi(vte_version) >= 3900) { + // Assume that the terminal supports DECSCUSR unless it is an + // old VTE based terminal. This should not get wrapped for tmux, + // which will handle it via its Ss/Se terminfo extension - usually + // according to its terminal-overrides. + + switch (shape) { + case SHAPE_BLOCK: shape = 1; break; + case SHAPE_VER: shape = 5; break; + case SHAPE_HOR: shape = 3; break; + default: WLOG("Unknown shape value %d", shape); break; + } + data->params[0].i = shape + (c.blinkon ==0); + unibi_format(vars, vars + 26, "\x1b[%p1%d q", + data->params, out, ui, NULL, NULL); + } +} + +/// Returns cursor mode from edit mode +static MouseMode tui_mode2cursor(int mode) +{ + switch (mode) { + case INSERT: return SHAPE_IDX_I; + case CMDLINE: return SHAPE_IDX_C; + case REPLACE: return SHAPE_IDX_R; + case NORMAL: + default: return SHAPE_IDX_N; + } +} + +/// @param mode editor mode static void tui_mode_change(UI *ui, int mode) { TUIData *data = ui->data; if (mode == INSERT) { if (data->showing_mode != INSERT) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_I); } } else if (mode == CMDLINE) { if (data->showing_mode != CMDLINE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); + tui_set_cursor(ui, SHAPE_IDX_C); } } else if (mode == REPLACE) { if (data->showing_mode != REPLACE) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_ul); + tui_set_cursor(ui, SHAPE_IDX_R); } } else { assert(mode == NORMAL); if (data->showing_mode != NORMAL) { - unibi_out(ui, data->unibi_ext.set_cursor_shape_block); + tui_set_cursor(ui, SHAPE_IDX_N); } } data->showing_mode = mode; } static void tui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) + int right) { TUIData *data = ui->data; ugrid_set_scroll_region(&data->grid, top, bot, left, right); @@ -831,8 +945,6 @@ static void fix_terminfo(TUIData *data) goto end; } - bool inside_tmux = os_getenv("TMUX") != NULL; - #define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1)) if (STARTS_WITH(term, "rxvt")) { @@ -890,40 +1002,6 @@ static void fix_terminfo(TUIData *data) unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB); } - const char * env_cusr_shape = os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE"); - if (env_cusr_shape && strncmp(env_cusr_shape, "0", 1) == 0) { - goto end; - } - bool cusr_blink = env_cusr_shape && strncmp(env_cusr_shape, "2", 1) == 0; - -#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) - // Support changing cursor shape on some popular terminals. - const char *term_prog = os_getenv("TERM_PROGRAM"); - const char *vte_version = os_getenv("VTE_VERSION"); - - if ((term_prog && !strcmp(term_prog, "Konsole")) - || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { - // Konsole uses a proprietary escape code to set the cursor shape - // and does not support DECSCUSR. - data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=1\x07")); - data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=2\x07")); - data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL, - TMUX_WRAP("\x1b]50;CursorShape=0\x07")); - } else if (!vte_version || atoi(vte_version) >= 3900) { - // Assume that the terminal supports DECSCUSR unless it is an - // old VTE based terminal. This should not get wrapped for tmux, - // which will handle it via its Ss/Se terminfo extension - usually - // according to its terminal-overrides. - data->unibi_ext.set_cursor_shape_bar = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[5 q" : "\x1b[6 q"); - data->unibi_ext.set_cursor_shape_ul = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[3 q" : "\x1b[4 q"); - data->unibi_ext.set_cursor_shape_block = - (int)unibi_add_ext_str(ut, NULL, cusr_blink ? "\x1b[1 q" : "\x1b[2 q"); - } - end: // Fill some empty slots with common terminal strings data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h index 07523bc124..2915b0e2f8 100644 --- a/src/nvim/tui/tui.h +++ b/src/nvim/tui/tui.h @@ -1,6 +1,8 @@ #ifndef NVIM_TUI_TUI_H #define NVIM_TUI_TUI_H +#include "nvim/cursor_shape.h" + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.h.generated.h" #endif diff --git a/src/nvim/ui.c b/src/nvim/ui.c index ea42e3e357..babb4efa96 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -29,6 +29,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/window.h" +#include "nvim/cursor_shape.h" #ifdef FEAT_TUI # include "nvim/tui/tui.h" #else @@ -179,6 +180,7 @@ void ui_refresh(void) row = col = 0; screen_resize(width, height); pum_set_external(pum_external); + ui_cursor_style_set(); } static void ui_refresh_event(void **argv) @@ -376,6 +378,13 @@ void ui_cursor_goto(int new_row, int new_col) pending_cursor_update = true; } +void ui_cursor_style_set(void) +{ + Dictionary style = cursor_shape_dict(); + UI_CALL(cursor_style_set, style); + api_free_dictionary(style); +} + void ui_update_menu(void) { UI_CALL(update_menu); diff --git a/src/nvim/ui.h b/src/nvim/ui.h index d14bc5812c..0af0c0db65 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -22,6 +22,7 @@ struct ui_t { void (*clear)(UI *ui); void (*eol_clear)(UI *ui); void (*cursor_goto)(UI *ui, int row, int col); + void (*cursor_style_set)(UI *ui, Dictionary cursor_shapes); void (*update_menu)(UI *ui); void (*busy_start)(UI *ui); void (*busy_stop)(UI *ui); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 25861abc1b..c9bad6b254 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/ui_bridge.h" #include "nvim/ugrid.h" +#include "nvim/api/private/helpers.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_bridge.c.generated.h" @@ -59,6 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.clear = ui_bridge_clear; rv->bridge.eol_clear = ui_bridge_eol_clear; rv->bridge.cursor_goto = ui_bridge_cursor_goto; + rv->bridge.cursor_style_set = ui_bridge_cursor_styleset; rv->bridge.update_menu = ui_bridge_update_menu; rv->bridge.busy_start = ui_bridge_busy_start; rv->bridge.busy_stop = ui_bridge_busy_stop; @@ -178,6 +180,23 @@ static void ui_bridge_cursor_goto_event(void **argv) ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); } +static void ui_bridge_cursor_styleset(UI *b, Dictionary style) +{ + Object copy = copy_object(DICTIONARY_OBJ(style)); + Object *pobj = xmalloc(sizeof(copy)); + *pobj = copy; + UI_CALL(b, cursor_styleset, 2, b, pobj); +} +static void ui_bridge_cursor_styleset_event(void **argv) +{ + UI *ui = UI(argv[0]); + Object *styles = (Object *)argv[1]; + + ui->cursor_style_set(ui, styles->data.dictionary); + api_free_object(*styles); + xfree(styles); +} + static void ui_bridge_update_menu(UI *b) { UI_CALL(b, update_menu, 1, b); -- cgit From 54bab0019b3638f213608757b523062195be156b Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 21 Mar 2017 00:03:01 +0100 Subject: tui: 'guicursor' color For now only supports valid hex colors (does not check for the validity the hex color) when termguicolors is set, otherwise it won't attempt to change the cursor color. --- src/nvim/syntax.c | 45 +++++++++++++++++++++++++++------------------ src/nvim/syntax.h | 10 ++++++---- src/nvim/tui/tui.c | 19 +++++++++++++++++-- 3 files changed, 50 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4f2f44ff86..acda25e738 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -48,9 +48,9 @@ static bool did_syntax_onoff = false; struct hl_group { char_u *sg_name; ///< highlight group name char_u *sg_name_u; ///< uppercase of sg_name - int sg_attr; ///< Screen attr + int sg_attr; ///< Screen attr @see ATTR_ENTRY int sg_link; ///< link to this highlight group ID - int sg_set; ///< combination of SG_* flags + int sg_set; ///< combination of flags in \ref SG_SET scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs int sg_cterm; ///< "cterm=" highlighting attr @@ -59,6 +59,7 @@ struct hl_group { int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs int sg_gui; ///< "gui=" highlighting attributes + ///< (combination of \ref HL_ATTRIBUTES) RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color @@ -67,9 +68,12 @@ struct hl_group { uint8_t *sg_rgb_sp_name; ///< RGB special color name }; +/// \addtogroup SG_SET +/// @{ #define SG_CTERM 2 // cterm has been set #define SG_GUI 4 // gui has been set #define SG_LINK 8 // link has been set +/// @} // highlight groups for 'highlight' option static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -6093,16 +6097,16 @@ int load_colors(char_u *name) return retval; } -/* - * Handle the ":highlight .." command. - * When using ":hi clear" this is called recursively for each group with - * "forceit" and "init" both TRUE. - */ -void -do_highlight ( + +/// Handle the ":highlight .." command. +/// When using ":hi clear" this is called recursively for each group with +/// "forceit" and "init" both TRUE. +/// @param init TRUE when called for initializing +void +do_highlight( char_u *line, int forceit, - int init /* TRUE when called for initializing */ + int init ) { char_u *name_end; @@ -6704,12 +6708,10 @@ static garray_T attr_table = GA_EMPTY_INIT_VALUE; #define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] -/* - * Return the attr number for a set of colors and font. - * Add a new entry to the term_attr_table, attr_table or gui_attr_table - * if the combination is new. - * Return 0 for error. - */ +/// Return the attr number for a set of colors and font. +/// Add a new entry to the term_attr_table, attr_table or gui_attr_table +/// if the combination is new. +/// @return 0 for error. int get_attr_entry(attrentry_T *aep) { garray_T *table = &attr_table; @@ -6932,7 +6934,7 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg /// Check whether highlight group has attribute /// -/// @param[in] id Highilght group to check. +/// @param[in] id Highlight group to check. /// @param[in] flag Attribute to check. /// @param[in] modec 'g' for GUI, 'c' for term. /// @@ -8245,7 +8247,14 @@ color_name_table_T color_name_table[] = { { NULL, 0 }, }; -RgbValue name_to_color(uint8_t *name) + +/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), +/// else look into color_name_table to translate a color name to its +/// hex value +/// +/// @param[in] name string value to convert to RGB +/// return the hex value or -1 if could not find a correct value +RgbValue name_to_color(const uint8_t *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index af2ac719c6..574e3372e2 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -5,10 +5,11 @@ #include "nvim/buffer_defs.h" -/* - * Terminal highlighting attribute bits. - * Attributes above HL_ALL are used for syntax highlighting. - */ + +/// Terminal highlighting attribute bits. +/// Attributes above HL_ALL are used for syntax highlighting. +/// \addtogroup HL_ATTRIBUTES +/// @{ #define HL_NORMAL 0x00 #define HL_INVERSE 0x01 #define HL_BOLD 0x02 @@ -16,6 +17,7 @@ #define HL_UNDERLINE 0x08 #define HL_UNDERCURL 0x10 #define HL_STANDOUT 0x20 +/// @} #define HL_CONTAINED 0x01 /* not used on toplevel */ #define HL_TRANSP 0x02 /* has no highlighting */ diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index ebcef33fa1..12281246fe 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -78,6 +78,7 @@ typedef struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; int set_rgb_foreground, set_rgb_background; + int set_cursor_color; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; } TUIData; @@ -132,6 +133,7 @@ static void terminfo_start(UI *ui) data->showing_mode = 0; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; + data->unibi_ext.set_cursor_color = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; data->unibi_ext.enable_focus_reporting = -1; @@ -548,8 +550,12 @@ static void tui_set_cursor(UI *ui, MouseMode mode) case SHAPE_HOR: shape = 3; break; default: WLOG("Unknown shape value %d", shape); break; } - printf(TMUX_WRAP("\x1b]50;CursorShape=%d;BlinkingCursorEnabled=%d\x07"), - shape, (c.blinkon !=0)); + data->params[0].i = shape; + data->params[1].i = (c.blinkon ==0); + + unibi_format(vars, vars + 26, + TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), + data->params, out, ui, NULL, NULL); } else if (!vte_version || atoi(vte_version) >= 3900) { // Assume that the terminal supports DECSCUSR unless it is an // old VTE based terminal. This should not get wrapped for tmux, @@ -566,6 +572,13 @@ static void tui_set_cursor(UI *ui, MouseMode mode) unibi_format(vars, vars + 26, "\x1b[%p1%d q", data->params, out, ui, NULL, NULL); } + + if (c.id != 0 && ui->rgb) { + int attr = syn_id2attr(c.id); + attrentry_T *aep = syn_cterm_attr2entry(attr); + data->params[0].i = aep->rgb_bg_color; + unibi_out(ui, data->unibi_ext.set_cursor_color); + } } /// Returns cursor mode from edit mode @@ -1004,6 +1017,8 @@ static void fix_terminfo(TUIData *data) end: // Fill some empty slots with common terminal strings + data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( + ut, NULL, "\033]12;#%p1%06x\007"); data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, "\x1b[?1002h\x1b[?1006h"); data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL, -- cgit From c2826a7830ddba66261afdf45fcf4d0043506342 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Apr 2017 13:08:42 +0200 Subject: 'guicursor': Empty means "block cursor in all modes". Also: update default 'guicursor' to match the documentation. --- src/nvim/cursor_shape.c | 13 +++++++------ src/nvim/cursor_shape.h | 4 ++-- src/nvim/options.lua | 2 +- src/nvim/tui/tui.c | 1 - 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 9f0eb215ef..c78bcbc29d 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -45,7 +45,7 @@ static cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into a Dictionary of dictionaries -/// @return a dictionary of the form {"normal" : { "cursor_shape": ... }, ...} +/// @return dictionary of the form {"normal" : { "cursor_shape": ... }, ...} Dictionary cursor_shape_dict(void) { Dictionary all = ARRAY_DICT_INIT; @@ -82,7 +82,7 @@ Dictionary cursor_shape_dict(void) /// Parse the 'guicursor' option /// -/// @param what either SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') +/// @param what SHAPE_CURSOR or SHAPE_MOUSE ('mouseshape') /// /// @returns error message for an illegal option, NULL otherwise. char_u *parse_shape_opt(int what) @@ -103,10 +103,11 @@ char_u *parse_shape_opt(int what) * First round: check for errors; second round: do it for real. */ for (round = 1; round <= 2; ++round) { - /* - * Repeat for all comma separated parts. - */ + // Repeat for all comma separated parts. modep = p_guicursor; + if (*p_guicursor == NUL) { + modep = (char_u *)"a:block-blinkon0"; + } while (*modep != NUL) { colonp = vim_strchr(modep, ':'); if (colonp == NULL) @@ -115,7 +116,7 @@ char_u *parse_shape_opt(int what) return (char_u *)N_("E546: Illegal mode"); commap = vim_strchr(modep, ','); - // Repeat for all mode's before the colon. + // Repeat for all modes before the colon. // For the 'a' mode, we loop to handle all the modes. all_idx = -1; assert(modep < colonp); diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 127d0df555..14ace2a861 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -14,7 +14,7 @@ SHAPE_IDX_CR = 6, ///< Command line Replace mode SHAPE_IDX_O = 7, ///< Operator-pending mode SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive SHAPE_IDX_CLINE = 9, ///< On command line -SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_STATUS = 10, ///< status line SHAPE_IDX_SDRAG = 11, ///< dragging a status line SHAPE_IDX_VSEP = 12, ///< A vertical separator line SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line @@ -37,7 +37,7 @@ SHAPE_VER = 2 ///< vertical bar cursor #define SHAPE_CURSOR 2 /* used for text cursor shape */ typedef struct cursor_entry { - char *full_name; ///< mode full name + char *full_name; ///< mode description CursorShape shape; ///< cursor shape: one of the SHAPE_ defines int mshape; ///< mouse shape: one of the MSHAPE defines int percentage; ///< percentage of cell for bar diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 9dff3410d6..09f016cf5a 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1000,7 +1000,7 @@ return { deny_duplicates=true, vi_def=true, varname='p_guicursor', - defaults={if_true={vi="n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block"}} + defaults={if_true={vi="n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175"}} }, { full_name='guifont', abbreviation='gfn', diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 12281246fe..e1fec0f678 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -148,7 +148,6 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); - // Initialize the cursor shape. // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear -- cgit From 3a69dbfca6642463ca8e19f814f71791f66332f3 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 1 Apr 2017 22:32:16 +0200 Subject: api/cursor_style_set: mode descriptions --- src/nvim/cursor_shape.c | 44 +++++++++++++++++--------------------------- src/nvim/cursor_shape.h | 4 ++-- src/nvim/tui/tui.c | 4 ++-- 3 files changed, 21 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index c78bcbc29d..7ec70bb724 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -13,35 +13,25 @@ /// Handling of cursor and mouse pointer shapes in various modes. static cursorentry_T shape_table[SHAPE_IDX_COUNT] = { - // The values will be filled in from the 'guicursor' and 'mouseshape' - // defaults when Vim starts. - // Adjust the SHAPE_IDX_ defines when making changes! - { "normal", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, - { "visual", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, - { "insert", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, - { "replace", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_normal", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_insert", 0, - 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_replace", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, - { "pending", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, - { "visual_select", - 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, - { "cmd_line", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, - { "statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, - { "drag_statusline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, - { "vsep", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, - { "vdrag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, + // Values are set by 'guicursor' and 'mouseshape'. + // Adjust the SHAPE_IDX_ defines when changing this! + { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE }, + { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE }, + { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE }, + { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE }, + { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE }, + { "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE }, + { "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE }, + { "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE }, + { "vsep_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE }, + { "vsep_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE }, { "more", 0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE }, { "more_lastline", 0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE }, - { "match_paren", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, + { "showmatch", 0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR }, }; /// Converts cursor_shapes into a Dictionary of dictionaries diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 14ace2a861..0006ede31d 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -14,9 +14,9 @@ SHAPE_IDX_CR = 6, ///< Command line Replace mode SHAPE_IDX_O = 7, ///< Operator-pending mode SHAPE_IDX_VE = 8, ///< Visual mode with 'selection' exclusive SHAPE_IDX_CLINE = 9, ///< On command line -SHAPE_IDX_STATUS = 10, ///< status line +SHAPE_IDX_STATUS = 10, ///< On status line SHAPE_IDX_SDRAG = 11, ///< dragging a status line -SHAPE_IDX_VSEP = 12, ///< A vertical separator line +SHAPE_IDX_VSEP = 12, ///< On vertical separator line SHAPE_IDX_VDRAG = 13, ///< dragging a vertical separator line SHAPE_IDX_MORE = 14, ///< Hit-return or More SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index e1fec0f678..badc0cd870 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -487,7 +487,7 @@ static void tui_cursor_style_set(UI *ui, Dictionary args) data->cursor_shapes[mode_id] = r; } - // force redrawal + // force redraw MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); tui_set_cursor(ui, cursor_mode); } @@ -550,7 +550,7 @@ static void tui_set_cursor(UI *ui, MouseMode mode) default: WLOG("Unknown shape value %d", shape); break; } data->params[0].i = shape; - data->params[1].i = (c.blinkon ==0); + data->params[1].i = (c.blinkon == 0); unibi_format(vars, vars + 26, TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), -- cgit From 16babc66870b5579f3305fa1289f25e1dc496655 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sat, 1 Apr 2017 18:00:42 -0400 Subject: tui: Only enable/disable mouse when there's something to do (#6411) If we get a mouse_on/mouse_off event, but the mouse is already in the corresponding state, there's no need to send the event up to the terminal. Closes #4394 --- src/nvim/tui/tui.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 55936ad58d..ebdfb1e7a1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -452,15 +452,19 @@ static void tui_busy_stop(UI *ui) static void tui_mouse_on(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.enable_mouse); - data->mouse_enabled = true; + if (!data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.enable_mouse); + data->mouse_enabled = true; + } } static void tui_mouse_off(UI *ui) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.disable_mouse); - data->mouse_enabled = false; + if (data->mouse_enabled) { + unibi_out(ui, data->unibi_ext.disable_mouse); + data->mouse_enabled = false; + } } static void tui_mode_change(UI *ui, int mode) -- cgit From b10880dadcbd3b3ad368621f95a0f4be7e30dc0d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Apr 2017 22:11:35 +0300 Subject: eval: Make writefile() able to disable fsync() --- src/nvim/eval.c | 6 +++++- src/nvim/os/fileio.c | 20 ++++++++++++-------- src/nvim/shada.c | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f0d78a2508..8a3e3f3e22 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17421,6 +17421,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; + bool do_fsync = true; if (argvars[2].v_type != VAR_UNKNOWN) { const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { @@ -17432,6 +17433,9 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strchr(flags, 'a')) { append = true; } + if (strchr(flags, 'S')) { + do_fsync = false; + } } char buf[NUMBUFLEN]; @@ -17453,7 +17457,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (write_list(&fp, argvars[0].vval.v_list, binary)) { rettv->vval.v_number = 0; } - if ((error = file_close(&fp)) != 0) { + if ((error = file_close(&fp, do_fsync)) != 0) { emsgf(_("E80: Error when closing file %s: %s"), fname, os_strerror(error)); } diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 3c928363cc..4b7b53fc7f 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -113,27 +113,31 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, /// Close file and free its buffer /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_close(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int error = file_fsync(fp); - const int error2 = os_close(fp->fd); + const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp)); + const int close_error = os_close(fp->fd); rbuffer_free(fp->rv); - if (error2 != 0) { - return error2; + if (close_error != 0) { + return close_error; } - return error; + return flush_error; } /// Close and free file obtained using file_open_new() /// /// @param[in,out] fp File to close. +/// @param[in] do_fsync If true, use fsync() to write changes to disk. /// /// @return 0 or error code. -int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +int file_free(FileDescriptor *const fp, const bool do_fsync) + FUNC_ATTR_NONNULL_ALL { - const int ret = file_close(fp); + const int ret = file_close(fp, do_fsync); xfree(fp); return ret; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index f65fdaf1c0..c7b95958e0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -811,7 +811,7 @@ static int open_shada_file_for_reading(const char *const fname, /// Wrapper for closing file descriptors static void close_file(void *cookie) { - const int error = file_free(cookie); + const int error = file_free(cookie, true); if (error != 0) { emsgf(_(SERR "System error while closing ShaDa file: %s"), os_strerror(error)); -- cgit From 364709bedb17bfde4eb12d8f2c1427fe958dc6fc Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:35:29 +0300 Subject: fileio: Refactor errmsg handling Adds os_strerror() result to a number of places. Also since I could not track where err\* variables are NULL and where they are not, using macros to make sure that all three variables are set at once. Removes #ifdef UNIX around the use of os_fsync, makes it use os_close in place of close in some places. --- src/nvim/fileio.c | 171 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index bd632b2755..fb44d2cc1d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2258,9 +2258,16 @@ buf_write ( int len; linenr_T lnum; long nchars; - char_u *errmsg = NULL; - int errmsg_allocated = FALSE; - char_u *errnum = NULL; +#define SET_ERRMSG_NUM(num, msg) \ + errnum = num ": ", errmsg = msg, errmsgarg = 0 +#define SET_ERRMSG_ARG(msg, error) \ + errnum = NULL, errmsg = msg, errmsgarg = error +#define SET_ERRMSG(msg) \ + errnum = NULL, errmsg = msg, errmsgarg = 0 + const char *errnum = NULL; + char *errmsg = NULL; + int errmsgarg = 0; + bool errmsg_allocated = false; char_u *buffer; char_u smallbuf[SMBUFSIZE]; char_u *backup_ext; @@ -2282,7 +2289,6 @@ buf_write ( /* writing everything */ int whole = (start == 1 && end == buf->b_ml.ml_line_count); linenr_T old_line_count = buf->b_ml.ml_line_count; - int attr; int fileformat; int write_bin; struct bw_info write_info; /* info for buf_write_bytes() */ @@ -2577,13 +2583,11 @@ buf_write ( perm = file_info_old.stat.st_mode; if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ if (S_ISDIR(file_info_old.stat.st_mode)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (os_nodetype((char *)fname) != NODE_WRITABLE) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } /* It's a device of some kind (or a fifo) which we can write to @@ -2599,8 +2603,7 @@ buf_write ( */ c = os_nodetype((char *)fname); if (c == NODE_OTHER) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); + SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; } if (c == NODE_WRITABLE) { @@ -2612,8 +2615,7 @@ buf_write ( if (perm < 0) { newfile = true; } else if (os_isdir(fname)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); + SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; } if (overwriting) { @@ -2632,11 +2634,9 @@ buf_write ( if (!forceit && file_readonly) { if (vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); } else { - errnum = (char_u *)"E505: "; - errmsg = (char_u *)_("is read-only (add ! to override)"); + SET_ERRMSG_NUM("E505", _("is read-only (add ! to override)")); } goto fail; } @@ -2904,23 +2904,27 @@ buf_write ( while ((write_info.bw_len = read_eintr(fd, copybuf, BUFSIZE)) > 0) { if (buf_write_bytes(&write_info) == FAIL) { - errmsg = (char_u *)_( - "E506: Can't write to backup file (add ! to override)"); + SET_ERRMSG(_( + "E506: Can't write to backup file (add ! to override)")); break; } os_breakcheck(); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); break; } } - if (close(bfd) < 0 && errmsg == NULL) - errmsg = (char_u *)_( - "E507: Close error for backup file (add ! to override)"); - if (write_info.bw_len < 0) - errmsg = (char_u *)_( - "E508: Can't read file for backup (add ! to override)"); + int error; + if ((error = os_close(bfd)) != 0 && errmsg == NULL) { + SET_ERRMSG_ARG(_( + "E507: Close error for backup file (add ! to override): %s"), + error); + } + if (write_info.bw_len < 0) { + SET_ERRMSG(_( + "E508: Can't read file for backup (add ! to override)")); + } #ifdef UNIX set_file_time(backup, file_info_old.stat.st_atim.tv_sec, @@ -2937,18 +2941,19 @@ buf_write ( } } nobackup: - close(fd); /* ignore errors for closing read file */ + os_close(fd); /* ignore errors for closing read file */ xfree(copybuf); - if (backup == NULL && errmsg == NULL) - errmsg = (char_u *)_( - "E509: Cannot create backup file (add ! to override)"); - /* ignore errors when forceit is TRUE */ + if (backup == NULL && errmsg == NULL) { + SET_ERRMSG(_( + "E509: Cannot create backup file (add ! to override)")); + } + // Ignore errors when forceit is TRUE. if ((some_error || errmsg != NULL) && !forceit) { retval = FAIL; goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); } else { char_u *dirp; char_u *p; @@ -2963,8 +2968,7 @@ nobackup: * anyway, thus we need an extra check here. */ if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); + SET_ERRMSG_NUM("E504", _(err_readonly)); goto fail; } @@ -3028,7 +3032,7 @@ nobackup: } } if (backup == NULL && !forceit) { - errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)"); + SET_ERRMSG(_("E510: Can't make backup file (add ! to override)")); goto fail; } } @@ -3069,7 +3073,7 @@ nobackup: && !(exiting && backup != NULL)) { ml_preserve(buf, FALSE); if (got_int) { - errmsg = (char_u *)_(e_interr); + SET_ERRMSG(_(e_interr)); goto restore_backup; } } @@ -3140,8 +3144,8 @@ nobackup: */ if (*p_ccv != NUL) { wfname = vim_tempname(); - if (wfname == NULL) { /* Can't write without a tempfile! */ - errmsg = (char_u *)_("E214: Can't find temp file for writing"); + if (wfname == NULL) { // Can't write without a tempfile! + SET_ERRMSG(_("E214: Can't find temp file for writing")); goto restore_backup; } } @@ -3153,8 +3157,8 @@ nobackup: && wfname == fname ) { if (!forceit) { - errmsg = (char_u *)_( - "E213: Cannot convert (add ! to write without conversion)"); + SET_ERRMSG(_( + "E213: Cannot convert (add ! to write without conversion)")); goto restore_backup; } notconverted = TRUE; @@ -3189,11 +3193,10 @@ nobackup: if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) || (os_fileinfo_link((char *)fname, &file_info) && !os_fileinfo_id_equal(&file_info, &file_info_old))) { - errmsg = (char_u *)_("E166: Can't open linked file for writing"); - } else + SET_ERRMSG(_("E166: Can't open linked file for writing")); + } else { #endif - { - errmsg = (char_u *)_("E212: Can't open file for writing"); + SET_ERRMSG(_("E212: Can't open file for writing")); if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL && perm >= 0) { #ifdef UNIX @@ -3211,7 +3214,9 @@ nobackup: os_remove((char *)wfname); continue; } +#ifdef UNIX } +#endif } restore_backup: @@ -3253,7 +3258,7 @@ restore_backup: xfree(wfname); goto fail; } - errmsg = NULL; + SET_ERRMSG(NULL); write_info.bw_fd = fd; @@ -3373,7 +3378,6 @@ restore_backup: nchars += len; } -#if defined(UNIX) // On many journalling file systems there is a bug that causes both the // original and the backup file to be lost when halting the system right // after writing the file. That's because only the meta-data is @@ -3382,11 +3386,11 @@ restore_backup: // For a device do try the fsync() but don't complain if it does not work // (could be a pipe). // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. - if (p_fs && os_fsync(fd) != 0 && !device) { - errmsg = (char_u *)_("E667: Fsync failed"); + int error; + if (p_fs && (error = os_fsync(fd)) != 0 && !device) { + SET_ERRMSG_ARG(_("E667: Fsync failed: %s"), error); end = 0; } -#endif #ifdef HAVE_SELINUX /* Probably need to set the security context. */ @@ -3416,8 +3420,8 @@ restore_backup: } #endif - if (close(fd) != 0) { - errmsg = (char_u *)_("E512: Close failed"); + if ((error = os_close(fd)) != 0) { + SET_ERRMSG_ARG(_("E512: Close failed: %s"), error); end = 0; } @@ -3454,21 +3458,25 @@ restore_backup: if (end == 0) { if (errmsg == NULL) { if (write_info.bw_conv_error) { - if (write_info.bw_conv_error_lnum == 0) - errmsg = (char_u *)_( - "E513: write error, conversion failed (make 'fenc' empty to override)"); + if (write_info.bw_conv_error_lnum == 0) { + SET_ERRMSG(_( + "E513: write error, conversion failed " + "(make 'fenc' empty to override)")); + } else { - errmsg_allocated = TRUE; - errmsg = xmalloc(300); - vim_snprintf((char *)errmsg, 300, - _("E513: write error, conversion failed in line %" PRId64 + errmsg_allocated = true; + SET_ERRMSG(xmalloc(300)); + vim_snprintf( + errmsg, 300, + _("E513: write error, conversion failed in line %" PRIdLINENR " (make 'fenc' empty to override)"), - (int64_t)write_info.bw_conv_error_lnum); + write_info.bw_conv_error_lnum); } - } else if (got_int) - errmsg = (char_u *)_(e_interr); - else - errmsg = (char_u *)_("E514: write error (file system full?)"); + } else if (got_int) { + SET_ERRMSG(_(e_interr)); + } else { + SET_ERRMSG(_("E514: write error (file system full?)")); + } } /* @@ -3673,33 +3681,39 @@ nofail: #endif if (errmsg != NULL) { - int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0; + const size_t numlen = (errnum != NULL ? strlen(errnum) : 0); - attr = hl_attr(HLF_E); /* set highlight for error messages */ - msg_add_fname(buf, + // Put file name in IObuff with quotes. #ifndef UNIX - sfname + msg_add_fname(buf, sfname); #else - fname + msg_add_fname(buf, fname); #endif - ); /* put file name in IObuff with quotes */ - if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE) - IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL; - /* If the error message has the form "is ...", put the error number in - * front of the file name. */ + const size_t errmsglen = strlen(errmsg); + if (STRLEN(IObuff) + errmsglen + numlen >= IOSIZE) { + IObuff[IOSIZE - errmsglen - numlen - 1] = NUL; + } + // If the error message has the form "is ...", put the error number in + // front of the file name. if (errnum != NULL) { STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, (size_t)numlen); + memmove(IObuff, errnum, numlen); } - STRCAT(IObuff, errmsg); - emsg(IObuff); - if (errmsg_allocated) + xstrlcat((char *)IObuff, errmsg, IOSIZE); + if (errmsgarg != 0) { + emsgf((const char *)IObuff, os_strerror(errmsgarg)); + } else { + emsgf((const char *)IObuff); + } + if (errmsg_allocated) { xfree(errmsg); + } retval = FAIL; if (end == 0) { + const int attr = hl_attr(HLF_E); // Set highlight for error messages. MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), - attr | MSG_HIST); + attr | MSG_HIST); MSG_PUTS_ATTR(_( "don't quit the editor until the file is successfully written!"), attr | MSG_HIST); @@ -3759,6 +3773,9 @@ nofail: got_int |= prev_got_int; return retval; +#undef SET_ERRMSG +#undef SET_ERRMSG_ARG +#undef SET_ERRMSG_NUM } /* -- cgit From 8dd9c6edd87403fb583b1e8b5567987e159fd7e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:40:48 +0300 Subject: message: Do not use IObuff in emsgf --- src/nvim/message.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 83f2735b50..4423b430a5 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -573,16 +573,17 @@ void emsg_invreg(int name) /// Print an error message with unknown number of arguments bool emsgf(const char *const fmt, ...) { + static char errbuf[IOSIZE]; if (emsg_not_now()) { return true; } va_list ap; va_start(ap, fmt); - vim_vsnprintf((char *) IObuff, IOSIZE, fmt, ap, NULL); + vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); va_end(ap); - return emsg(IObuff); + return emsg(errbuf); } static void msg_emsgf_event(void **argv) -- cgit From 8eb598c08ea51731536184a4b3e98000ce073877 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 00:44:49 +0300 Subject: fixup! --- src/nvim/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 4423b430a5..1d3609291a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -583,7 +583,7 @@ bool emsgf(const char *const fmt, ...) vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); va_end(ap); - return emsg(errbuf); + return emsg((const char_u *)errbuf); } static void msg_emsgf_event(void **argv) -- cgit From 1c41b9c77552618a5010ca69bee92033c4082748 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 01:39:09 +0300 Subject: fileio: Clean up IObuff-manipulation mess --- src/nvim/fileio.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index fb44d2cc1d..e382faf7a2 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2259,7 +2259,7 @@ buf_write ( linenr_T lnum; long nchars; #define SET_ERRMSG_NUM(num, msg) \ - errnum = num ": ", errmsg = msg, errmsgarg = 0 + errnum = num, errmsg = msg, errmsgarg = 0 #define SET_ERRMSG_ARG(msg, error) \ errnum = NULL, errmsg = msg, errmsgarg = error #define SET_ERRMSG(msg) \ @@ -3681,29 +3681,21 @@ nofail: #endif if (errmsg != NULL) { - const size_t numlen = (errnum != NULL ? strlen(errnum) : 0); - - // Put file name in IObuff with quotes. #ifndef UNIX msg_add_fname(buf, sfname); #else msg_add_fname(buf, fname); #endif - const size_t errmsglen = strlen(errmsg); - if (STRLEN(IObuff) + errmsglen + numlen >= IOSIZE) { - IObuff[IOSIZE - errmsglen - numlen - 1] = NUL; - } - // If the error message has the form "is ...", put the error number in - // front of the file name. if (errnum != NULL) { - STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, numlen); - } - xstrlcat((char *)IObuff, errmsg, IOSIZE); - if (errmsgarg != 0) { - emsgf((const char *)IObuff, os_strerror(errmsgarg)); + if (errmsgarg != 0) { + emsgf("%s: %s%s: %s", errnum, IObuff, errmsg, os_strerror(errmsgarg)); + } else { + emsgf("%s: %s%s", errnum, IObuff, errmsg); + } + } else if (errmsgarg != 0) { + emsgf(errmsg, os_strerror(errmsgarg)); } else { - emsgf((const char *)IObuff); + emsgf(errmsg); } if (errmsg_allocated) { xfree(errmsg); @@ -3822,7 +3814,7 @@ static int set_rw_fname(char_u *fname, char_u *sfname) /* * Put file name into IObuff with quotes. */ -void msg_add_fname(buf_T *buf, char_u *fname) +static void msg_add_fname(buf_T *buf, char_u *fname) { if (fname == NULL) fname = (char_u *)"-stdin-"; -- cgit From 5dcf2804455f45eac8aad7d900bf60464a4b2888 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 02:03:05 +0300 Subject: fileio: Refactor msg_add_fname to something which needs no comments --- src/nvim/buffer.c | 4 ++-- src/nvim/fileio.c | 58 ++++++++++++++++++++++++++++++------------------------- src/nvim/os/env.c | 7 ++++--- src/nvim/path.c | 14 +++++++------- 4 files changed, 45 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c9101c5b53..292eb03a16 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2724,7 +2724,7 @@ fileinfo ( else name = curbuf->b_ffname; home_replace(shorthelp ? curbuf : NULL, name, p, - (int)(IOSIZE - (p - buffer)), TRUE); + (size_t)(IOSIZE - (p - buffer)), true); } vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", @@ -2889,7 +2889,7 @@ void maketitle(void) buf[off++] = ' '; buf[off++] = '('; home_replace(curbuf, curbuf->b_ffname, - buf + off, SPACE_FOR_DIR - off, TRUE); + buf + off, (size_t)(SPACE_FOR_DIR - off), true); #ifdef BACKSLASH_IN_FILENAME /* avoid "c:/name" to be reduced to "c" */ if (isalpha(buf[off]) && buf[off + 1] == ':') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e382faf7a2..1ae73e787d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -200,18 +200,14 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) { int msg_scroll_save; - if (msg_silent != 0) + if (msg_silent != 0) { return; - msg_add_fname(buf, name); /* put file name in IObuff with quotes */ - /* If it's extremely long, truncate it. */ - if (STRLEN(IObuff) > IOSIZE - 80) - IObuff[IOSIZE - 80] = NUL; - STRCAT(IObuff, s); - /* - * For the first message may have to start a new line. - * For further ones overwrite the previous one, reset msg_scroll before - * calling filemess(). - */ + } + add_quoted_fname((char *)IObuff, IOSIZE - 80, buf,(const char *)name); + xstrlcat((char *)IObuff, (const char *)s, IOSIZE); + // For the first message may have to start a new line. + // For further ones overwrite the previous one, reset msg_scroll before + // calling filemess(). msg_scroll_save = msg_scroll; if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) msg_scroll = FALSE; @@ -1800,8 +1796,8 @@ failed: } if (!filtering && !(flags & READ_DUMMY)) { - msg_add_fname(curbuf, sfname); /* fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, curbuf,(const char *)sfname); + c = false; #ifdef UNIX # ifdef S_ISFIFO @@ -3531,8 +3527,8 @@ restore_backup: fname = sfname; /* use shortname now, for the messages */ #endif if (!filtering) { - msg_add_fname(buf, fname); /* put fname in IObuff with quotes */ - c = FALSE; + add_quoted_fname((char *)IObuff, IOSIZE, buf,(const char *)fname); + c = false; if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); c = TRUE; @@ -3681,10 +3677,11 @@ nofail: #endif if (errmsg != NULL) { + // - 100 to save some space for further error message #ifndef UNIX - msg_add_fname(buf, sfname); + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)sfname); #else - msg_add_fname(buf, fname); + add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)fname); #endif if (errnum != NULL) { if (errmsgarg != 0) { @@ -3811,16 +3808,25 @@ static int set_rw_fname(char_u *fname, char_u *sfname) return OK; } -/* - * Put file name into IObuff with quotes. - */ -static void msg_add_fname(buf_T *buf, char_u *fname) +/// Put file name into the specified buffer with quotes +/// +/// Replaces home directory at the start with `~`. +/// +/// @param[out] ret_buf Buffer to save results to. +/// @param[in] buf_len ret_buf length. +/// @param[in] buf buf_T file name is coming from. +/// @param[in] fname File name to write. +static void add_quoted_fname(char *const ret_buf, const size_t buf_len, + const buf_T *const buf, const char *fname) + FUNC_ATTR_NONNULL_ARG(1) { - if (fname == NULL) - fname = (char_u *)"-stdin-"; - home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE); - IObuff[0] = '"'; - STRCAT(IObuff, "\" "); + if (fname == NULL) { + fname = "-stdin-"; + } + ret_buf[0] = '"'; + home_replace(buf, (const char_u *)fname, (char_u *)ret_buf + 1, + (int)buf_len - 4, true); + xstrlcat(ret_buf, "\" ", buf_len); } /// Append message for text mode to IObuff. diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a10c835591..1a97adfa21 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -703,7 +703,8 @@ char *vim_getenv(const char *name) /// @param dstlen Maximum length of the result /// @param one If true, only replace one file name, including spaces and commas /// in the file name -void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) +void home_replace(const buf_T *const buf, const char_u *src, + char_u *dst, size_t dstlen, bool one) { size_t dirlen = 0, envlen = 0; size_t len; @@ -717,7 +718,7 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one) * If the file is a help file, remove the path completely. */ if (buf != NULL && buf->b_help) { - STRCPY(dst, path_tail(src)); + xstrlcpy((char *)dst, (char *)path_tail(src), dstlen); return; } @@ -809,7 +810,7 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET len += STRLEN(src); } char_u *dst = xmalloc(len); - home_replace(buf, src, dst, (int)len, true); + home_replace(buf, src, dst, len, true); return dst; } diff --git a/src/nvim/path.c b/src/nvim/path.c index d0248690d9..6bf42ed2fa 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -84,15 +84,15 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) /// /// @return pointer just past the last path separator (empty string, if fname /// ends in a slash), or empty string if fname is NULL. -char_u *path_tail(char_u *fname) +char_u *path_tail(const char_u *fname) FUNC_ATTR_NONNULL_RET { if (fname == NULL) { return (char_u *)""; } - char_u *tail = get_past_head(fname); - char_u *p = tail; + const char_u *tail = get_past_head(fname); + const char_u *p = tail; // Find last part of path. while (*p != NUL) { if (vim_ispathsep_nocolon(*p)) { @@ -100,7 +100,7 @@ char_u *path_tail(char_u *fname) } mb_ptr_adv(p); } - return tail; + return (char_u *)tail; } /// Get pointer to tail of "fname", including path separators. @@ -174,9 +174,9 @@ const char *path_next_component(const char *fname) /// Get a pointer to one character past the head of a path name. /// Unix: after "/"; Win: after "c:\" /// If there is no head, path is returned. -char_u *get_past_head(char_u *path) +char_u *get_past_head(const char_u *path) { - char_u *retval = path; + const char_u *retval = path; #ifdef WIN32 // May skip "c:" @@ -189,7 +189,7 @@ char_u *get_past_head(char_u *path) ++retval; } - return retval; + return (char_u *)retval; } /* -- cgit From 97a7f4745dd1d75cd176dede1a4430bc4e28f8f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 02:11:27 +0300 Subject: eval: Add s flag, use p_fs by default, error out on unknown flag --- src/nvim/eval.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8a3e3f3e22..7ab07fe6a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17421,20 +17421,24 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; - bool do_fsync = true; + bool do_fsync = !!p_fs; if (argvars[2].v_type != VAR_UNKNOWN) { const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } - if (strchr(flags, 'b')) { - binary = true; - } - if (strchr(flags, 'a')) { - append = true; - } - if (strchr(flags, 'S')) { - do_fsync = false; + for (const char *p = flags; *p; p++) { + switch (*p) { + case 'b': { binary = true; break; } + case 'a': { append = true; break; } + case 's': { do_fsync = true; break; } + case 'S': { do_fsync = false; break; } + default: { + // Using %s, p and not %c, *p to preserve multibyte characters + emsgf(_("E5060: Unknown flag: %s"), p); + return; + } + } } } -- cgit From 2dbd49f73cba0dac8d430f86b301b6e9c15f3a7f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:02:17 +0300 Subject: fileio: Save details about E212 error --- src/nvim/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 1ae73e787d..8e0e45aee6 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3192,7 +3192,7 @@ nobackup: SET_ERRMSG(_("E166: Can't open linked file for writing")); } else { #endif - SET_ERRMSG(_("E212: Can't open file for writing")); + SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd); if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL && perm >= 0) { #ifdef UNIX -- cgit From ac87c7e5ae5fc15ca97150dfce1476408804ae5a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 3 Apr 2017 03:46:44 +0300 Subject: fileio: Fix most linter errors One error is still kept: buf_write function is too large. --- src/nvim/fileio.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 8e0e45aee6..c1b8203ed1 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -203,7 +203,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) if (msg_silent != 0) { return; } - add_quoted_fname((char *)IObuff, IOSIZE - 80, buf,(const char *)name); + add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name); xstrlcat((char *)IObuff, (const char *)s, IOSIZE); // For the first message may have to start a new line. // For further ones overwrite the previous one, reset msg_scroll before @@ -1796,7 +1796,7 @@ failed: } if (!filtering && !(flags & READ_DUMMY)) { - add_quoted_fname((char *)IObuff, IOSIZE, curbuf,(const char *)sfname); + add_quoted_fname((char *)IObuff, IOSIZE, curbuf, (const char *)sfname); c = false; #ifdef UNIX @@ -2913,9 +2913,9 @@ buf_write ( int error; if ((error = os_close(bfd)) != 0 && errmsg == NULL) { - SET_ERRMSG_ARG(_( - "E507: Close error for backup file (add ! to override): %s"), - error); + SET_ERRMSG_ARG(_("E507: Close error for backup file " + "(add ! to override): %s"), + error); } if (write_info.bw_len < 0) { SET_ERRMSG(_( @@ -2937,7 +2937,7 @@ buf_write ( } } nobackup: - os_close(fd); /* ignore errors for closing read file */ + os_close(fd); // Ignore errors for closing read file. xfree(copybuf); if (backup == NULL && errmsg == NULL) { @@ -3458,8 +3458,7 @@ restore_backup: SET_ERRMSG(_( "E513: write error, conversion failed " "(make 'fenc' empty to override)")); - } - else { + } else { errmsg_allocated = true; SET_ERRMSG(xmalloc(300)); vim_snprintf( @@ -3527,7 +3526,7 @@ restore_backup: fname = sfname; /* use shortname now, for the messages */ #endif if (!filtering) { - add_quoted_fname((char *)IObuff, IOSIZE, buf,(const char *)fname); + add_quoted_fname((char *)IObuff, IOSIZE, buf, (const char *)fname); c = false; if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); -- cgit From bc6d868d00a739050b683f33994f7493cf81bd61 Mon Sep 17 00:00:00 2001 From: Yichao Zhou Date: Sun, 26 Mar 2017 03:15:52 -0700 Subject: 'listchars': `Whitespace` highlight group #6367 --- src/nvim/globals.h | 3 ++- src/nvim/option.c | 3 ++- src/nvim/screen.c | 8 ++++---- src/nvim/syntax.c | 1 + 4 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index c15287aa38..3c705d88a5 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -464,6 +464,7 @@ typedef enum { , HLF_CUL // 'cursurline' , HLF_MC // 'colorcolumn' , HLF_QFL // selected quickfix line + , HLF_0 // Whitespace , HLF_COUNT // MUST be the last one } hlf_T; @@ -472,7 +473,7 @@ typedef enum { #define HL_FLAGS { '8', '~', 'z', 'Z', '@', 'd', 'e', 'i', 'l', 'm', 'M', 'n', \ 'N', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', 'f', 'F', \ 'A', 'C', 'D', 'T', '-', '>', 'B', 'P', 'R', 'L', '+', '=', \ - 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q' } + 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q', '0' } EXTERN int highlight_attr[HLF_COUNT]; /* Highl. attr for each context. */ EXTERN int highlight_user[9]; /* User[1-9] attributes */ diff --git a/src/nvim/option.c b/src/nvim/option.c index b3b4dc1e0a..695d0edebf 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -245,7 +245,8 @@ typedef struct vimoption { "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ - "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine" + "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine," \ + "0:Whitespace" /* * options[] is initialized here. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index cf460adb82..d9a21aa81f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3409,7 +3409,7 @@ win_line ( || (c == ' ' && lcs_space && ptr - line <= trailcol))) { c = (c == ' ') ? lcs_space : lcs_nbsp; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3424,7 +3424,7 @@ win_line ( if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') { c = lcs_trail; n_attr = 1; - extra_attr = hl_attr(HLF_8); + extra_attr = hl_attr(HLF_0); saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { @@ -3525,8 +3525,8 @@ win_line ( c_extra = lcs_tab2; } n_attr = tab_len + 1; - extra_attr = hl_attr(HLF_8); - saved_attr2 = char_attr; /* save current attr */ + extra_attr = hl_attr(HLF_0); + saved_attr2 = char_attr; // save current attr mb_c = c; if (enc_utf8 && (*mb_char2len)(c) > 1) { mb_utf8 = TRUE; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index acda25e738..e36b00d770 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5916,6 +5916,7 @@ static char *highlight_init_both[] = "default link EndOfBuffer NonText", "default link QuickFixLine Search", "default link Substitute Search", + "default link Whitespace NonText", NULL }; -- cgit From 3ccd59ee8216f3da812c5cf81eb392e6a95b539a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 3 Apr 2017 16:16:21 +0200 Subject: 'guicursor': enabled=false if 'guicursor' is empty Closes #6429 Closes #6430 --- src/nvim/api/ui.c | 6 +++--- src/nvim/cursor_shape.c | 6 ++---- src/nvim/tui/tui.c | 48 ++++++++++++++++++++++++++---------------------- src/nvim/ui.c | 3 ++- src/nvim/ui.h | 2 +- src/nvim/ui_bridge.c | 22 ++++++++++++---------- 6 files changed, 46 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index a95be0fabb..de60339e5f 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -300,11 +300,11 @@ static void remote_ui_scroll(UI *ui, int count) push_call(ui, "scroll", args); } -static void remote_ui_cursor_style_set(UI *ui, Dictionary styles) +static void remote_ui_cursor_style_set(UI *ui, bool enabled, Dictionary data) { Array args = ARRAY_DICT_INIT; - Object copy = copy_object(DICTIONARY_OBJ(styles)); - ADD(args, copy); + ADD(args, BOOLEAN_OBJ(enabled)); + ADD(args, copy_object(DICTIONARY_OBJ(data))); push_call(ui, "cursor_style_set", args); } diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 7ec70bb724..34ee53bf75 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -89,10 +89,8 @@ char_u *parse_shape_opt(int what) int found_ve = false; /* found "ve" flag */ int round; - /* - * First round: check for errors; second round: do it for real. - */ - for (round = 1; round <= 2; ++round) { + // First round: check for errors; second round: do it for real. + for (round = 1; round <= 2; round++) { // Repeat for all comma separated parts. modep = p_guicursor; if (*p_guicursor == NUL) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index badc0cd870..f34f5f1bc4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -84,6 +84,7 @@ typedef struct { } TUIData; static bool volatile got_winch = false; +static bool cursor_style_enabled = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" @@ -151,6 +152,7 @@ static void terminfo_start(UI *ui) // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear + // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_clear_screen); // Enable bracketed paste @@ -437,11 +439,11 @@ static void tui_cursor_goto(UI *ui, int row, int col) CursorShape tui_cursor_decode_shape(const char *shape_str) { CursorShape shape = 0; - if (strcmp(shape_str, "block") == 0) { + if (strequal(shape_str, "block")) { shape = SHAPE_BLOCK; - } else if (strcmp(shape_str, "vertical") == 0) { + } else if (strequal(shape_str, "vertical")) { shape = SHAPE_VER; - } else if (strcmp(shape_str, "horizontal") == 0) { + } else if (strequal(shape_str, "horizontal")) { shape = SHAPE_HOR; } else { EMSG2(_(e_invarg2), shape_str); @@ -454,40 +456,41 @@ static cursorentry_T decode_cursor_entry(Dictionary args) cursorentry_T r; for (size_t i = 0; i < args.size; i++) { - char *keyStr = args.items[i].key.data; + char *key = args.items[i].key.data; Object value = args.items[i].value; - if (strcmp(keyStr, "cursor_shape") == 0) { + if (strequal(key, "cursor_shape")) { r.shape = tui_cursor_decode_shape(args.items[i].value.data.string.data); - } else if (strcmp(keyStr, "blinkon") == 0) { + } else if (strequal(key, "blinkon")) { r.blinkon = (int)value.data.integer; - } else if (strcmp(keyStr, "blinkoff") == 0) { + } else if (strequal(key, "blinkoff")) { r.blinkoff = (int)value.data.integer; - } else if (strcmp(keyStr, "hl_id") == 0) { + } else if (strequal(key, "hl_id")) { r.id = (int)value.data.integer; } } return r; } -static void tui_cursor_style_set(UI *ui, Dictionary args) +static void tui_cursor_style_set(UI *ui, bool enabled, Dictionary args) { + cursor_style_enabled = enabled; + if (!enabled) { + return; // Do not send cursor style control codes. + } TUIData *data = ui->data; + assert(args.size); + // Keys: as defined by `shape_table`. for (size_t i = 0; i < args.size; i++) { char *mode_name = args.items[i].key.data; const int mode_id = cursor_mode_str2int(mode_name); - - if (mode_id < 0) { - WLOG("Unknown mode '%s'", mode_name); - continue; - } + assert(mode_id >= 0); cursorentry_T r = decode_cursor_entry(args.items[i].value.data.dictionary); r.full_name = mode_name; data->cursor_shapes[mode_id] = r; } - // force redraw MouseMode cursor_mode = tui_mode2cursor(data->showing_mode); tui_set_cursor(ui, cursor_mode); } @@ -528,6 +531,9 @@ static void tui_mouse_off(UI *ui) /// @param mode one of SHAPE_XXX static void tui_set_cursor(UI *ui, MouseMode mode) { + if (!cursor_style_enabled) { + return; + } TUIData *data = ui->data; cursorentry_T c = data->cursor_shapes[mode]; int shape = c.shape; @@ -536,17 +542,15 @@ static void tui_set_cursor(UI *ui, MouseMode mode) # define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) // Support changing cursor shape on some popular terminals. - const char *term_prog = os_getenv("TERM_PROGRAM"); const char *vte_version = os_getenv("VTE_VERSION"); - if ((term_prog && !strcmp(term_prog, "Konsole")) - || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { + if (os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION")) { // Konsole uses a proprietary escape code to set the cursor shape // and does not support DECSCUSR. switch (shape) { case SHAPE_BLOCK: shape = 0; break; case SHAPE_VER: shape = 1; break; - case SHAPE_HOR: shape = 3; break; + case SHAPE_HOR: shape = 2; break; default: WLOG("Unknown shape value %d", shape); break; } data->params[0].i = shape; @@ -1102,15 +1106,15 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, stty_erase = tui_get_stty_erase(); } - if (strcmp(name, "key_backspace") == 0) { + if (strequal(name, "key_backspace")) { ILOG("libtermkey:kbs=%s", value); if (stty_erase != NULL && stty_erase[0] != 0) { return stty_erase; } - } else if (strcmp(name, "key_dc") == 0) { + } else if (strequal(name, "key_dc")) { ILOG("libtermkey:kdch1=%s", value); // Vim: "If and are now the same, redefine ." - if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { + if (stty_erase != NULL && value != NULL && strequal(stty_erase, value)) { return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index babb4efa96..28f71b7ef2 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -381,7 +381,8 @@ void ui_cursor_goto(int new_row, int new_col) void ui_cursor_style_set(void) { Dictionary style = cursor_shape_dict(); - UI_CALL(cursor_style_set, style); + bool enabled = (*p_guicursor != NUL); + UI_CALL(cursor_style_set, enabled, style); api_free_dictionary(style); } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 0af0c0db65..8ffc5a45a6 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -22,7 +22,7 @@ struct ui_t { void (*clear)(UI *ui); void (*eol_clear)(UI *ui); void (*cursor_goto)(UI *ui, int row, int col); - void (*cursor_style_set)(UI *ui, Dictionary cursor_shapes); + void (*cursor_style_set)(UI *ui, bool enabled, Dictionary cursor_styles); void (*update_menu)(UI *ui); void (*busy_start)(UI *ui); void (*busy_stop)(UI *ui); diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index c9bad6b254..9f780663ac 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -60,7 +60,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.clear = ui_bridge_clear; rv->bridge.eol_clear = ui_bridge_eol_clear; rv->bridge.cursor_goto = ui_bridge_cursor_goto; - rv->bridge.cursor_style_set = ui_bridge_cursor_styleset; + rv->bridge.cursor_style_set = ui_bridge_cursor_style_set; rv->bridge.update_menu = ui_bridge_update_menu; rv->bridge.busy_start = ui_bridge_busy_start; rv->bridge.busy_stop = ui_bridge_busy_stop; @@ -180,19 +180,21 @@ static void ui_bridge_cursor_goto_event(void **argv) ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); } -static void ui_bridge_cursor_styleset(UI *b, Dictionary style) +static void ui_bridge_cursor_style_set(UI *b, bool enabled, Dictionary styles) { - Object copy = copy_object(DICTIONARY_OBJ(style)); - Object *pobj = xmalloc(sizeof(copy)); - *pobj = copy; - UI_CALL(b, cursor_styleset, 2, b, pobj); + bool *enabledp = xmalloc(sizeof(*enabledp)); + Object *stylesp = xmalloc(sizeof(*stylesp)); + *enabledp = enabled; + *stylesp = copy_object(DICTIONARY_OBJ(styles)); + UI_CALL(b, cursor_style_set, 3, b, enabledp, stylesp); } -static void ui_bridge_cursor_styleset_event(void **argv) +static void ui_bridge_cursor_style_set_event(void **argv) { UI *ui = UI(argv[0]); - Object *styles = (Object *)argv[1]; - - ui->cursor_style_set(ui, styles->data.dictionary); + bool *enabled = argv[1]; + Object *styles = argv[2]; + ui->cursor_style_set(ui, *enabled, styles->data.dictionary); + xfree(enabled); api_free_object(*styles); xfree(styles); } -- cgit From e348e256f3ed93fe462971447ee79033307b2ddf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 4 Apr 2017 02:37:43 +0200 Subject: 'guicursor': Disable by default for unknown terminals. User can still set guicursor explicitly in init.vim. Closes #5990 Closes #6403 --- src/nvim/main.c | 2 +- src/nvim/option.c | 20 +++++++++++--------- src/nvim/os/env.c | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index 33e1551351..7ad42d6776 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -283,7 +283,7 @@ int main(int argc, char **argv) cmdline_row = (int)(Rows - p_ch); msg_row = cmdline_row; screenalloc(false); /* allocate screen buffers */ - set_init_2(); + set_init_2(params.headless); TIME_MSG("inits 2"); msg_scroll = TRUE; diff --git a/src/nvim/option.c b/src/nvim/option.c index 695d0edebf..458d80716c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -939,11 +939,8 @@ void free_all_options(void) #endif -/* - * Initialize the options, part two: After getting Rows and Columns and - * setting 'term'. - */ -void set_init_2(void) +/// Initialize the options, part two: After getting Rows and Columns. +void set_init_2(bool headless) { int idx; @@ -966,8 +963,12 @@ void set_init_2(void) p_window = Rows - 1; } set_number_default("window", Rows - 1); - parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ - (void)parse_printoptions(); /* parse 'printoptions' default value */ + if (!headless && !os_term_is_nice()) { + set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"", + OPT_GLOBAL, SID_NONE); + } + parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' + (void)parse_printoptions(); // parse 'printoptions' default value } /* @@ -2842,9 +2843,10 @@ did_set_string_option ( } } - /* 'guicursor' */ - else if (varp == &p_guicursor) + // 'guicursor' + else if (varp == &p_guicursor) { errmsg = parse_shape_opt(SHAPE_CURSOR); + } else if (varp == &p_popt) errmsg = parse_printoptions(); diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 1a97adfa21..839e0d1b51 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -889,3 +889,17 @@ bool os_setenv_append_path(const char *fname) } return false; } + +/// Returns true if the terminal can be assumed to silently ignore unknown +/// control codes. +bool os_term_is_nice(void) +{ +#if defined(__APPLE__) || defined(WIN32) + return true; +#else + const char *vte_version = os_getenv("VTE_VERSION"); + return (vte_version && atoi(vte_version) >= 3900) + || NULL != os_getenv("KONSOLE_PROFILE_NAME") + || NULL != os_getenv("KONSOLE_DBUS_SESSION"); +#endif +} -- cgit From a7f34e199144bfb657c180cdc1413093fd34bdf9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 4 Apr 2017 03:38:57 +0200 Subject: options: remove 'guiheadroom' --- src/nvim/options.lua | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 09f016cf5a..4ca63f2efe 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1025,13 +1025,6 @@ return { redraw={'everything'}, enable_if=false, }, - { - full_name='guiheadroom', abbreviation='ghr', - type='number', scope={'global'}, - vi_def=true, - enable_if=false, - defaults={if_true={vi=50}} - }, { full_name='guioptions', abbreviation='go', type='string', list='flags', scope={'global'}, -- cgit