Patch 8.1.1728
Problem:    Wrong place for command line history viminfo support.
Solution:   Move it to viminfo.c.
Files:      src/ex_getln.c, src/proto/ex_getln.pro, src/viminfo.c,
            src/structs.h


*** ../vim-8.1.1727/src/ex_getln.c      2019-07-16 21:38:48.101430996 +0200
--- src/ex_getln.c      2019-07-21 21:50:54.391963984 +0200
***************
*** 60,80 ****
  static int    extra_char_shift;
  
  #ifdef FEAT_CMDHIST
- typedef struct hist_entry
- {
-     int               hisnum;         /* identifying number */
-     int               viminfo;        /* when TRUE hisstr comes from viminfo 
*/
-     char_u    *hisstr;        /* actual entry, separator char after the NUL */
-     time_t    time_set;       /* when it was typed, zero if unknown */
- } histentry_T;
- 
  static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL};
  static int    hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1};  /* lastused entry */
  static int    hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
                    /* identifying (unique) number of newest history entry */
  static int    hislen = 0;             /* actual length of history tables */
- 
- static int    hist_char2type(int c);
  #endif
  
  #ifdef FEAT_RIGHTLEFT
--- 60,70 ----
***************
*** 116,124 ****
  static int    ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
  # endif
  #endif
- #ifdef FEAT_CMDHIST
- static void   clear_hist_entry(histentry_T *hisptr);
- #endif
  
  #ifdef FEAT_CMDWIN
  static int    open_cmdwin(void);
--- 106,111 ----
***************
*** 5873,5879 ****
  /*
   * Translate a history character to the associated type number.
   */
!     static int
  hist_char2type(int c)
  {
      if (c == ':')
--- 5860,5866 ----
  /*
   * Translate a history character to the associated type number.
   */
!     int
  hist_char2type(int c)
  {
      if (c == ':')
***************
*** 6010,6016 ****
      }
  }
  
!     static void
  clear_hist_entry(histentry_T *hisptr)
  {
      hisptr->hisnum = 0;
--- 5997,6003 ----
      }
  }
  
!     void
  clear_hist_entry(histentry_T *hisptr)
  {
      hisptr->hisnum = 0;
***************
*** 6023,6029 ****
   * Check if command line 'str' is already in history.
   * If 'move_to_front' is TRUE, matching entry is moved to end of history.
   */
!     static int
  in_history(
      int           type,
      char_u  *str,
--- 6010,6016 ----
   * Check if command line 'str' is already in history.
   * If 'move_to_front' is TRUE, matching entry is moved to end of history.
   */
!     int
  in_history(
      int           type,
      char_u  *str,
***************
*** 6629,7107 ****
  #endif
  
  #if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO)
! /*
!  * Buffers for history read from a viminfo file.  Only valid while reading.
!  */
! static histentry_T *viminfo_history[HIST_COUNT] =
!                                              {NULL, NULL, NULL, NULL, NULL};
! static int    viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0};
! static int    viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0};
! static int    viminfo_add_at_front = FALSE;
! 
! /*
!  * Translate a history type number to the associated character.
!  */
!     static int
! hist_type2char(
!     int           type,
!     int           use_question)           /* use '?' instead of '/' */
! {
!     if (type == HIST_CMD)
!       return ':';
!     if (type == HIST_SEARCH)
!     {
!       if (use_question)
!           return '?';
!       else
!           return '/';
!     }
!     if (type == HIST_EXPR)
!       return '=';
!     return '@';
! }
! 
! /*
!  * Prepare for reading the history from the viminfo file.
!  * This allocates history arrays to store the read history lines.
!  */
!     void
! prepare_viminfo_history(int asklen, int writing)
  {
!     int           i;
!     int           num;
!     int           type;
!     int           len;
! 
!     init_history();
!     viminfo_add_at_front = (asklen != 0 && !writing);
!     if (asklen > hislen)
!       asklen = hislen;
! 
!     for (type = 0; type < HIST_COUNT; ++type)
!     {
!       /* Count the number of empty spaces in the history list.  Entries read
!        * from viminfo previously are also considered empty.  If there are
!        * more spaces available than we request, then fill them up. */
!       for (i = 0, num = 0; i < hislen; i++)
!           if (history[type][i].hisstr == NULL || history[type][i].viminfo)
!               num++;
!       len = asklen;
!       if (num > len)
!           len = num;
!       if (len <= 0)
!           viminfo_history[type] = NULL;
!       else
!           viminfo_history[type] = LALLOC_MULT(histentry_T, len);
!       if (viminfo_history[type] == NULL)
!           len = 0;
!       viminfo_hislen[type] = len;
!       viminfo_hisidx[type] = 0;
!     }
  }
  
! /*
!  * Accept a line from the viminfo, store it in the history array when it's
!  * new.
!  */
!     int
! read_viminfo_history(vir_T *virp, int writing)
  {
!     int               type;
!     long_u    len;
!     char_u    *val;
!     char_u    *p;
! 
!     type = hist_char2type(virp->vir_line[0]);
!     if (viminfo_hisidx[type] < viminfo_hislen[type])
!     {
!       val = viminfo_readstring(virp, 1, TRUE);
!       if (val != NULL && *val != NUL)
!       {
!           int sep = (*val == ' ' ? NUL : *val);
! 
!           if (!in_history(type, val + (type == HIST_SEARCH),
!                                         viminfo_add_at_front, sep, writing))
!           {
!               /* Need to re-allocate to append the separator byte. */
!               len = STRLEN(val);
!               p = alloc(len + 2);
!               if (p != NULL)
!               {
!                   if (type == HIST_SEARCH)
!                   {
!                       /* Search entry: Move the separator from the first
!                        * column to after the NUL. */
!                       mch_memmove(p, val + 1, (size_t)len);
!                       p[len] = sep;
!                   }
!                   else
!                   {
!                       /* Not a search entry: No separator in the viminfo
!                        * file, add a NUL separator. */
!                       mch_memmove(p, val, (size_t)len + 1);
!                       p[len + 1] = NUL;
!                   }
!                   viminfo_history[type][viminfo_hisidx[type]].hisstr = p;
!                   viminfo_history[type][viminfo_hisidx[type]].time_set = 0;
!                   viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE;
!                   viminfo_history[type][viminfo_hisidx[type]].hisnum = 0;
!                   viminfo_hisidx[type]++;
!               }
!           }
!       }
!       vim_free(val);
!     }
!     return viminfo_readline(virp);
  }
  
- /*
-  * Accept a new style history line from the viminfo, store it in the history
-  * array when it's new.
-  */
      void
! handle_viminfo_history(
!       garray_T    *values,
!       int         writing)
  {
!     int               type;
!     long_u    len;
!     char_u    *val;
!     char_u    *p;
!     bval_T    *vp = (bval_T *)values->ga_data;
! 
!     /* Check the format:
!      * |{bartype},{histtype},{timestamp},{separator},"text" */
!     if (values->ga_len < 4
!           || vp[0].bv_type != BVAL_NR
!           || vp[1].bv_type != BVAL_NR
!           || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY)
!           || vp[3].bv_type != BVAL_STRING)
!       return;
! 
!     type = vp[0].bv_nr;
!     if (type >= HIST_COUNT)
!       return;
!     if (viminfo_hisidx[type] < viminfo_hislen[type])
!     {
!       val = vp[3].bv_string;
!       if (val != NULL && *val != NUL)
!       {
!           int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR
!                                                     ? vp[2].bv_nr : NUL;
!           int idx;
!           int overwrite = FALSE;
! 
!           if (!in_history(type, val, viminfo_add_at_front, sep, writing))
!           {
!               /* If lines were written by an older Vim we need to avoid
!                * getting duplicates. See if the entry already exists. */
!               for (idx = 0; idx < viminfo_hisidx[type]; ++idx)
!               {
!                   p = viminfo_history[type][idx].hisstr;
!                   if (STRCMP(val, p) == 0
!                         && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1]))
!                   {
!                       overwrite = TRUE;
!                       break;
!                   }
!               }
! 
!               if (!overwrite)
!               {
!                   /* Need to re-allocate to append the separator byte. */
!                   len = vp[3].bv_len;
!                   p = alloc(len + 2);
!               }
!               else
!                   len = 0; /* for picky compilers */
!               if (p != NULL)
!               {
!                   viminfo_history[type][idx].time_set = vp[1].bv_nr;
!                   if (!overwrite)
!                   {
!                       mch_memmove(p, val, (size_t)len + 1);
!                       /* Put the separator after the NUL. */
!                       p[len + 1] = sep;
!                       viminfo_history[type][idx].hisstr = p;
!                       viminfo_history[type][idx].hisnum = 0;
!                       viminfo_history[type][idx].viminfo = TRUE;
!                       viminfo_hisidx[type]++;
!                   }
!               }
!           }
!       }
!     }
  }
  
! /*
!  * Concatenate history lines from viminfo after the lines typed in this Vim.
!  */
!     static void
! concat_history(int type)
  {
!     int idx;
!     int i;
! 
!     idx = hisidx[type] + viminfo_hisidx[type];
!     if (idx >= hislen)
!       idx -= hislen;
!     else if (idx < 0)
!       idx = hislen - 1;
!     if (viminfo_add_at_front)
!       hisidx[type] = idx;
!     else
!     {
!       if (hisidx[type] == -1)
!           hisidx[type] = hislen - 1;
!       do
!       {
!           if (history[type][idx].hisstr != NULL
!                                           || history[type][idx].viminfo)
!               break;
!           if (++idx == hislen)
!               idx = 0;
!       } while (idx != hisidx[type]);
!       if (idx != hisidx[type] && --idx < 0)
!           idx = hislen - 1;
!     }
!     for (i = 0; i < viminfo_hisidx[type]; i++)
!     {
!       vim_free(history[type][idx].hisstr);
!       history[type][idx].hisstr = viminfo_history[type][i].hisstr;
!       history[type][idx].viminfo = TRUE;
!       history[type][idx].time_set = viminfo_history[type][i].time_set;
!       if (--idx < 0)
!           idx = hislen - 1;
!     }
!     idx += 1;
!     idx %= hislen;
!     for (i = 0; i < viminfo_hisidx[type]; i++)
!     {
!       history[type][idx++].hisnum = ++hisnum[type];
!       idx %= hislen;
!     }
  }
  
! #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
!     static int
! sort_hist(const void *s1, const void *s2)
  {
!     histentry_T *p1 = *(histentry_T **)s1;
!     histentry_T *p2 = *(histentry_T **)s2;
! 
!     if (p1->time_set < p2->time_set) return -1;
!     if (p1->time_set > p2->time_set) return 1;
!     return 0;
  }
  #endif
  
- /*
-  * Merge history lines from viminfo and lines typed in this Vim based on the
-  * timestamp;
-  */
-     static void
- merge_history(int type)
- {
-     int               max_len;
-     histentry_T **tot_hist;
-     histentry_T *new_hist;
-     int               i;
-     int               len;
- 
-     /* Make one long list with all entries. */
-     max_len = hislen + viminfo_hisidx[type];
-     tot_hist = ALLOC_MULT(histentry_T *, max_len);
-     new_hist = ALLOC_MULT(histentry_T, hislen );
-     if (tot_hist == NULL || new_hist == NULL)
-     {
-       vim_free(tot_hist);
-       vim_free(new_hist);
-       return;
-     }
-     for (i = 0; i < viminfo_hisidx[type]; i++)
-       tot_hist[i] = &viminfo_history[type][i];
-     len = i;
-     for (i = 0; i < hislen; i++)
-       if (history[type][i].hisstr != NULL)
-           tot_hist[len++] = &history[type][i];
- 
-     /* Sort the list on timestamp. */
-     qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist);
- 
-     /* Keep the newest ones. */
-     for (i = 0; i < hislen; i++)
-     {
-       if (i < len)
-       {
-           new_hist[i] = *tot_hist[i];
-           tot_hist[i]->hisstr = NULL;
-           if (new_hist[i].hisnum == 0)
-               new_hist[i].hisnum = ++hisnum[type];
-       }
-       else
-           clear_hist_entry(&new_hist[i]);
-     }
-     hisidx[type] = (i < len ? i : len) - 1;
- 
-     /* Free what is not kept. */
-     for (i = 0; i < viminfo_hisidx[type]; i++)
-       vim_free(viminfo_history[type][i].hisstr);
-     for (i = 0; i < hislen; i++)
-       vim_free(history[type][i].hisstr);
-     vim_free(history[type]);
-     history[type] = new_hist;
-     vim_free(tot_hist);
- }
- 
- /*
-  * Finish reading history lines from viminfo.  Not used when writing viminfo.
-  */
-     void
- finish_viminfo_history(vir_T *virp)
- {
-     int       type;
-     int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY;
- 
-     for (type = 0; type < HIST_COUNT; ++type)
-     {
-       if (history[type] == NULL)
-           continue;
- 
-       if (merge)
-           merge_history(type);
-       else
-           concat_history(type);
- 
-       VIM_CLEAR(viminfo_history[type]);
-       viminfo_hisidx[type] = 0;
-     }
- }
- 
- /*
-  * Write history to viminfo file in "fp".
-  * When "merge" is TRUE merge history lines with a previously read viminfo
-  * file, data is in viminfo_history[].
-  * When "merge" is FALSE just write all history lines.  Used for ":wviminfo!".
-  */
-     void
- write_viminfo_history(FILE *fp, int merge)
- {
-     int           i;
-     int           type;
-     int           num_saved;
-     int     round;
- 
-     init_history();
-     if (hislen == 0)
-       return;
-     for (type = 0; type < HIST_COUNT; ++type)
-     {
-       num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
-       if (num_saved == 0)
-           continue;
-       if (num_saved < 0)  /* Use default */
-           num_saved = hislen;
-       fprintf(fp, _("\n# %s History (newest to oldest):\n"),
-                           type == HIST_CMD ? _("Command Line") :
-                           type == HIST_SEARCH ? _("Search String") :
-                           type == HIST_EXPR ? _("Expression") :
-                           type == HIST_INPUT ? _("Input Line") :
-                                       _("Debug Line"));
-       if (num_saved > hislen)
-           num_saved = hislen;
- 
-       /*
-        * Merge typed and viminfo history:
-        * round 1: history of typed commands.
-        * round 2: history from recently read viminfo.
-        */
-       for (round = 1; round <= 2; ++round)
-       {
-           if (round == 1)
-               /* start at newest entry, somewhere in the list */
-               i = hisidx[type];
-           else if (viminfo_hisidx[type] > 0)
-               /* start at newest entry, first in the list */
-               i = 0;
-           else
-               /* empty list */
-               i = -1;
-           if (i >= 0)
-               while (num_saved > 0
-                       && !(round == 2 && i >= viminfo_hisidx[type]))
-               {
-                   char_u  *p;
-                   time_t  timestamp;
-                   int     c = NUL;
- 
-                   if (round == 1)
-                   {
-                       p = history[type][i].hisstr;
-                       timestamp = history[type][i].time_set;
-                   }
-                   else
-                   {
-                       p = viminfo_history[type] == NULL ? NULL
-                                           : viminfo_history[type][i].hisstr;
-                       timestamp = viminfo_history[type] == NULL ? 0
-                                         : viminfo_history[type][i].time_set;
-                   }
- 
-                   if (p != NULL && (round == 2
-                                      || !merge
-                                      || !history[type][i].viminfo))
-                   {
-                       --num_saved;
-                       fputc(hist_type2char(type, TRUE), fp);
-                       /* For the search history: put the separator in the
-                        * second column; use a space if there isn't one. */
-                       if (type == HIST_SEARCH)
-                       {
-                           c = p[STRLEN(p) + 1];
-                           putc(c == NUL ? ' ' : c, fp);
-                       }
-                       viminfo_writestring(fp, p);
- 
-                       {
-                           char    cbuf[NUMBUFLEN];
- 
-                           /* New style history with a bar line. Format:
-                            * 
|{bartype},{histtype},{timestamp},{separator},"text" */
-                           if (c == NUL)
-                               cbuf[0] = NUL;
-                           else
-                               sprintf(cbuf, "%d", c);
-                           fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY,
-                                                type, (long)timestamp, cbuf);
-                           barline_writestring(fp, p, LSIZE - 20);
-                           putc('\n', fp);
-                       }
-                   }
-                   if (round == 1)
-                   {
-                       /* Decrement index, loop around and stop when back at
-                        * the start. */
-                       if (--i < 0)
-                           i = hislen - 1;
-                       if (i == hisidx[type])
-                           break;
-                   }
-                   else
-                   {
-                       /* Increment index. Stop at the end in the while. */
-                       ++i;
-                   }
-               }
-       }
-       for (i = 0; i < viminfo_hisidx[type]; ++i)
-           if (viminfo_history[type] != NULL)
-               vim_free(viminfo_history[type][i].hisstr);
-       VIM_CLEAR(viminfo_history[type]);
-       viminfo_hisidx[type] = 0;
-     }
- }
- #endif /* FEAT_VIMINFO */
- 
  #if defined(FEAT_CMDWIN) || defined(PROTO)
  /*
   * Open a window on the current command line and history.  Allow editing in
--- 6616,6652 ----
  #endif
  
  #if (defined(FEAT_VIMINFO) && defined(FEAT_CMDHIST)) || defined(PROTO)
!     int
! get_hislen(void)
  {
!     return hislen;
  }
  
!     histentry_T *
! get_histentry(int hist_type)
  {
!     return history[hist_type];
  }
  
      void
! set_histentry(int hist_type, histentry_T *entry)
  {
!     history[hist_type] = entry;
  }
  
!     int *
! get_hisidx(int hist_type)
  {
!     return &hisidx[hist_type];
  }
  
!     int *
! get_hisnum(int hist_type)
  {
!     return &hisnum[hist_type];
  }
  #endif
  
  #if defined(FEAT_CMDWIN) || defined(PROTO)
  /*
   * Open a window on the current command line and history.  Allow editing in
*** ../vim-8.1.1727/src/proto/ex_getln.pro      2019-07-04 20:26:17.798397726 
+0200
--- src/proto/ex_getln.pro      2019-07-21 21:36:44.449022096 +0200
***************
*** 34,40 ****
--- 34,43 ----
  int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, 
char_u ***matches);
  int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u 
***file, char_u *((*func)(expand_T *, int)), int escaped);
  void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options);
+ int hist_char2type(int c);
  void init_history(void);
+ void clear_hist_entry(histentry_T *hisptr);
+ int in_history(int type, char_u *str, int move_to_front, int sep, int 
writing);
  int get_histtype(char_u *name);
  void add_to_history(int histype, char_u *new_entry, int in_map, int sep);
  int get_history_idx(int histype);
***************
*** 49,58 ****
  int get_cmdline_type(void);
  int get_list_range(char_u **str, int *num1, int *num2);
  void ex_history(exarg_T *eap);
! void prepare_viminfo_history(int asklen, int writing);
! int read_viminfo_history(vir_T *virp, int writing);
! void handle_viminfo_history(garray_T *values, int writing);
! void finish_viminfo_history(vir_T *virp);
! void write_viminfo_history(FILE *fp, int merge);
  char_u *script_get(exarg_T *eap, char_u *cmd);
  /* vim: set ft=c : */
--- 52,61 ----
  int get_cmdline_type(void);
  int get_list_range(char_u **str, int *num1, int *num2);
  void ex_history(exarg_T *eap);
! int get_hislen(void);
! histentry_T *get_histentry(int hist_type);
! void set_histentry(int hist_type, histentry_T *entry);
! int *get_hisidx(int hist_type);
! int *get_hisnum(int hist_type);
  char_u *script_get(exarg_T *eap, char_u *cmd);
  /* vim: set ft=c : */
*** ../vim-8.1.1727/src/viminfo.c       2019-07-21 19:25:16.654609424 +0200
--- src/viminfo.c       2019-07-21 21:42:26.870931629 +0200
***************
*** 169,174 ****
--- 169,661 ----
      vim_free(line);
  }
  
+ #if defined(FEAT_CMDHIST) || defined(PROTO)
+ /*
+  * Buffers for history read from a viminfo file.  Only valid while reading.
+  */
+ static histentry_T *viminfo_history[HIST_COUNT] =
+                                              {NULL, NULL, NULL, NULL, NULL};
+ static int    viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0, 0};
+ static int    viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0, 0};
+ static int    viminfo_add_at_front = FALSE;
+ 
+ /*
+  * Translate a history type number to the associated character.
+  */
+     static int
+ hist_type2char(
+     int           type,
+     int           use_question)           // use '?' instead of '/'
+ {
+     if (type == HIST_CMD)
+       return ':';
+     if (type == HIST_SEARCH)
+     {
+       if (use_question)
+           return '?';
+       else
+           return '/';
+     }
+     if (type == HIST_EXPR)
+       return '=';
+     return '@';
+ }
+ 
+ /*
+  * Prepare for reading the history from the viminfo file.
+  * This allocates history arrays to store the read history lines.
+  */
+     static void
+ prepare_viminfo_history(int asklen, int writing)
+ {
+     int           i;
+     int           num;
+     int           type;
+     int           len;
+     int           hislen = get_hislen();
+ 
+     init_history();
+     viminfo_add_at_front = (asklen != 0 && !writing);
+     if (asklen > hislen)
+       asklen = hislen;
+ 
+     for (type = 0; type < HIST_COUNT; ++type)
+     {
+       histentry_T *histentry = get_histentry(type);
+ 
+       // Count the number of empty spaces in the history list.  Entries read
+       // from viminfo previously are also considered empty.  If there are
+       // more spaces available than we request, then fill them up.
+       for (i = 0, num = 0; i < hislen; i++)
+           if (histentry[i].hisstr == NULL || histentry[i].viminfo)
+               num++;
+       len = asklen;
+       if (num > len)
+           len = num;
+       if (len <= 0)
+           viminfo_history[type] = NULL;
+       else
+           viminfo_history[type] = LALLOC_MULT(histentry_T, len);
+       if (viminfo_history[type] == NULL)
+           len = 0;
+       viminfo_hislen[type] = len;
+       viminfo_hisidx[type] = 0;
+     }
+ }
+ 
+ /*
+  * Accept a line from the viminfo, store it in the history array when it's
+  * new.
+  */
+     static int
+ read_viminfo_history(vir_T *virp, int writing)
+ {
+     int               type;
+     long_u    len;
+     char_u    *val;
+     char_u    *p;
+ 
+     type = hist_char2type(virp->vir_line[0]);
+     if (viminfo_hisidx[type] < viminfo_hislen[type])
+     {
+       val = viminfo_readstring(virp, 1, TRUE);
+       if (val != NULL && *val != NUL)
+       {
+           int sep = (*val == ' ' ? NUL : *val);
+ 
+           if (!in_history(type, val + (type == HIST_SEARCH),
+                                         viminfo_add_at_front, sep, writing))
+           {
+               // Need to re-allocate to append the separator byte.
+               len = STRLEN(val);
+               p = alloc(len + 2);
+               if (p != NULL)
+               {
+                   if (type == HIST_SEARCH)
+                   {
+                       // Search entry: Move the separator from the first
+                       // column to after the NUL.
+                       mch_memmove(p, val + 1, (size_t)len);
+                       p[len] = sep;
+                   }
+                   else
+                   {
+                       // Not a search entry: No separator in the viminfo
+                       // file, add a NUL separator.
+                       mch_memmove(p, val, (size_t)len + 1);
+                       p[len + 1] = NUL;
+                   }
+                   viminfo_history[type][viminfo_hisidx[type]].hisstr = p;
+                   viminfo_history[type][viminfo_hisidx[type]].time_set = 0;
+                   viminfo_history[type][viminfo_hisidx[type]].viminfo = TRUE;
+                   viminfo_history[type][viminfo_hisidx[type]].hisnum = 0;
+                   viminfo_hisidx[type]++;
+               }
+           }
+       }
+       vim_free(val);
+     }
+     return viminfo_readline(virp);
+ }
+ 
+ /*
+  * Accept a new style history line from the viminfo, store it in the history
+  * array when it's new.
+  */
+     static void
+ handle_viminfo_history(
+       garray_T    *values,
+       int         writing)
+ {
+     int               type;
+     long_u    len;
+     char_u    *val;
+     char_u    *p;
+     bval_T    *vp = (bval_T *)values->ga_data;
+ 
+     // Check the format:
+     // |{bartype},{histtype},{timestamp},{separator},"text"
+     if (values->ga_len < 4
+           || vp[0].bv_type != BVAL_NR
+           || vp[1].bv_type != BVAL_NR
+           || (vp[2].bv_type != BVAL_NR && vp[2].bv_type != BVAL_EMPTY)
+           || vp[3].bv_type != BVAL_STRING)
+       return;
+ 
+     type = vp[0].bv_nr;
+     if (type >= HIST_COUNT)
+       return;
+     if (viminfo_hisidx[type] < viminfo_hislen[type])
+     {
+       val = vp[3].bv_string;
+       if (val != NULL && *val != NUL)
+       {
+           int sep = type == HIST_SEARCH && vp[2].bv_type == BVAL_NR
+                                                     ? vp[2].bv_nr : NUL;
+           int idx;
+           int overwrite = FALSE;
+ 
+           if (!in_history(type, val, viminfo_add_at_front, sep, writing))
+           {
+               // If lines were written by an older Vim we need to avoid
+               // getting duplicates. See if the entry already exists.
+               for (idx = 0; idx < viminfo_hisidx[type]; ++idx)
+               {
+                   p = viminfo_history[type][idx].hisstr;
+                   if (STRCMP(val, p) == 0
+                         && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1]))
+                   {
+                       overwrite = TRUE;
+                       break;
+                   }
+               }
+ 
+               if (!overwrite)
+               {
+                   // Need to re-allocate to append the separator byte.
+                   len = vp[3].bv_len;
+                   p = alloc(len + 2);
+               }
+               else
+                   len = 0; // for picky compilers
+               if (p != NULL)
+               {
+                   viminfo_history[type][idx].time_set = vp[1].bv_nr;
+                   if (!overwrite)
+                   {
+                       mch_memmove(p, val, (size_t)len + 1);
+                       // Put the separator after the NUL.
+                       p[len + 1] = sep;
+                       viminfo_history[type][idx].hisstr = p;
+                       viminfo_history[type][idx].hisnum = 0;
+                       viminfo_history[type][idx].viminfo = TRUE;
+                       viminfo_hisidx[type]++;
+                   }
+               }
+           }
+       }
+     }
+ }
+ 
+ /*
+  * Concatenate history lines from viminfo after the lines typed in this Vim.
+  */
+     static void
+ concat_history(int type)
+ {
+     int               idx;
+     int               i;
+     int               hislen = get_hislen();
+     histentry_T *histentry = get_histentry(type);
+     int               *hisidx = get_hisidx(type);
+     int               *hisnum = get_hisnum(type);
+ 
+     idx = *hisidx + viminfo_hisidx[type];
+     if (idx >= hislen)
+       idx -= hislen;
+     else if (idx < 0)
+       idx = hislen - 1;
+     if (viminfo_add_at_front)
+       *hisidx = idx;
+     else
+     {
+       if (*hisidx == -1)
+           *hisidx = hislen - 1;
+       do
+       {
+           if (histentry[idx].hisstr != NULL || histentry[idx].viminfo)
+               break;
+           if (++idx == hislen)
+               idx = 0;
+       } while (idx != *hisidx);
+       if (idx != *hisidx && --idx < 0)
+           idx = hislen - 1;
+     }
+     for (i = 0; i < viminfo_hisidx[type]; i++)
+     {
+       vim_free(histentry[idx].hisstr);
+       histentry[idx].hisstr = viminfo_history[type][i].hisstr;
+       histentry[idx].viminfo = TRUE;
+       histentry[idx].time_set = viminfo_history[type][i].time_set;
+       if (--idx < 0)
+           idx = hislen - 1;
+     }
+     idx += 1;
+     idx %= hislen;
+     for (i = 0; i < viminfo_hisidx[type]; i++)
+     {
+       histentry[idx++].hisnum = ++*hisnum;
+       idx %= hislen;
+     }
+ }
+ 
+     static int
+ sort_hist(const void *s1, const void *s2)
+ {
+     histentry_T *p1 = *(histentry_T **)s1;
+     histentry_T *p2 = *(histentry_T **)s2;
+ 
+     if (p1->time_set < p2->time_set) return -1;
+     if (p1->time_set > p2->time_set) return 1;
+     return 0;
+ }
+ 
+ /*
+  * Merge history lines from viminfo and lines typed in this Vim based on the
+  * timestamp;
+  */
+     static void
+ merge_history(int type)
+ {
+     int               max_len;
+     histentry_T **tot_hist;
+     histentry_T *new_hist;
+     int               i;
+     int               len;
+     int               hislen = get_hislen();
+     histentry_T *histentry = get_histentry(type);
+     int               *hisidx = get_hisidx(type);
+     int               *hisnum = get_hisnum(type);
+ 
+     // Make one long list with all entries.
+     max_len = hislen + viminfo_hisidx[type];
+     tot_hist = ALLOC_MULT(histentry_T *, max_len);
+     new_hist = ALLOC_MULT(histentry_T, hislen );
+     if (tot_hist == NULL || new_hist == NULL)
+     {
+       vim_free(tot_hist);
+       vim_free(new_hist);
+       return;
+     }
+     for (i = 0; i < viminfo_hisidx[type]; i++)
+       tot_hist[i] = &viminfo_history[type][i];
+     len = i;
+     for (i = 0; i < hislen; i++)
+       if (histentry[i].hisstr != NULL)
+           tot_hist[len++] = &histentry[i];
+ 
+     // Sort the list on timestamp.
+     qsort((void *)tot_hist, (size_t)len, sizeof(histentry_T *), sort_hist);
+ 
+     // Keep the newest ones.
+     for (i = 0; i < hislen; i++)
+     {
+       if (i < len)
+       {
+           new_hist[i] = *tot_hist[i];
+           tot_hist[i]->hisstr = NULL;
+           if (new_hist[i].hisnum == 0)
+               new_hist[i].hisnum = ++*hisnum;
+       }
+       else
+           clear_hist_entry(&new_hist[i]);
+     }
+     *hisidx = (i < len ? i : len) - 1;
+ 
+     // Free what is not kept.
+     for (i = 0; i < viminfo_hisidx[type]; i++)
+       vim_free(viminfo_history[type][i].hisstr);
+     for (i = 0; i < hislen; i++)
+       vim_free(histentry[i].hisstr);
+     vim_free(histentry);
+     set_histentry(type, new_hist);
+     vim_free(tot_hist);
+ }
+ 
+ /*
+  * Finish reading history lines from viminfo.  Not used when writing viminfo.
+  */
+     static void
+ finish_viminfo_history(vir_T *virp)
+ {
+     int       type;
+     int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY;
+ 
+     for (type = 0; type < HIST_COUNT; ++type)
+     {
+       if (get_histentry(type) == NULL)
+           continue;
+ 
+       if (merge)
+           merge_history(type);
+       else
+           concat_history(type);
+ 
+       VIM_CLEAR(viminfo_history[type]);
+       viminfo_hisidx[type] = 0;
+     }
+ }
+ 
+ /*
+  * Write history to viminfo file in "fp".
+  * When "merge" is TRUE merge history lines with a previously read viminfo
+  * file, data is in viminfo_history[].
+  * When "merge" is FALSE just write all history lines.  Used for ":wviminfo!".
+  */
+     static void
+ write_viminfo_history(FILE *fp, int merge)
+ {
+     int           i;
+     int           type;
+     int           num_saved;
+     int     round;
+     int           hislen;
+ 
+     init_history();
+     hislen = get_hislen();
+     if (hislen == 0)
+       return;
+     for (type = 0; type < HIST_COUNT; ++type)
+     {
+       histentry_T *histentry = get_histentry(type);
+       int         *hisidx = get_hisidx(type);
+ 
+       num_saved = get_viminfo_parameter(hist_type2char(type, FALSE));
+       if (num_saved == 0)
+           continue;
+       if (num_saved < 0)  // Use default
+           num_saved = hislen;
+       fprintf(fp, _("\n# %s History (newest to oldest):\n"),
+                           type == HIST_CMD ? _("Command Line") :
+                           type == HIST_SEARCH ? _("Search String") :
+                           type == HIST_EXPR ? _("Expression") :
+                           type == HIST_INPUT ? _("Input Line") :
+                                       _("Debug Line"));
+       if (num_saved > hislen)
+           num_saved = hislen;
+ 
+       /*
+        * Merge typed and viminfo history:
+        * round 1: history of typed commands.
+        * round 2: history from recently read viminfo.
+        */
+       for (round = 1; round <= 2; ++round)
+       {
+           if (round == 1)
+               // start at newest entry, somewhere in the list
+               i = *hisidx;
+           else if (viminfo_hisidx[type] > 0)
+               // start at newest entry, first in the list
+               i = 0;
+           else
+               // empty list
+               i = -1;
+           if (i >= 0)
+               while (num_saved > 0
+                       && !(round == 2 && i >= viminfo_hisidx[type]))
+               {
+                   char_u  *p;
+                   time_t  timestamp;
+                   int     c = NUL;
+ 
+                   if (round == 1)
+                   {
+                       p = histentry[i].hisstr;
+                       timestamp = histentry[i].time_set;
+                   }
+                   else
+                   {
+                       p = viminfo_history[type] == NULL ? NULL
+                                           : viminfo_history[type][i].hisstr;
+                       timestamp = viminfo_history[type] == NULL ? 0
+                                         : viminfo_history[type][i].time_set;
+                   }
+ 
+                   if (p != NULL && (round == 2
+                                      || !merge
+                                      || !histentry[i].viminfo))
+                   {
+                       --num_saved;
+                       fputc(hist_type2char(type, TRUE), fp);
+                       // For the search history: put the separator in the
+                       // second column; use a space if there isn't one.
+                       if (type == HIST_SEARCH)
+                       {
+                           c = p[STRLEN(p) + 1];
+                           putc(c == NUL ? ' ' : c, fp);
+                       }
+                       viminfo_writestring(fp, p);
+ 
+                       {
+                           char    cbuf[NUMBUFLEN];
+ 
+                           // New style history with a bar line. Format:
+                           // 
|{bartype},{histtype},{timestamp},{separator},"text"
+                           if (c == NUL)
+                               cbuf[0] = NUL;
+                           else
+                               sprintf(cbuf, "%d", c);
+                           fprintf(fp, "|%d,%d,%ld,%s,", BARTYPE_HISTORY,
+                                                type, (long)timestamp, cbuf);
+                           barline_writestring(fp, p, LSIZE - 20);
+                           putc('\n', fp);
+                       }
+                   }
+                   if (round == 1)
+                   {
+                       // Decrement index, loop around and stop when back at
+                       // the start.
+                       if (--i < 0)
+                           i = hislen - 1;
+                       if (i == *hisidx)
+                           break;
+                   }
+                   else
+                   {
+                       // Increment index. Stop at the end in the while.
+                       ++i;
+                   }
+               }
+       }
+       for (i = 0; i < viminfo_hisidx[type]; ++i)
+           if (viminfo_history[type] != NULL)
+               vim_free(viminfo_history[type][i].hisstr);
+       VIM_CLEAR(viminfo_history[type]);
+       viminfo_hisidx[type] = 0;
+     }
+ }
+ #endif // FEAT_VIMINFO
+ 
      static void
  write_viminfo_barlines(vir_T *virp, FILE *fp_out)
  {
*** ../vim-8.1.1727/src/structs.h       2019-07-21 19:25:16.654609424 +0200
--- src/structs.h       2019-07-21 19:31:33.645009873 +0200
***************
*** 1115,1120 ****
--- 1115,1131 ----
      garray_T  vir_barlines;   // lines starting with |
  } vir_T;
  
+ /*
+  * Structure used for the command line history.
+  */
+ typedef struct hist_entry
+ {
+     int               hisnum;         /* identifying number */
+     int               viminfo;        /* when TRUE hisstr comes from viminfo 
*/
+     char_u    *hisstr;        /* actual entry, separator char after the NUL */
+     time_t    time_set;       /* when it was typed, zero if unknown */
+ } histentry_T;
+ 
  #define CONV_NONE             0
  #define CONV_TO_UTF8          1
  #define CONV_9_TO_UTF8                2
*** ../vim-8.1.1727/src/version.c       2019-07-21 19:25:16.654609424 +0200
--- src/version.c       2019-07-21 21:43:16.222637934 +0200
***************
*** 779,780 ****
--- 779,782 ----
  {   /* Add new patch number below this line */
+ /**/
+     1728,
  /**/

-- 
A year spent in artificial intelligence is enough to make one
believe in God.

 /// 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/201907211952.x6LJqa5r008346%40masaka.moolenaar.net.

Raspunde prin e-mail lui