# HG changeset patch # User Christian Brabandt # Date 1453243505 -3600 # Node ID 20dc2763a3b989a105a140894493daaa05f883aa # Parent f56cebad5ba224977859bd39d171a9c59f8128a7 commit https://github.com/vim/vim/commit/f7edf40448a09e04eec3bd05e043f7fea93b07c9 Author: Bram Moolenaar Date: Tue Jan 19 23:36:15 2016 +0100 patch 7.4.1143 Problem: Can't sort on floating point numbers. Solution: Add the "f" flag to ":sort". (Alex Jakushev) Also add the "f" flag to sort(). diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2016 Jan 02 +*change.txt* For Vim version 7.4. Last change: 2016 Jan 19 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1745,7 +1745,7 @@ Vim has a sorting function and a sorting found here: |sort()|, |uniq()|. *:sor* *:sort* -:[range]sor[t][!] [i][u][r][n][x][o][b] [/{pattern}/] +:[range]sor[t][!] [b][f][i][n][o][r][u][x] [/{pattern}/] Sort lines in [range]. When no range is given all lines are sorted. @@ -1753,10 +1753,18 @@ found here: |sort()|, |uniq()|. With [i] case is ignored. + Options [n][f][x][o][b] are mutually exclusive. + With [n] sorting is done on the first decimal number in the line (after or inside a {pattern} match). One leading '-' is included in the number. + With [f] sorting is done on the Float in the line. + The value of Float is determined similar to passing + the text (after or inside a {pattern} match) to + str2float() function. This option is available only + if Vim was compiled with Floating point support. + With [x] sorting is done on the first hexadecimal number in the line (after or inside a {pattern} match). A leading "0x" or "0X" is ignored. @@ -1768,10 +1776,10 @@ found here: |sort()|, |uniq()|. 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 - will be kept in their original order. + With [u] (u stands for unique) only keep the first of + a sequence of identical lines (ignoring case when [i] + is used). Without this flag, a sequence of identical + lines will be kept in their original order. Note that leading and trailing white space may cause lines to be different. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -809,6 +809,9 @@ static typval_T *alloc_tv __ARGS((void)) static typval_T *alloc_string_tv __ARGS((char_u *string)); static void init_tv __ARGS((typval_T *varp)); static long get_tv_number __ARGS((typval_T *varp)); +#ifdef FEAT_FLOAT +static float_T get_tv_float(typval_T *varp); +#endif static linenr_T get_tv_lnum __ARGS((typval_T *argvars)); static linenr_T get_tv_lnum_buf __ARGS((typval_T *argvars, buf_T *buf)); static char_u *get_tv_string __ARGS((typval_T *varp)); @@ -18143,6 +18146,9 @@ typedef struct static int item_compare_ic; static int item_compare_numeric; static int item_compare_numbers; +#ifdef FEAT_FLOAT +static int item_compare_float; +#endif static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; @@ -18182,6 +18188,16 @@ item_compare(s1, s2) return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } +#ifdef FEAT_FLOAT + if (item_compare_float) + { + float_T v1 = get_tv_float(tv1); + float_T v2 = get_tv_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } +#endif + /* 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. */ @@ -18316,6 +18332,9 @@ do_sort_uniq(argvars, rettv, sort) item_compare_ic = FALSE; item_compare_numeric = FALSE; item_compare_numbers = FALSE; +#ifdef FEAT_FLOAT + item_compare_float = FALSE; +#endif item_compare_func = NULL; item_compare_selfdict = NULL; if (argvars[1].v_type != VAR_UNKNOWN) @@ -18346,6 +18365,13 @@ do_sort_uniq(argvars, rettv, sort) item_compare_func = NULL; item_compare_numbers = TRUE; } +#ifdef FEAT_FLOAT + else if (STRCMP(item_compare_func, "f") == 0) + { + item_compare_func = NULL; + item_compare_float = TRUE; + } +#endif else if (STRCMP(item_compare_func, "i") == 0) { item_compare_func = NULL; @@ -21613,6 +21639,40 @@ get_tv_number_chk(varp, denote) return n; } +#ifdef FEAT_FLOAT + static float_T +get_tv_float(varp) + typval_T *varp; +{ + switch (varp->v_type) + { + case VAR_NUMBER: + return (float_T)(varp->vval.v_number); +#ifdef FEAT_FLOAT + case VAR_FLOAT: + return varp->vval.v_float; + break; +#endif + 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; + default: + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + return 0; +} +#endif + /* * Get the lnum from the first argument. * Also accepts ".", "$", etc., but that only works for the current buffer. diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -275,18 +275,30 @@ linelen(has_tab) static char_u *sortbuf1; static char_u *sortbuf2; -static int sort_ic; /* ignore case */ -static int sort_nr; /* sort on number */ -static int sort_rx; /* sort on regex instead of skipping it */ - -static int sort_abort; /* flag to indicate if sorting has been interrupted */ +static int sort_ic; /* ignore case */ +static int sort_nr; /* sort on number */ +static int sort_rx; /* sort on regex instead of skipping it */ +#ifdef FEAT_FLOAT +static int sort_flt; /* sort on floating number */ +#endif + +static int sort_abort; /* flag to indicate if sorting has been interrupted */ /* Struct to store info to be sorted. */ typedef struct { linenr_T lnum; /* line number */ - long start_col_nr; /* starting column number or number */ - long end_col_nr; /* ending column number */ + union { + struct + { + long start_col_nr; /* starting column number */ + long end_col_nr; /* ending column number */ + } line; + long value; /* value if sorting by integer */ +#ifdef FEAT_FLOAT + float_T value_flt; /* value if sorting by float */ +#endif + } st_u; } sorti_T; static int @@ -319,19 +331,24 @@ sort_compare(s1, s2) /* When sorting numbers "start_col_nr" is the number, not the column * number. */ if (sort_nr) - result = l1.start_col_nr == l2.start_col_nr ? 0 - : l1.start_col_nr > l2.start_col_nr ? 1 : -1; + result = l1.st_u.value == l2.st_u.value ? 0 + : l1.st_u.value > l2.st_u.value ? 1 : -1; +#ifdef FEAT_FLOAT + else if (sort_flt) + result = l1.st_u.value_flt == l2.st_u.value_flt ? 0 + : l1.st_u.value_flt > l2.st_u.value_flt ? 1 : -1; +#endif else { /* We need to copy one line into "sortbuf1", because there is no * guarantee that the first pointer becomes invalid when obtaining the * second one. */ - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, - l1.end_col_nr - l1.start_col_nr + 1); - sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, - l2.end_col_nr - l2.start_col_nr + 1); - sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0; + STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr, + l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1); + sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = 0; + STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr, + l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1); + sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = 0; result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2); @@ -382,6 +399,9 @@ ex_sort(eap) goto sortend; sort_abort = sort_ic = sort_rx = sort_nr = 0; +#ifdef FEAT_FLOAT + sort_flt = 0; +#endif for (p = eap->arg; *p != NUL; ++p) { @@ -393,9 +413,16 @@ ex_sort(eap) sort_rx = TRUE; else if (*p == 'n') { - sort_nr = 2; + sort_nr = 1; ++format_found; } +#ifdef FEAT_FLOAT + else if (*p == 'f') + { + sort_flt = 1; + ++format_found; + } +#endif else if (*p == 'b') { sort_what = STR2NR_BIN + STR2NR_FORCE; @@ -460,7 +487,8 @@ ex_sort(eap) goto sortend; } - /* From here on "sort_nr" is used as a flag for any number sorting. */ + /* From here on "sort_nr" is used as a flag for any integer number + * sorting. */ sort_nr += sort_what; /* @@ -494,7 +522,7 @@ ex_sort(eap) if (regmatch.regprog != NULL) end_col = 0; - if (sort_nr) + if (sort_nr || sort_flt) { /* Make sure vim_str2nr doesn't read any digits past the end * of the match, by temporarily terminating the string there */ @@ -503,27 +531,45 @@ ex_sort(eap) *s2 = NUL; /* Sorting on number: Store the number itself. */ p = s + start_col; - if (sort_what & STR2NR_HEX) - s = skiptohex(p); - else if (sort_what & STR2NR_BIN) - s = skiptobin(p); + if (sort_nr) + { + if (sort_what & STR2NR_HEX) + s = skiptohex(p); + else if (sort_what & STR2NR_BIN) + s = skiptobin(p); + else + s = skiptodigit(p); + if (s > p && s[-1] == '-') + --s; /* include preceding negative sign */ + if (*s == NUL) + /* empty line should sort before any number */ + nrs[lnum - eap->line1].st_u.value = -MAXLNUM; + else + vim_str2nr(s, NULL, NULL, sort_what, + &nrs[lnum - eap->line1].st_u.value, NULL, 0); + } +#ifdef FEAT_FLOAT else - s = skiptodigit(p); - if (s > p && s[-1] == '-') - --s; /* include preceding negative sign */ - if (*s == NUL) - /* empty line should sort before any number */ - nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; - else - vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].start_col_nr, NULL, 0); + { + s = skipwhite(p); + if (*s == '+') + s = skipwhite(s + 1); + + if (*s == NUL) + /* empty line should sort before any number */ + nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX; + else + nrs[lnum - eap->line1].st_u.value_flt = + strtod((char *)s, NULL); + } +#endif *s2 = c; } else { /* Store the column to sort at. */ - nrs[lnum - eap->line1].start_col_nr = start_col; - nrs[lnum - eap->line1].end_col_nr = end_col; + nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col; + nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col; } nrs[lnum - eap->line1].lnum = lnum; diff --git a/src/testdir/test57.in b/src/testdir/test57.in --- a/src/testdir/test57.in +++ b/src/testdir/test57.in @@ -32,6 +32,7 @@ STARTTEST :/^t27:/+1,/^t28/-1sort no :/^t28:/+1,/^t29/-1sort b :/^t29:/+1,/^t30/-1sort b +:/^t30:/+1,/^t31/-1sort f :/^t01:/,$wq! test.out ENDTEST @@ -496,9 +497,9 @@ c321d b322b b321 b321b -t28: binary +t28: binary 0b111000 0b101100 0b101001 @@ -513,9 +514,9 @@ 0b101010 0b100010 0b100100 0b100010 -t29: binary with leading characters +t29: binary with leading characters 0b100010 0b010000 0b101001 @@ -530,4 +531,15 @@ ab0b100000 0b101010 0b000000 b0b111000 -t30: done + + +t30: float +1.234 +0.88 +123.456 +1.15e-6 +-1.1e3 +-1.01e3 + + +t31: done diff --git a/src/testdir/test57.ok b/src/testdir/test57.ok --- a/src/testdir/test57.ok +++ b/src/testdir/test57.ok @@ -453,6 +453,8 @@ c321d b322b b321 b321b + + t28: binary @@ -487,4 +489,13 @@ a0b101001 0b101010 b0b101100 b0b111000 -t30: done +t30: float + + +-1.1e3 +-1.01e3 +1.15e-6 +0.88 +1.234 +123.456 +t31: done diff --git a/src/testdir/test_sort.vim b/src/testdir/test_sort.vim --- a/src/testdir/test_sort.vim +++ b/src/testdir/test_sort.vim @@ -17,3 +17,7 @@ func Test_sort_numbers() call assert_equal([3, 13, 28], sort([13, 28, 3], 'N')) call assert_equal(['3', '13', '28'], sort(['13', '28', '3'], 'N')) endfunc + +func Test_sort_float() + call assert_equal([0.28, 3, 13.5], sort([13.5, 0.28, 3], 'f')) +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1143, +/**/ 1142, /**/ 1141,