diff options
-rw-r--r-- | runtime/doc/change.txt | 19 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 44 | ||||
-rw-r--r-- | src/nvim/ascii.h | 8 | ||||
-rw-r--r-- | src/nvim/charset.c | 85 | ||||
-rw-r--r-- | src/nvim/eval.c | 10 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 15 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/keymap.c | 4 | ||||
-rw-r--r-- | src/nvim/message.c | 45 | ||||
-rw-r--r-- | src/nvim/ops.c | 80 | ||||
-rw-r--r-- | src/nvim/option.c | 4 | ||||
-rw-r--r-- | src/nvim/options.lua | 2 | ||||
-rw-r--r-- | src/nvim/spell.c | 4 | ||||
-rw-r--r-- | src/nvim/vim.h | 2 | ||||
-rw-r--r-- | test/functional/eval/printf_spec.lua | 58 | ||||
-rw-r--r-- | test/functional/legacy/035_increment_and_decrement_spec.lua | 26 | ||||
-rw-r--r-- | test/functional/legacy/057_sort_spec.lua | 35 |
17 files changed, 350 insertions, 95 deletions
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 0fa383bc67..42dc84e0de 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -371,8 +371,10 @@ CTRL-X Subtract [count] from the number or alphabetic character at or after the cursor. The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned -octal and hexadecimal numbers and alphabetic characters. This depends on the -'nrformats' option. +binary/octal/hexadecimal numbers and alphabetic characters. This +depends on the 'nrformats' option. +- When 'nrformats' includes "bin", Vim considers numbers starting with '0b' or + '0B' as binary. - When 'nrformats' includes "octal", Vim considers numbers starting with a '0' to be octal, unless the number includes a '8' or '9'. Other numbers are decimal and may have a preceding minus sign. @@ -386,6 +388,10 @@ octal and hexadecimal numbers and alphabetic characters. This depends on the under or after the cursor. This is useful to make lists with an alphabetic index. +For decimals a leading negative sign is considered for incrementing or +decrementing, for binary and octal and hex values, it won't be considered. To +ignore the sign Visually select the number before using CTRL-A or CTRL-X. + For numbers with leading zeros (including all octal and hexadecimal numbers), Vim preserves the number of characters in the number when possible. CTRL-A on "0077" results in "0100", CTRL-X on "0x100" results in "0x0ff". @@ -397,6 +403,10 @@ octal number. Note that when 'nrformats' includes "octal", decimal numbers with leading zeros cause mistakes, because they can be confused with octal numbers. +Note similarly, when 'nrformats' includes "bin", binary numbers with a leading +'0x' or '0X' can be interpreted as hexadecimal rather than binary since '0b' +are valid hexadecimal digits. + The CTRL-A command is very useful in a macro. Example: Use the following steps to make a numbered list. @@ -1602,7 +1612,7 @@ Vim has a sorting function and a sorting command. The sorting function can be found here: |sort()|, |uniq()|. *:sor* *:sort* -:[range]sor[t][!] [i][u][r][n][x][o] [/{pattern}/] +:[range]sor[t][!] [i][u][r][n][x][o][b] [/{pattern}/] Sort lines in [range]. When no range is given all lines are sorted. @@ -1622,6 +1632,9 @@ found here: |sort()|, |uniq()|. With [o] sorting is done on the first octal number in the line (after or inside a {pattern} match). + With [b] sorting is done on the first binary number in + the line (after or inside a {pattern} match). + With [u] only keep the first of a sequence of identical lines (ignoring case when [i] is used). Without this flag, a sequence of identical lines diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e295772693..3472294483 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -65,14 +65,16 @@ the Number. Examples: Number 0 --> String "0" ~ Number -1 --> String "-1" ~ *octal* -Conversion from a String to a Number is done by converting the first digits -to a number. Hexadecimal "0xf9" and Octal "017" numbers are recognized. If -the String doesn't start with digits, the result is zero. Examples: +Conversion from a String to a Number is done by converting the first digits to +a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are +recognized. If the String doesn't start with digits, the result is zero. +Examples: String "456" --> Number 456 ~ String "6bar" --> Number 6 ~ String "foo" --> Number 0 ~ String "0xf1" --> Number 241 ~ String "0100" --> Number 64 ~ + String "0b101" --> Number 5 ~ String "-8" --> Number -8 ~ String "+8" --> Number 0 ~ @@ -4912,6 +4914,9 @@ printf({fmt}, {expr1} ...) *printf()* %c single byte %d decimal number %5d decimal number padded with spaces to 5 characters + %b binary number + %08b binary number padded with zeros to at least 8 characters + %B binary number using upper case letters %x hex number %04x hex number padded with zeros to at least 4 characters %X hex number using upper case letters @@ -4998,20 +5003,19 @@ printf({fmt}, {expr1} ...) *printf()* The conversion specifiers and their meanings are: - *printf-d* *printf-o* *printf-x* *printf-X* - doxX The Number argument is converted to signed decimal - (d), unsigned octal (o), or unsigned hexadecimal (x - and X) notation. The letters "abcdef" are used for - x conversions; the letters "ABCDEF" are used for X - conversions. - The precision, if any, gives the minimum number of - digits that must appear; if the converted value - requires fewer digits, it is padded on the left with - zeros. - In no case does a non-existent or small field width - cause truncation of a numeric field; if the result of - a conversion is wider than the field width, the field - is expanded to contain the conversion result. + *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X* + dbBoxX The Number argument is converted to signed decimal (d), + unsigned binary (b and B), unsigned octal (o), or + unsigned hexadecimal (x and X) notation. The letters + "abcdef" are used for x conversions; the letters + "ABCDEF" are used for X conversions. The precision, if + any, gives the minimum number of digits that must + appear; if the converted value requires fewer digits, it + is padded on the left with zeros. In no case does a + non-existent or small field width cause truncation of a + numeric field; if the result of a conversion is wider + than the field width, the field is expanded to contain + the conversion result. *printf-c* c The Number argument is converted to a byte, and the @@ -6127,12 +6131,14 @@ str2float( {expr}) *str2float()* str2nr( {expr} [, {base}]) *str2nr()* Convert string {expr} to a number. - {base} is the conversion base, it can be 8, 10 or 16. + {base} is the conversion base, it can be 2, 8, 10 or 16. When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as with the default String to Number conversion. When {base} is 16 a leading "0x" or "0X" is ignored. With a - different base the result will be zero. + different base the result will be zero. Similarly, when {base} + is 8 a leading "0" is ignored, and when {base} is 2 a leading + "0b" or "0B" is ignored. Text after the number is silently ignored. diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 2b3e94d5a0..9938d1b328 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -122,6 +122,14 @@ static inline bool ascii_isxdigit(int c) || (c >= 'A' && c <= 'F'); } +/// Checks if `c` is a binary digit, that is, 0-1. +/// +/// @see {ascii_isdigit} +static inline bool ascii_isbdigit(int c) +{ + return (c == '0' || c == '1'); +} + /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/charset.c b/src/nvim/charset.c index b93eafbf60..48046c9f17 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1454,6 +1454,20 @@ char_u* skipdigits(char_u *q) return p; } +/// skip over binary digits +/// +/// @param q +/// +/// @return Pointer to the character after the skipped digits. +char_u* skipbin(char_u *q) +{ + char_u *p = q; + + while (ascii_isbdigit(*p)) /* skip to next non-digit */ + ++p; + return p; +} + /// skip over digits and hex characters /// /// @param q @@ -1485,6 +1499,20 @@ char_u* skiptodigit(char_u *q) return p; } +/// skip to binary character (or NUL after the string) +/// +/// @param q +/// +/// @return Pointer to the binary character or (NUL after the string). +char_u* skiptobin(char_u *q) +{ + char_u *p = q; + + while (*p != NUL && !ascii_isbdigit(*p)) /* skip to next digit */ + ++p; + return p; +} + /// skip to hex character (or NUL after the string) /// /// @param q @@ -1720,33 +1748,38 @@ int vim_isblankline(char_u *lbuf) } /// Convert a string into a long and/or unsigned long, taking care of -/// hexadecimal and octal numbers. Accepts a '-' sign. -/// If "hexp" is not NULL, returns a flag to indicate the type of the number: +/// hexadecimal, octal and binary numbers. Accepts a '-' sign. +/// If "prep" is not NULL, returns a flag to indicate the type of the number: /// 0 decimal /// '0' octal +/// 'B' bin +/// 'b' bin /// 'X' hex /// 'x' hex /// If "len" is not NULL, the length of the number in characters is returned. /// If "nptr" is not NULL, the signed result is returned in it. /// If "unptr" is not NULL, the unsigned result is returned in it. +/// If "dobin" is non-zero recognize binary numbers, when > 1 always assume +/// binary number. /// If "dooct" is non-zero recognize octal numbers, when > 1 always assume /// octal number. /// If "dohex" is non-zero recognize hex numbers, when > 1 always assume /// hex number. /// /// @param start -/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex, -// '0' = octal +/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex, +// '0' = octal, 'b' or 'B' is bin /// @param len Returns the detected length of number. +/// @param dobin recognize binary number /// @param dooct recognize octal number /// @param dohex recognize hex number /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. -void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, +void vim_str2nr(char_u *start, int *prep, int *len, int dobin, int dooct, int dohex, long *nptr, unsigned long *unptr) { char_u *ptr = start; - int hex = 0; // default is decimal + int pre = 0; // default is decimal int negative = FALSE; unsigned long un = 0; int n; @@ -1756,31 +1789,35 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, ++ptr; } - // Recognize hex and octal. + // Recognize hex, octal, and bin. if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { - hex = ptr[1]; + pre = ptr[1]; if (dohex - && ((hex == 'X') || (hex == 'x')) + && ((pre == 'X') || (pre == 'x')) && ascii_isxdigit(ptr[2])) { // hexadecimal ptr += 2; + } else if (dobin + && ((pre == 'B') || (pre == 'b')) + && ascii_isbdigit(ptr[2])) { + // binary + ptr += 2; } else { // default is decimal - hex = 0; + pre = 0; if (dooct) { // Don't interpret "0", "08" or "0129" as octal. for (n = 1; ascii_isdigit(ptr[n]); ++n) { if (ptr[n] > '7') { // can't be octal - hex = 0; + pre = 0; break; } - if (ptr[n] >= '0') { // assume octal - hex = '0'; + pre = '0'; } } } @@ -1788,28 +1825,38 @@ void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - if ((hex == '0') || (dooct > 1)) { + if ((pre == 'B') || (pre == 'b') || (dobin > 1)) { + // bin + if (pre != 0) + n += 2; // skip over "0b" + while ('0' <= *ptr && *ptr <= '1') { + un = 2 * un + (unsigned long)(*ptr - '0'); + ++ptr; + } + } else if ((pre == '0') || (dooct > 1)) { // octal while ('0' <= *ptr && *ptr <= '7') { un = 8 * un + (unsigned long)(*ptr - '0'); ptr++; } - } else if ((hex != 0) || (dohex > 1)) { + } else if (pre != 0 || dohex > 1) { // hex + if (pre != 0) + n += 2; // skip over "0x" while (ascii_isxdigit(*ptr)) { un = 16 * un + (unsigned long)hex2nr(*ptr); - ptr++; + ++ptr; } } else { // decimal while (ascii_isdigit(*ptr)) { un = 10 * un + (unsigned long)(*ptr - '0'); - ptr++; + ++ptr; } } - if (hexp != NULL) { - *hexp = hex; + if (prep != NULL) { + *prep = pre; } if (len != NULL) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7ac51d7bd7..479ed2afc9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1149,7 +1149,7 @@ call_vim_function ( len = 0; else /* Recognize a number argument, the others must be strings. */ - vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL); + vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, TRUE, &n, NULL); if (len != 0 && len == (int)STRLEN(argv[i])) { argvars[i].v_type = VAR_NUMBER; argvars[i].vval.v_number = n; @@ -4127,7 +4127,7 @@ eval7 ( rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); + vim_str2nr(*arg, NULL, &len, TRUE, TRUE, TRUE, &n, NULL); *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -15982,7 +15982,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_UNKNOWN) { base = get_tv_number(&argvars[1]); - if (base != 8 && base != 10 && base != 16) { + if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; } @@ -15991,7 +15991,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv) p = skipwhite(get_tv_string(&argvars[0])); if (*p == '+') p = skipwhite(p + 1); - vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL); + vim_str2nr(p, NULL, NULL, base == 2 ? 2 : 0, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL); rettv->vval.v_number = n; } @@ -18273,7 +18273,7 @@ long get_tv_number_chk(typval_T *varp, int *denote) case VAR_STRING: if (varp->vval.v_string != NULL) vim_str2nr(varp->vval.v_string, NULL, NULL, - TRUE, TRUE, &n, NULL); + TRUE, TRUE, TRUE, &n, NULL); return n; case VAR_LIST: EMSG(_("E745: Using a List as a Number")); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 34c25589d4..22b243957c 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -348,6 +348,7 @@ void ex_sort(exarg_T *eap) long deleted; colnr_T start_col; colnr_T end_col; + int sort_bin; /* sort on bin number */ int sort_oct; /* sort on octal number */ int sort_hex; /* sort on hex number */ @@ -362,7 +363,7 @@ void ex_sort(exarg_T *eap) regmatch.regprog = NULL; sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0; + sort_abort = sort_ic = sort_rx = sort_nr = sort_bin = sort_oct = sort_hex = 0; for (p = eap->arg; *p != NUL; ++p) { if (ascii_iswhite(*p)) @@ -373,6 +374,8 @@ void ex_sort(exarg_T *eap) sort_rx = TRUE; else if (*p == 'n') sort_nr = 2; + else if (*p == 'b') + sort_bin = 2; else if (*p == 'o') sort_oct = 2; else if (*p == 'x') @@ -410,14 +413,14 @@ void ex_sort(exarg_T *eap) } } - /* Can only have one of 'n', 'o' and 'x'. */ - if (sort_nr + sort_oct + sort_hex > 2) { + /* Can only have one of 'n', 'b', 'o' and 'x'. */ + if (sort_nr + sort_bin + sort_oct + sort_hex > 2) { EMSG(_(e_invarg)); goto sortend; } /* From here on "sort_nr" is used as a flag for any number sorting. */ - sort_nr += sort_oct + sort_hex; + sort_nr += sort_bin + sort_oct + sort_hex; /* * Make an array with all line numbers. This avoids having to copy all @@ -454,6 +457,8 @@ void ex_sort(exarg_T *eap) p = s + start_col; if (sort_hex) s = skiptohex(p); + else if (sort_bin) + s = skiptobin(p); else s = skiptodigit(p); if (s > p && s[-1] == '-') @@ -462,7 +467,7 @@ void ex_sort(exarg_T *eap) /* empty line should sort before any number */ nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; else - vim_str2nr(s, NULL, NULL, sort_oct, sort_hex, + vim_str2nr(s, NULL, NULL, sort_bin, sort_oct, sort_hex, &nrs[lnum - eap->line1].start_col_nr, NULL); *s2 = c; } else { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6d81f3680a..1a9a5bd3ec 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4780,7 +4780,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == '-' || ascii_isdigit(**str)) { /* parse "from" part of range */ - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); + vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL); *str += len; *num1 = (int)num; first = TRUE; @@ -4788,7 +4788,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == ',') { /* parse "to" part of range */ *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); + vim_str2nr(*str, NULL, &len, FALSE, FALSE, FALSE, &num, NULL); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index b2fd929714..16b1577f06 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -575,7 +575,7 @@ find_special_key ( if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) bp += 3; /* skip t_xx, xx may be '-' or '>' */ else if (STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, NULL, NULL); + vim_str2nr(bp + 5, NULL, &l, TRUE, TRUE, TRUE, NULL, NULL); bp += l + 5; break; } @@ -602,7 +602,7 @@ find_special_key ( if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { /* <Char-123> or <Char-033> or <Char-0x33> */ - vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, NULL, &n); + vim_str2nr(last_dash + 6, NULL, NULL, TRUE, TRUE, TRUE, NULL, &n); key = (int)n; } else { /* diff --git a/src/nvim/message.c b/src/nvim/message.c index 66b8b9b5d2..c3c20f793c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3037,7 +3037,7 @@ static double tv_float(typval_T *tvs, int *idxp) * http://www.ijs.si/software/snprintf/ * * This snprintf() only supports the following conversion specifiers: - * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * s, c, b, B, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) * with flags: '-', '+', ' ', '0' and '#'. * An asterisk is supported for field width as well as precision. * @@ -3295,8 +3295,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) } break; - case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { - // u, o, x, X and p conversion specifiers imply the value is unsigned; + case 'd': case 'u': case 'b': case 'B': case 'o': case 'x': case 'X': case 'p': { + // u, b, B, o, x, X and p conversion specifiers imply the value is unsigned; // d implies a signed value // 0 if numeric argument is zero (or if pointer is NULL for 'p'), @@ -3399,7 +3399,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) // leave negative numbers for sprintf to handle, to // avoid handling tricky cases like (short int)-32768 } else if (alternate_form) { - if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' || + fmt_spec == 'b' || fmt_spec == 'B')) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } @@ -3411,7 +3412,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) precision = 1; // default precision is 1 if (precision == 0 && arg_sign == 0) { // when zero value is formatted with an explicit precision 0, - // resulting formatted string is empty (d, i, u, o, x, X, p) + // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) } else { char f[5]; int f_l = 0; @@ -3441,6 +3442,36 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg); break; } + } else if (fmt_spec == 'b' || fmt_spec == 'B') { + //binary + size_t bits = 0; + switch (length_modifier) { + case '\0': + case 'h': for (bits = sizeof(unsigned) * 8; bits > 0; bits--) + if ((uint_arg >> (bits - 1)) & 0x1) break; + + while (bits > 0) + tmp[str_arg_l++] = ((uint_arg >> --bits) & 0x1) ? '1' : '0'; + break; + case 'l': for (bits = sizeof(unsigned long) * 8; bits > 0; bits--) + if ((ulong_arg >> (bits - 1)) & 0x1) break; + + while (bits > 0) + tmp[str_arg_l++] = ((ulong_arg >> --bits) & 0x1) ? '1' : '0'; + break; + case '2': for (bits = sizeof(unsigned long long) * 8; bits > 0; bits--) + if ((ulong_long_arg >> (bits - 1)) & 0x1) break; + + while (bits > 0) + tmp[str_arg_l++] = ((ulong_long_arg >> --bits) & 0x1) ? '1' : '0'; + break; + case 'z': for (bits = sizeof(size_t) * 8; bits > 0; bits--) + if ((size_t_arg >> (bits - 1)) & 0x1) break; + + while (bits > 0) + tmp[str_arg_l++] = ((size_t_arg >> --bits) & 0x1) ? '1' : '0'; + break; + } } else { // unsigned switch (length_modifier) { @@ -3464,7 +3495,9 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs) if (zero_padding_insertion_ind + 1 < str_arg_l && tmp[zero_padding_insertion_ind] == '0' && (tmp[zero_padding_insertion_ind + 1] == 'x' - || tmp[zero_padding_insertion_ind + 1] == 'X')) + || tmp[zero_padding_insertion_ind + 1] == 'X' + || tmp[zero_padding_insertion_ind + 1] == 'b' + || tmp[zero_padding_insertion_ind + 1] == 'B')) zero_padding_insertion_ind += 2; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c3d968ca51..ee86f8fe7a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4197,7 +4197,7 @@ int do_addsub(int command, linenr_T Prenum1) int col; char_u *buf1; char_u buf2[NUMBUFLEN]; - int hex; /* 'X' or 'x': hex; '0': octal */ + int pre; /* 'X' or 'x': hex; '0': octal; 'B' or 'b': bin */ static int hexupper = FALSE; /* 0xABC */ unsigned long n, oldn; char_u *ptr; @@ -4206,6 +4206,7 @@ int do_addsub(int command, linenr_T Prenum1) int todel; int dohex; int dooct; + int dobin; int doalp; int firstdigit; int negative; @@ -4213,6 +4214,7 @@ int do_addsub(int command, linenr_T Prenum1) dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); /* "heX" */ dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); /* "Octal" */ + dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); /* "Bin" */ doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); /* "alPha" */ ptr = get_cursor_line_ptr(); @@ -4222,19 +4224,44 @@ int do_addsub(int command, linenr_T Prenum1) * First check if we are on a hexadecimal number, after the "0x". */ col = curwin->w_cursor.col; + + if (dobin) + while (col > 0 && ascii_isbdigit(ptr[col])) + --col; + if (dohex) while (col > 0 && ascii_isxdigit(ptr[col])) --col; - if ( dohex - && col > 0 + if ( dobin + && dohex + && ! ((col > 0 && (ptr[col] == 'X' || ptr[col] == 'x') && ptr[col - 1] == '0' - && ascii_isxdigit(ptr[col + 1])) { - /* - * Found hexadecimal number, move to its start. - */ - --col; + && ascii_isxdigit(ptr[col + 1])))) { + + /* In case of binary/hexadecimal pattern overlap match, rescan */ + + col = curwin->w_cursor.col; + + while (col > 0 && ascii_isdigit(ptr[col])) + col--; + } + + if (( dohex + && col > 0 + && (ptr[col] == 'X' + || ptr[col] == 'x') + && ptr[col - 1] == '0' + && ascii_isxdigit(ptr[col + 1])) || + ( dobin + && col > 0 + && (ptr[col] == 'B' + || ptr[col] == 'b') + && ptr[col - 1] == '0' + && ascii_isbdigit(ptr[col + 1]))) { + /* Found hexadecimal or binary number, move to its start. */ + --col; } else { /* * Search forward and then backward to find the start of number. @@ -4297,10 +4324,10 @@ int do_addsub(int command, linenr_T Prenum1) } /* get the number value (unsigned) */ - vim_str2nr(ptr + col, &hex, &length, dooct, dohex, NULL, &n); + vim_str2nr(ptr + col, &pre, &length, dobin, dooct, dohex, NULL, &n); - /* ignore leading '-' for hex and octal numbers */ - if (hex && negative) { + /* ignore leading '-' for hex, octal and bin numbers */ + if (pre && negative) { ++col; --length; negative = FALSE; @@ -4320,7 +4347,7 @@ int do_addsub(int command, linenr_T Prenum1) n += (unsigned long)Prenum1; /* handle wraparound for decimal numbers */ - if (!hex) { + if (!pre) { if (subtract) { if (n > oldn) { n = 1 + (n ^ (unsigned long)-1); @@ -4370,23 +4397,38 @@ int do_addsub(int command, linenr_T Prenum1) if (negative) { *ptr++ = '-'; } - if (hex) { + if (pre) { *ptr++ = '0'; --length; } - if (hex == 'x' || hex == 'X') { - *ptr++ = hex; + if (pre == 'b' || pre == 'B' + || pre == 'x' || pre == 'X') { + *ptr++ = pre; --length; } /* * Put the number characters in buf2[]. */ - if (hex == 0) + if (pre == 'b' || pre == 'B') { + + size_t bits = 0; + size_t pos = 0; + + /* leading zeros */ + for (bits = 8 * sizeof(unsigned long); bits > 0; bits--) + if ((n >> (bits - 1)) & 0x1) break; + + while (bits > 0) + buf2[pos++] = ((n >> --bits) & 0x1) ? '1' : '0'; + + buf2[pos] = '\0'; + + } else if (pre == 0) sprintf((char *)buf2, "%" PRIu64, (uint64_t)n); - else if (hex == '0') + else if (pre == '0') sprintf((char *)buf2, "%" PRIo64, (uint64_t)n); - else if (hex && hexupper) + else if (pre && hexupper) sprintf((char *)buf2, "%" PRIX64, (uint64_t)n); else sprintf((char *)buf2, "%" PRIx64, (uint64_t)n); @@ -4398,7 +4440,7 @@ int do_addsub(int command, linenr_T Prenum1) * Don't do this when * the result may look like an octal number. */ - if (firstdigit == '0' && !(dooct && hex == 0)) + if (firstdigit == '0' && !(dooct && pre == 0)) while (length-- > 0) *ptr++ = '0'; *ptr = NUL; diff --git a/src/nvim/option.c b/src/nvim/option.c index 2ac1abeeba..4ba5c4deeb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -260,7 +260,7 @@ typedef struct vimoption { static char *(p_ambw_values[]) = {"single", "double", NULL}; static char *(p_bg_values[]) = {"light", "dark", NULL}; -static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL}; +static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", NULL}; static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; static char *(p_wop_values[]) = {"tagfile", NULL}; static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; @@ -1431,7 +1431,7 @@ do_set ( } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative (for 'undolevels'), octal and // hex numbers. - vim_str2nr(arg, NULL, &i, true, true, &value, NULL); + vim_str2nr(arg, NULL, &i, true, true, true, &value, NULL); if (arg[i] != NUL && !ascii_iswhite(arg[i])) { errmsg = e_invarg; goto skip; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 0eccf63e15..3dd37cb5dc 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1599,7 +1599,7 @@ return { deny_duplicates=true, alloced=true, varname='p_nf', - defaults={if_true={vi="octal,hex", vim="hex"}} + defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}} }, { full_name='number', abbreviation='nu', diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 420e8e2b70..794ae91a19 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1095,7 +1095,9 @@ spell_check ( // 0X99FF. But always do check spelling to find "3GPP" and "11 // julifeest". if (*ptr >= '0' && *ptr <= '9') { - if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) + if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) + mi.mi_end = skipbin(ptr + 2); + else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) mi.mi_end = skiphex(ptr + 2); else mi.mi_end = skipdigits(ptr); diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 71d7556880..51ff0b8df9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -35,7 +35,7 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ -#define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */ +#define NUMBUFLEN 65 /* length of a buffer to store a number in ASCII */ #define MAX_TYPENR 65535 diff --git a/test/functional/eval/printf_spec.lua b/test/functional/eval/printf_spec.lua new file mode 100644 index 0000000000..00afb30e17 --- /dev/null +++ b/test/functional/eval/printf_spec.lua @@ -0,0 +1,58 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local funcs = helpers.funcs +local exc_exec = helpers.exc_exec + +describe('printf()', function() + it('works with zero and %b', function() + eq('0', funcs.printf('%lb', 0)) + eq('0', funcs.printf('%llb', 0)) + eq('0', funcs.printf('%zb', 0)) + end) + it('works with one and %b', function() + eq('1', funcs.printf('%b', 1)) + eq('1', funcs.printf('%lb', 1)) + eq('1', funcs.printf('%llb', 1)) + eq('1', funcs.printf('%zb', 1)) + end) + it('works with 0xff and %b', function() + eq('11111111', funcs.printf('%b', 0xff)) + eq('11111111', funcs.printf('%lb', 0xff)) + eq('11111111', funcs.printf('%llb', 0xff)) + eq('11111111', funcs.printf('%zb', 0xff)) + end) + it('accepts width modifier with %b', function() + eq(' 1', funcs.printf('%3b', 1)) + end) + it('accepts prefix modifier with %b', function() + eq('0b1', funcs.printf('%#b', 1)) + end) + it('writes capital B with %B', function() + eq('0B1', funcs.printf('%#B', 1)) + end) + it('accepts prefix, zero-fill and width modifiers with %b', function() + eq('0b001', funcs.printf('%#05b', 1)) + end) + it('accepts prefix and width modifiers with %b', function() + eq(' 0b1', funcs.printf('%#5b', 1)) + end) + it('does not write prefix for zero with prefix and width modifier used with %b', function() + eq(' 0', funcs.printf('%#5b', 0)) + end) + it('accepts precision modifier with %b', function() + eq('00000', funcs.printf('%.5b', 0)) + end) + it('accepts all modifiers with %b at once', function() + -- zero-fill modifier is ignored when used with left-align + -- force-sign and add-blank are ignored + -- use-grouping-characters modifier is ignored always + eq('0b00011 ', funcs.printf('% \'+#0-10.5b', 3)) + end) + it('errors out when %b modifier is used for a list', function() + eq('Vim(call):E745: Using a List as a Number', exc_exec('call printf("%b", [])')) + end) + it('errors out when %b modifier is used for a float', function() + eq('Vim(call):E805: Using a Float as a Number', exc_exec('call printf("%b", 3.1415926535)')) + end) +end) diff --git a/test/functional/legacy/035_increment_and_decrement_spec.lua b/test/functional/legacy/035_increment_and_decrement_spec.lua index 20c0cc4206..e6252c384b 100644 --- a/test/functional/legacy/035_increment_and_decrement_spec.lua +++ b/test/functional/legacy/035_increment_and_decrement_spec.lua @@ -11,34 +11,40 @@ describe('increment and decrement commands', function() it('should work', function() -- Insert some numbers in various bases. insert([[ - 100 0x100 077 0 - 100 0x100 077 + 0b101 100 0x100 077 0 + 0b101 100 0x100 077 100 0x100 077 0xfF 0xFf - 100 0x100 077]]) + 100 0x100 077 + 0x0b101 0b1101]]) -- Increment and decrement numbers in the first row, interpreting the -- numbers as decimal, octal or hexadecimal. - execute('set nrformats=octal,hex', '1') - feed('102ll64128$') + execute('set nrformats=bin,octal,hex', '1') + feed('63l102ll64128$') -- For the second row, treat the numbers as decimal or octal. -- 0x100 should be interpreted as decimal 0, the character x, and decimal 100. execute('set nrformats=octal', '2') - feed('0102l2w65129blx6lD') + feed('0w102l2w65129blx6lD') -- For the third row, treat the numbers as decimal or hexadecimal. -- 077 should be interpreted as decimal 77. execute('set nrformats=hex', '3') feed('0101l257Txldt ') - -- For the last row, interpret all numbers as decimal. + -- For the fourth row, interpret all numbers as decimal. execute('set nrformats=', '4') feed('0200l100w78') + -- For the last row, interpret as binary and hexadecimal. + execute('set nrformats=bin,hex', '5') + feed('010065l6432') + expect([[ - 0 0x0ff 0000 -1 - 0 1x100 0777777 + 0b011 0 0x0ff 0000 -1 + 1b101 0 1x100 0777777 -1 0x0 078 0xFE 0xfe - -100 -100x100 000]]) + -100 -100x100 000 + 0x0b0de 0b0101101]]) end) end) diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 585b391198..65defbae96 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -600,4 +600,39 @@ describe(':sort', function() eq('Vim(sort):E474: Invalid argument', eval('tmpvar')) expect(text) end) + + it('binary', function() + insert([[ + 0b111000 + 0b101100 + 0b101001 + 0b101001 + 0b101000 + 0b000000 + 0b001000 + 0b010000 + 0b101000 + 0b100000 + 0b101010 + 0b100010 + 0b100100 + 0b100010]]) + execute([[sort b]]) + expect([[ + 0b000000 + 0b001000 + 0b010000 + 0b100000 + 0b100010 + 0b100010 + 0b100100 + 0b101000 + 0b101000 + 0b101001 + 0b101001 + 0b101010 + 0b101100 + 0b111000]]) + end) + end) |