To: vim_dev@googlegroups.com Subject: Patch 8.2.0988 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0988 Problem: Getting directory contents is always case sorted. Solution: Add sort options and v:collate. (Christian Brabandt, closes #6229) Files: runtime/doc/eval.txt, runtime/doc/mlang.txt, src/auto/configure, src/cmdexpand.c, src/config.h.in, src/configure.ac, src/evalfunc.c, src/evalvars.c, src/ex_cmds2.c, src/fileio.c, src/filepath.c, src/globals.h, src/proto/fileio.pro, src/testdir/test_cmdline.vim, src/testdir/test_functions.vim src/vim.h *** ../vim-8.2.0987/runtime/doc/eval.txt 2020-06-13 15:47:21.074282253 +0200 --- runtime/doc/eval.txt 2020-06-16 19:59:43.652250328 +0200 *************** *** 1740,1745 **** --- 1745,1758 ---- was used the value is 1, otherwise it is 0. Note that this can only be used in autocommands. For user commands || can be used. + *v:collate* *collate-variable* + v:collate The current locale setting for collation order of the runtime + environment. This allows Vim scripts to be aware of the + current locale encoding. Technical: it's the value of + LC_COLLATE. When not using a locale the value is "C". + This variable can not be set directly, use the |:language| + command. + See |multi-lang|. *v:completed_item* *completed_item-variable* v:completed_item *************** *** 2674,2681 **** rand([{expr}]) Number get pseudo-random number range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} ! readdir({dir} [, {expr}]) List file names in {dir} selected by {expr} ! readdirex({dir} [, {expr}]) List file info in {dir} selected by {expr} readfile({fname} [, {type} [, {max}]]) List get list of lines from file {fname} reduce({object}, {func} [, {initial}]) --- 2691,2700 ---- rand([{expr}]) Number get pseudo-random number range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} ! readdir({dir} [, {expr} [, {dict}]]) ! List file names in {dir} selected by {expr} ! readdirex({dir} [, {expr} [, {dict}]]) ! List file info in {dir} selected by {expr} readfile({fname} [, {type} [, {max}]]) List get list of lines from file {fname} reduce({object}, {func} [, {initial}]) *************** *** 7885,7895 **** :echo rand(seed) :echo rand(seed) % 16 " random number 0 - 15 < ! readdir({directory} [, {expr}]) *readdir()* Return a list with file and directory names in {directory}. You can also use |glob()| if you don't need to do complicated things, such as limiting the number of matches. ! The list will be sorted (case sensitive). When {expr} is omitted all entries are included. When {expr} is given, it is evaluated to check what to do: --- 7914,7925 ---- :echo rand(seed) :echo rand(seed) % 16 " random number 0 - 15 < ! readdir({directory} [, {expr} [, {dict}]]) *readdir()* Return a list with file and directory names in {directory}. You can also use |glob()| if you don't need to do complicated things, such as limiting the number of matches. ! The list will be sorted (case sensitive), see the {dict} ! argument below for changing the sort order. When {expr} is omitted all entries are included. When {expr} is given, it is evaluated to check what to do: *************** *** 7907,7924 **** < To skip hidden and backup files: > readdir(dirname, {n -> n !~ '^\.\|\~$'}) < If you want to get a directory tree: > ! function! s:tree(dir) ! return {a:dir : map(readdir(a:dir), \ {_, x -> isdirectory(x) ? ! \ {x : s:tree(a:dir . '/' . x)} : x})} ! endfunction ! echo s:tree(".") < Can also be used as a |method|: > GetDirName()->readdir() < ! readdirex({directory} [, {expr}]) *readdirex()* Extended version of |readdir()|. Return a list of Dictionaries with file and directory information in {directory}. --- 7937,7974 ---- < To skip hidden and backup files: > readdir(dirname, {n -> n !~ '^\.\|\~$'}) + < The optional {dict} argument allows for further custom + values. Currently this is used to specify if and how sorting + should be performed. The dict can have the following members: + + sort How to sort the result returned from the system. + Valid values are: + "none" do not sort (fastest method) + "case" sort case sensitive (byte value of + each character, technically, using + strcmp()) (default) + "icase" sort case insensitive (technically + using strcasecmp()) + "collate" sort using the collation order + of the "POSIX" or "C" |locale| + (technically using strcoll()) + Other values are silently ignored. + + For example, to get a list of all files in the current + directory without sorting the individual entries: > + readdir('.', '1', #{sort: 'none'}) < If you want to get a directory tree: > ! function! s:tree(dir) ! return {a:dir : map(readdir(a:dir), \ {_, x -> isdirectory(x) ? ! \ {x : s:tree(a:dir . '/' . x)} : x})} ! endfunction ! echo s:tree(".") < Can also be used as a |method|: > GetDirName()->readdir() < ! readdirex({directory} [, {expr} [, {dict}]]) *readdirex()* Extended version of |readdir()|. Return a list of Dictionaries with file and directory information in {directory}. *************** *** 7927,7933 **** This is much faster than calling |readdir()| then calling |getfperm()|, |getfsize()|, |getftime()| and |getftype()| for each file and directory especially on MS-Windows. ! The list will be sorted by name (case sensitive). The Dictionary for file and directory information has the following items: --- 7977,7985 ---- This is much faster than calling |readdir()| then calling |getfperm()|, |getfsize()|, |getftime()| and |getftype()| for each file and directory especially on MS-Windows. ! The list will by default be sorted by name (case sensitive), ! the sorting can be changed by using the optional {dict} ! argument, see |readdir()|. The Dictionary for file and directory information has the following items: *************** *** 7968,7973 **** --- 8020,8030 ---- For example, to get a list of files ending in ".txt": > readdirex(dirname, {e -> e.name =~ '.txt$'}) < + For example, to get a list of all files in the current + directory without sorting the individual entries: > + readdirex(dirname, '1', #{sort: 'none'}) + + < Can also be used as a |method|: > GetDirName()->readdirex() < *** ../vim-8.2.0987/runtime/doc/mlang.txt 2019-12-12 12:49:06.000000000 +0100 --- runtime/doc/mlang.txt 2020-06-16 20:00:21.944103819 +0200 *************** *** 37,42 **** --- 37,43 ---- :lan[guage] mes[sages] :lan[guage] cty[pe] :lan[guage] tim[e] + :lan[guage] col[late] Print the current language (aka locale). With the "messages" argument the language used for messages is printed. Technical: LC_MESSAGES. *************** *** 44,58 **** character encoding is printed. Technical: LC_CTYPE. With the "time" argument the language used for strftime() is printed. Technical: LC_TIME. Without argument all parts of the locale are printed (this is system dependent). The current language can also be obtained with the ! |v:lang|, |v:ctype| and |v:lc_time| variables. :lan[guage] {name} :lan[guage] mes[sages] {name} :lan[guage] cty[pe] {name} :lan[guage] tim[e] {name} Set the current language (aka locale) to {name}. The locale {name} must be a valid locale on your system. Some systems accept aliases like "en" or --- 45,63 ---- character encoding is printed. Technical: LC_CTYPE. With the "time" argument the language used for strftime() is printed. Technical: LC_TIME. + With the "collate" argument the language used for + collation order is printed. Technical: LC_COLLATE. Without argument all parts of the locale are printed (this is system dependent). The current language can also be obtained with the ! |v:lang|, |v:ctype|, |v:collate| and |v:lc_time| ! variables. :lan[guage] {name} :lan[guage] mes[sages] {name} :lan[guage] cty[pe] {name} :lan[guage] tim[e] {name} + :lan[guage] col[late] {name} Set the current language (aka locale) to {name}. The locale {name} must be a valid locale on your system. Some systems accept aliases like "en" or *************** *** 72,78 **** With the "time" argument the language used for time and date messages is set. This affects strftime(). This sets $LC_TIME. ! Without an argument both are set, and additionally $LANG is set. When compiled with the |+float| feature the LC_NUMERIC value will always be set to "C", so that floating --- 77,86 ---- With the "time" argument the language used for time and date messages is set. This affects strftime(). This sets $LC_TIME. ! With the "collate" argument the language used for the ! collation order is set. This affects sorting of ! characters. This sets $LC_COLLATE. ! Without an argument all are set, and additionally $LANG is set. When compiled with the |+float| feature the LC_NUMERIC value will always be set to "C", so that floating *** ../vim-8.2.0987/src/auto/configure 2020-06-05 23:16:25.621415440 +0200 --- src/auto/configure 2020-06-16 19:42:20.672234619 +0200 *************** *** 12618,12624 **** getpwent getpwnam getpwuid getrlimit gettimeofday localtime_r lstat \ memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ ! sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt do : --- 12618,12624 ---- getpwent getpwnam getpwuid getrlimit gettimeofday localtime_r lstat \ memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ ! sigprocmask sigvec strcasecmp strcoll strerror strftime stricmp strncasecmp \ strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt do : *** ../vim-8.2.0987/src/cmdexpand.c 2020-06-12 19:35:29.338583023 +0200 --- src/cmdexpand.c 2020-06-16 19:40:39.140621851 +0200 *************** *** 1728,1734 **** { 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); --- 1728,1735 ---- { if ( STRNCMP(arg, "messages", p - arg) == 0 || STRNCMP(arg, "ctype", p - arg) == 0 ! || STRNCMP(arg, "time", p - arg) == 0 ! || STRNCMP(arg, "collate", p - arg) == 0) { xp->xp_context = EXPAND_LOCALES; xp->xp_pattern = skipwhite(p); *** ../vim-8.2.0987/src/config.h.in 2020-06-05 23:16:25.621415440 +0200 --- src/config.h.in 2020-06-16 19:40:39.144621835 +0200 *************** *** 198,203 **** --- 198,204 ---- #undef HAVE_SIGVEC #undef HAVE_SMACK #undef HAVE_STRCASECMP + #undef HAVE_STRCOLL #undef HAVE_STRERROR #undef HAVE_STRFTIME #undef HAVE_STRICMP *** ../vim-8.2.0987/src/configure.ac 2020-06-05 23:16:25.621415440 +0200 --- src/configure.ac 2020-06-16 19:40:39.144621835 +0200 *************** *** 3739,3745 **** getpwent getpwnam getpwuid getrlimit gettimeofday localtime_r lstat \ memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ ! sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt) AC_FUNC_SELECT_ARGTYPES --- 3739,3745 ---- getpwent getpwnam getpwuid getrlimit gettimeofday localtime_r lstat \ memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ ! sigprocmask sigvec strcasecmp strcoll strerror strftime stricmp strncasecmp \ strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt) AC_FUNC_SELECT_ARGTYPES *** ../vim-8.2.0987/src/evalfunc.c 2020-06-13 15:47:21.070282268 +0200 --- src/evalfunc.c 2020-06-16 19:40:39.144621835 +0200 *************** *** 769,776 **** }, {"rand", 0, 1, FEARG_1, ret_number, f_rand}, {"range", 1, 3, FEARG_1, ret_list_number, f_range}, ! {"readdir", 1, 2, FEARG_1, ret_list_string, f_readdir}, ! {"readdirex", 1, 2, FEARG_1, ret_list_dict_any, f_readdirex}, {"readfile", 1, 3, FEARG_1, ret_any, f_readfile}, {"reduce", 2, 3, FEARG_1, ret_any, f_reduce}, {"reg_executing", 0, 0, 0, ret_string, f_reg_executing}, --- 769,776 ---- }, {"rand", 0, 1, FEARG_1, ret_number, f_rand}, {"range", 1, 3, FEARG_1, ret_list_number, f_range}, ! {"readdir", 1, 3, FEARG_1, ret_list_string, f_readdir}, ! {"readdirex", 1, 3, FEARG_1, ret_list_dict_any, f_readdirex}, {"readfile", 1, 3, FEARG_1, ret_any, f_readfile}, {"reduce", 2, 3, FEARG_1, ret_any, f_reduce}, {"reg_executing", 0, 0, 0, ret_string, f_reg_executing}, *** ../vim-8.2.0987/src/evalvars.c 2020-06-14 23:05:06.368915209 +0200 --- src/evalvars.c 2020-06-16 19:40:39.144621835 +0200 *************** *** 145,150 **** --- 145,151 ---- {VV_NAME("versionlong", VAR_NUMBER), VV_RO}, {VV_NAME("echospace", VAR_NUMBER), VV_RO}, {VV_NAME("argv", VAR_LIST), VV_RO}, + {VV_NAME("collate", VAR_STRING), VV_RO}, }; // shorthand *** ../vim-8.2.0987/src/ex_cmds2.c 2020-06-12 19:35:29.338583023 +0200 --- src/ex_cmds2.c 2020-06-16 19:40:39.144621835 +0200 *************** *** 1185,1190 **** --- 1185,1198 ---- loc = get_locale_val(LC_TIME); # endif set_vim_var_string(VV_LC_TIME, loc, -1); + + # ifdef HAVE_GET_LOCALE_VAL + loc = get_locale_val(LC_COLLATE); + # else + // setlocale() not supported: use the default value + loc = (char_u *)"C"; + # endif + set_vim_var_string(VV_COLLATE, loc, -1); } #endif *************** *** 1232,1237 **** --- 1240,1251 ---- name = skipwhite(p); whatstr = "time "; } + else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) + { + what = LC_COLLATE; + name = skipwhite(p); + whatstr = "collate "; + } } if (*name == NUL) *************** *** 1274,1280 **** // Reset $LC_ALL, otherwise it would overrule everything. vim_setenv((char_u *)"LC_ALL", (char_u *)""); ! if (what != LC_TIME) { // Tell gettext() what to translate to. It apparently doesn't // use the currently effective locale. Also do this when --- 1288,1294 ---- // Reset $LC_ALL, otherwise it would overrule everything. vim_setenv((char_u *)"LC_ALL", (char_u *)""); ! if (what != LC_TIME && what != LC_COLLATE) { // Tell gettext() what to translate to. It apparently doesn't // use the currently effective locale. Also do this when *************** *** 1309,1315 **** } # ifdef FEAT_EVAL ! // Set v:lang, v:lc_time and v:ctype to the final result. set_lang_var(); # endif # ifdef FEAT_TITLE --- 1323,1329 ---- } # ifdef FEAT_EVAL ! // Set v:lang, v:lc_time, v:collate and v:ctype to the final result. set_lang_var(); # endif # ifdef FEAT_TITLE *************** *** 1462,1472 **** return (char_u *)"ctype"; if (idx == 2) return (char_u *)"time"; init_locales(); if (locales == NULL) return NULL; ! return locales[idx - 3]; } /* --- 1476,1488 ---- return (char_u *)"ctype"; if (idx == 2) return (char_u *)"time"; + if (idx == 3) + return (char_u *)"collate"; init_locales(); if (locales == NULL) return NULL; ! return locales[idx - 4]; } /* *** ../vim-8.2.0987/src/fileio.c 2020-06-12 22:59:07.262097216 +0200 --- src/fileio.c 2020-06-16 19:40:39.144621835 +0200 *************** *** 35,40 **** --- 35,44 ---- static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags); static char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name"); + #ifdef FEAT_EVAL + static int readdirex_sort; + #endif + void filemess( buf_T *buf, *************** *** 4645,4651 **** name1 = dict_get_string(*(dict_T**)p1, (char_u*)"name", FALSE); name2 = dict_get_string(*(dict_T**)p2, (char_u*)"name", FALSE); ! return STRCMP(name1, name2); } #endif --- 4649,4671 ---- name1 = dict_get_string(*(dict_T**)p1, (char_u*)"name", FALSE); name2 = dict_get_string(*(dict_T**)p2, (char_u*)"name", FALSE); ! if (readdirex_sort == READDIR_SORT_BYTE) ! return STRCMP(name1, name2); ! else if (readdirex_sort == READDIR_SORT_IC) ! return STRICMP(name1, name2); ! else ! return STRCOLL(name1, name2); ! } ! ! static int ! compare_readdir_item(const void *s1, const void *s2) ! { ! if (readdirex_sort == READDIR_SORT_BYTE) ! return STRCMP(*(char **)s1, *(char **)s2); ! else if (readdirex_sort == READDIR_SORT_IC) ! return STRICMP(*(char **)s1, *(char **)s2); ! else ! return STRCOLL(*(char **)s1, *(char **)s2); } #endif *************** *** 4663,4669 **** char_u *path, int withattr UNUSED, void *context, ! int (*checkitem)(void *context, void *item)) { int failed = FALSE; char_u *p; --- 4683,4690 ---- char_u *path, int withattr UNUSED, void *context, ! int (*checkitem)(void *context, void *item), ! int sort) { int failed = FALSE; char_u *p; *************** *** 4687,4692 **** --- 4708,4715 ---- else \ vim_free(item); \ } while (0) + + readdirex_sort = READDIR_SORT_BYTE; # else # define FREE_ITEM(item) vim_free(item) # endif *************** *** 4844,4858 **** # undef FREE_ITEM ! if (!failed && gap->ga_len > 0) { # ifdef FEAT_EVAL if (withattr) qsort((void*)gap->ga_data, (size_t)gap->ga_len, sizeof(dict_T*), compare_readdirex_item); else ! # endif sort_strings((char_u **)gap->ga_data, gap->ga_len); } return failed ? FAIL : OK; --- 4867,4885 ---- # undef FREE_ITEM ! if (!failed && gap->ga_len > 0 && sort > READDIR_SORT_NONE) { # ifdef FEAT_EVAL + readdirex_sort = sort; if (withattr) qsort((void*)gap->ga_data, (size_t)gap->ga_len, sizeof(dict_T*), compare_readdirex_item); else ! qsort((void*)gap->ga_data, (size_t)gap->ga_len, sizeof(char_u *), ! compare_readdir_item); ! # else sort_strings((char_u **)gap->ga_data, gap->ga_len); + # endif } return failed ? FAIL : OK; *************** *** 4883,4889 **** exp = vim_strsave(name); if (exp == NULL) return -1; ! if (readdir_core(&ga, exp, FALSE, NULL, NULL) == OK) { for (i = 0; i < ga.ga_len; ++i) { --- 4910,4916 ---- exp = vim_strsave(name); if (exp == NULL) return -1; ! if (readdir_core(&ga, exp, FALSE, NULL, NULL, READDIR_SORT_NONE) == OK) { for (i = 0; i < ga.ga_len; ++i) { *** ../vim-8.2.0987/src/filepath.c 2020-06-12 22:59:07.262097216 +0200 --- src/filepath.c 2020-06-16 19:40:39.144621835 +0200 *************** *** 1405,1410 **** --- 1405,1440 ---- return retval; } + static int + readdirex_dict_arg(typval_T *tv, int *cmp) + { + char_u *compare; + + if (tv->v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return FAIL; + } + + if (dict_find(tv->vval.v_dict, (char_u *)"sort", -1) != NULL) + compare = dict_get_string(tv->vval.v_dict, (char_u *)"sort", FALSE); + else + { + semsg(_(e_no_dict_key), "sort"); + return FAIL; + } + + if (STRCMP(compare, (char_u *) "none") == 0) + *cmp = READDIR_SORT_NONE; + else if (STRCMP(compare, (char_u *) "case") == 0) + *cmp = READDIR_SORT_BYTE; + else if (STRCMP(compare, (char_u *) "icase") == 0) + *cmp = READDIR_SORT_IC; + else if (STRCMP(compare, (char_u *) "collate") == 0) + *cmp = READDIR_SORT_COLLATE; + return OK; + } + /* * "readdir()" function */ *************** *** 1417,1430 **** char_u *p; garray_T ga; int i; if (rettv_list_alloc(rettv) == FAIL) return; path = tv_get_string(&argvars[0]); expr = &argvars[1]; ret = readdir_core(&ga, path, FALSE, (void *)expr, ! (expr->v_type == VAR_UNKNOWN) ? NULL : readdir_checkitem); if (ret == OK) { for (i = 0; i < ga.ga_len; i++) --- 1447,1465 ---- char_u *p; garray_T ga; int i; + int sort = READDIR_SORT_BYTE; if (rettv_list_alloc(rettv) == FAIL) return; path = tv_get_string(&argvars[0]); expr = &argvars[1]; + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && + readdirex_dict_arg(&argvars[2], &sort) == FAIL) + return; + ret = readdir_core(&ga, path, FALSE, (void *)expr, ! (expr->v_type == VAR_UNKNOWN) ? NULL : readdir_checkitem, sort); if (ret == OK) { for (i = 0; i < ga.ga_len; i++) *************** *** 1480,1493 **** char_u *path; garray_T ga; int i; if (rettv_list_alloc(rettv) == FAIL) return; path = tv_get_string(&argvars[0]); expr = &argvars[1]; ret = readdir_core(&ga, path, TRUE, (void *)expr, ! (expr->v_type == VAR_UNKNOWN) ? NULL : readdirex_checkitem); if (ret == OK) { for (i = 0; i < ga.ga_len; i++) --- 1515,1533 ---- char_u *path; garray_T ga; int i; + int sort = READDIR_SORT_BYTE; if (rettv_list_alloc(rettv) == FAIL) return; path = tv_get_string(&argvars[0]); expr = &argvars[1]; + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && + readdirex_dict_arg(&argvars[2], &sort) == FAIL) + return; + ret = readdir_core(&ga, path, TRUE, (void *)expr, ! (expr->v_type == VAR_UNKNOWN) ? NULL : readdirex_checkitem, sort); if (ret == OK) { for (i = 0; i < ga.ga_len; i++) *** ../vim-8.2.0987/src/globals.h 2020-06-15 19:51:52.633404482 +0200 --- src/globals.h 2020-06-16 19:40:39.144621835 +0200 *************** *** 1699,1704 **** --- 1699,1706 ---- EXTERN char e_unknown_option[] INIT(= N_("E113: Unknown option: %s")); EXTERN char e_letunexp[] INIT(= N_("E18: Unexpected characters in :let")); EXTERN char e_reduceempty[] INIT(= N_("E998: Reduce of an empty %s with no initial value")); + // TODO: Change Error Number + EXTERN char e_no_dict_key[] INIT(= N_("E999: Dictionary with key \"%s\" required")); #endif #ifdef FEAT_QUICKFIX EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); *** ../vim-8.2.0987/src/proto/fileio.pro 2020-06-01 16:09:30.266292734 +0200 --- src/proto/fileio.pro 2020-06-16 19:40:39.148621821 +0200 *************** *** 31,37 **** void buf_reload(buf_T *buf, int orig_mode); void buf_store_time(buf_T *buf, stat_T *st, char_u *fname); void write_lnum_adjust(linenr_T offset); ! int readdir_core(garray_T *gap, char_u *path, int withattr, void *context, int (*checkitem)(void *context, void *item)); int delete_recursive(char_u *name); void vim_deltempdir(void); char_u *vim_tempname(int extra_char, int keep); --- 31,37 ---- void buf_reload(buf_T *buf, int orig_mode); void buf_store_time(buf_T *buf, stat_T *st, char_u *fname); void write_lnum_adjust(linenr_T offset); ! int readdir_core(garray_T *gap, char_u *path, int withattr, void *context, int (*checkitem)(void *context, void *item), int sort); int delete_recursive(char_u *name); void vim_deltempdir(void); char_u *vim_tempname(int extra_char, int keep); *** ../vim-8.2.0987/src/testdir/test_cmdline.vim 2020-06-12 19:35:29.338583023 +0200 --- src/testdir/test_cmdline.vim 2020-06-16 19:40:39.148621821 +0200 *************** *** 604,613 **** endfunc func Test_cmdline_complete_languages() let lang = substitute(execute('language messages'), '.*"\(.*\)"$', '\1', '') call feedkeys(":language \\\"\", 'tx') ! call assert_match('^"language .*\.*\.*\', @:) call assert_match('^"language .*\<' . lang . '\>', @:) --- 604,623 ---- endfunc func Test_cmdline_complete_languages() + let lang = substitute(execute('language time'), '.*"\(.*\)"$', '\1', '') + call assert_equal(lang, v:lc_time) + + let lang = substitute(execute('language ctype'), '.*"\(.*\)"$', '\1', '') + call assert_equal(lang, v:ctype) + + let lang = substitute(execute('language collate'), '.*"\(.*\)"$', '\1', '') + call assert_equal(lang, v:collate) + let lang = substitute(execute('language messages'), '.*"\(.*\)"$', '\1', '') + call assert_equal(lang, v:lang) call feedkeys(":language \\\"\", 'tx') ! call assert_match('^"language .*\.*\.*\.*\', @:) call assert_match('^"language .*\<' . lang . '\>', @:) *************** *** 619,624 **** --- 629,637 ---- call feedkeys(":language time \\\"\", 'tx') call assert_match('^"language .*\<' . lang . '\>', @:) + + call feedkeys(":language collate \\\"\", 'tx') + call assert_match('^"language .*\<' . lang . '\>', @:) endfunc func Test_cmdline_complete_env_variable() *** ../vim-8.2.0987/src/testdir/test_functions.vim 2020-06-15 23:18:08.924482716 +0200 --- src/testdir/test_functions.vim 2020-06-16 19:40:39.148621821 +0200 *************** *** 1937,1942 **** --- 1937,2021 ---- eval 'Xdir'->delete('rf') endfunc + func Test_readdirex_sort() + CheckUnix + " Skip tests on Mac OS X and Cygwin (does not allow several files with different casing) + if has("osxdarwin") || has("osx") || has("macunix") || has("win32unix") + throw 'Skipped: Test_readdirex_sort on systems that do not allow this using the default filesystem' + endif + let _collate = v:collate + call mkdir('Xdir2') + call writefile(['1'], 'Xdir2/README.txt') + call writefile(['2'], 'Xdir2/Readme.txt') + call writefile(['3'], 'Xdir2/readme.txt') + + " 1) default + let files = readdirex('Xdir2')->map({-> v:val.name}) + let default = copy(files) + call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], files, 'sort using default') + + " 2) no sorting + let files = readdirex('Xdir2', 1, #{sort: 'none'})->map({-> v:val.name}) + let unsorted = copy(files) + call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], sort(files), 'unsorted') + + " 3) sort by case (same as default) + let files = readdirex('Xdir2', 1, #{sort: 'case'})->map({-> v:val.name}) + call assert_equal(default, files, 'sort by case') + + " 4) sort by ignoring case + let files = readdirex('Xdir2', 1, #{sort: 'icase'})->map({-> v:val.name}) + call assert_equal(unsorted->sort('i'), files, 'sort by icase') + + " 5) Default Collation + let collate = v:collate + lang collate C + let files = readdirex('Xdir2', 1, #{sort: 'collate'})->map({-> v:val.name}) + call assert_equal(['README.txt', 'Readme.txt', 'readme.txt'], files, 'sort by C collation') + + " 6) Collation de_DE + " Switch locale, this may not work on the CI system, if the locale isn't + " available + try + lang collate de_DE + let files = readdirex('Xdir2', 1, #{sort: 'collate'})->map({-> v:val.name}) + call assert_equal(['readme.txt', 'Readme.txt', 'README.txt'], files, 'sort by de_DE collation') + catch + throw 'Skipped: de_DE collation is not available' + + finally + exe 'lang collate' collate + eval 'Xdir2'->delete('rf') + endtry + endfunc + + func Test_readdir_sort() + " some more cases for testing sorting for readdirex + let dir = 'Xdir3' + call mkdir(dir) + call writefile(['1'], dir .. '/README.txt') + call writefile(['2'], dir .. '/Readm.txt') + call writefile(['3'], dir .. '/read.txt') + call writefile(['4'], dir .. '/Z.txt') + call writefile(['5'], dir .. '/a.txt') + call writefile(['6'], dir .. '/b.txt') + + " 1) default + let files = readdir(dir) + let default = copy(files) + call assert_equal(default->sort(), files, 'sort using default') + + " 2) sort by case (same as default) + let files = readdir(dir, '1', #{sort: 'case'}) + call assert_equal(default, files, 'sort using default') + + " 3) sort by ignoring case + let files = readdir(dir, '1', #{sort: 'icase'}) + call assert_equal(default->sort('i'), files, 'sort by ignoring case') + + eval dir->delete('rf') + endfunc + func Test_delete_rf() call mkdir('Xdir') call writefile([], 'Xdir/foo.txt') *** ../vim-8.2.0987/src/vim.h 2020-06-10 20:56:55.021354582 +0200 --- src/vim.h 2020-06-16 19:40:39.148621821 +0200 *************** *** 1599,1604 **** --- 1599,1609 ---- # define STRICMP(d, s) vim_stricmp((char *)(d), (char *)(s)) # endif #endif + #ifdef HAVE_STRCOLL + # define STRCOLL(d, s) strcoll((char *)(d), (char *)(s)) + #else + # define STRCOLL(d, s) strcmp((char *)(d), (char *)(s)) + #endif // Like strcpy() but allows overlapped source and destination. #define STRMOVE(d, s) mch_memmove((d), (s), STRLEN(s) + 1) *************** *** 1896,1902 **** #define VALID_PATH 1 #define VALID_HEAD 2 ! // Defines for Vim variables. These must match vimvars[] in eval.c! #define VV_COUNT 0 #define VV_COUNT1 1 #define VV_PREVCOUNT 2 --- 1901,1907 ---- #define VALID_PATH 1 #define VALID_HEAD 2 ! // Defines for Vim variables. These must match vimvars[] in evalvars.c! #define VV_COUNT 0 #define VV_COUNT1 1 #define VV_PREVCOUNT 2 *************** *** 1992,1998 **** #define VV_VERSIONLONG 92 #define VV_ECHOSPACE 93 #define VV_ARGV 94 ! #define VV_LEN 95 // number of v: vars // used for v_number in VAR_BOOL and VAR_SPECIAL #define VVAL_FALSE 0L // VAR_BOOL --- 1997,2004 ---- #define VV_VERSIONLONG 92 #define VV_ECHOSPACE 93 #define VV_ARGV 94 ! #define VV_COLLATE 95 ! #define VV_LEN 96 // number of v: vars // used for v_number in VAR_BOOL and VAR_SPECIAL #define VVAL_FALSE 0L // VAR_BOOL *************** *** 2669,2672 **** --- 2675,2684 ---- #define FSK_IN_STRING 0x04 // TRUE in string, double quote is escaped #define FSK_SIMPLIFY 0x08 // simplify and + // Flags for the readdirex function, how to sort the result + #define READDIR_SORT_NONE 0 // do not sort + #define READDIR_SORT_BYTE 1 // sort by byte order (strcmp), default + #define READDIR_SORT_IC 2 // sort ignoring case (strcasecmp) + #define READDIR_SORT_COLLATE 3 // sort according to collation (strcoll) + #endif // VIM__H *** ../vim-8.2.0987/src/version.c 2020-06-16 11:34:38.318223430 +0200 --- src/version.c 2020-06-16 19:42:15.904252802 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 988, /**/ -- BLACK KNIGHT: I move for no man. ARTHUR: So be it! [hah] [parry thrust] [ARTHUR chops the BLACK KNIGHT's left arm off] ARTHUR: Now stand aside, worthy adversary. BLACK KNIGHT: 'Tis but a scratch. The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///