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 |<bang>|
                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 \<c-a>\<c-b>\"\<cr>", 'tx')
!   call assert_match('^"language .*\<ctype\>.*\<messages\>.*\<time\>', @:)
  
    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 \<c-a>\<c-b>\"\<cr>", 'tx')
!   call assert_match('^"language 
.*\<collate\>.*\<ctype\>.*\<messages\>.*\<time\>', @:)
  
    call assert_match('^"language .*\<' . lang . '\>', @:)
  
***************
*** 619,624 ****
--- 629,637 ----
  
    call feedkeys(":language time \<c-a>\<c-b>\"\<cr>", 'tx')
    call assert_match('^"language .*\<' . lang . '\>', @:)
+ 
+   call feedkeys(":language collate \<c-a>\<c-b>\"\<cr>", '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 <C-H> and <A-x>
  
+ // 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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202006161804.05GI4HHF1411307%40masaka.moolenaar.net.

Raspunde prin e-mail lui