changeset 9256:26c7bf23ec1d v7.4.1911

commit https://github.com/vim/vim/commit/1fd99c1ca89a3d13bb53aff4a5a8f5ee740713e5 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jun 9 20:24:28 2016 +0200 patch 7.4.1911 Problem: Recent history lines may be lost when exiting Vim. Solution: Merge history using the timestamp.
author Christian Brabandt <cb@256bit.org>
date Thu, 09 Jun 2016 20:30:07 +0200
parents 8735c9123f66
children bad6fcce0446
files src/ex_cmds.c src/ex_getln.c src/proto/ex_getln.pro src/testdir/test_viminfo.vim src/version.c src/vim.h
diffstat 6 files changed, 204 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -1755,9 +1755,6 @@ static void write_viminfo_version(FILE *
 static void write_viminfo_barlines(vir_T *virp, FILE *fp_out);
 static int  viminfo_errcnt;
 
-#define VIMINFO_VERSION 2
-#define VIMINFO_VERSION_WITH_HISTORY 2
-
     static int
 no_viminfo(void)
 {
@@ -2306,7 +2303,7 @@ read_viminfo_up_to_marks(
 #ifdef FEAT_CMDHIST
     /* Finish reading history items. */
     if (!writing)
-	finish_viminfo_history();
+	finish_viminfo_history(virp);
 #endif
 
     /* Change file names to buffer numbers for fmarks. */
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -5536,6 +5536,7 @@ clear_hist_entry(histentry_T *hisptr)
     hisptr->hisnum = 0;
     hisptr->viminfo = FALSE;
     hisptr->hisstr = NULL;
+    hisptr->time_set = 0;
 }
 
 /*
@@ -6262,6 +6263,8 @@ read_viminfo_history(vir_T *virp, int wr
 		    }
 		    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]++;
 		}
 	    }
@@ -6338,6 +6341,8 @@ handle_viminfo_history(
 			/* 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]++;
 		    }
 		}
@@ -6347,57 +6352,146 @@ handle_viminfo_history(
 }
 
 /*
+ * 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
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+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 = (histentry_T **)alloc(max_len * (int)sizeof(histentry_T *));
+    new_hist = (histentry_T *)alloc(hislen * (int)sizeof(histentry_T));
+    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] = 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;
+}
+
+/*
  * Finish reading history lines from viminfo.  Not used when writing viminfo.
  */
     void
-finish_viminfo_history(void)
+finish_viminfo_history(vir_T *virp)
 {
-    int idx;
-    int i;
     int	type;
+    int merge = virp->vir_version >= VIMINFO_VERSION_WITH_HISTORY;
 
     for (type = 0; type < HIST_COUNT; ++type)
     {
 	if (history[type] == NULL)
 	    continue;
-	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;
+
+	if (merge)
+	    merge_history(type);
 	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;
-	}
+	    concat_history(type);
+
 	vim_free(viminfo_history[type]);
 	viminfo_history[type] = NULL;
 	viminfo_hisidx[type] = 0;
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -51,7 +51,7 @@ 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(bval_T *values, int count, int writing);
-void finish_viminfo_history(void);
+void finish_viminfo_history(vir_T *virp);
 void write_viminfo_history(FILE *fp, int merge);
 void cmd_pchar(int c, int offset);
 int cmd_gchar(int offset);
--- a/src/testdir/test_viminfo.vim
+++ b/src/testdir/test_viminfo.vim
@@ -116,3 +116,66 @@ func Test_cmdline_history()
 
   call delete('Xviminfo')
 endfunc
+
+func Test_cmdline_history_order()
+  call histdel(':')
+  call test_settime(11)
+  call histadd(':', "echo '11'")
+  call test_settime(22)
+  call histadd(':', "echo '22'")
+  call test_settime(33)
+  call histadd(':', "echo '33'")
+  wviminfo Xviminfo
+
+  call histdel(':')
+  " items go in between
+  call test_settime(15)
+  call histadd(':', "echo '15'")
+  call test_settime(27)
+  call histadd(':', "echo '27'")
+
+  rviminfo Xviminfo
+  call assert_equal("echo '33'", histget(':', -1))
+  call assert_equal("echo '27'", histget(':', -2))
+  call assert_equal("echo '22'", histget(':', -3))
+  call assert_equal("echo '15'", histget(':', -4))
+  call assert_equal("echo '11'", histget(':', -5))
+
+  call histdel(':')
+  " items go before and after
+  call test_settime(8)
+  call histadd(':', "echo '8'")
+  call test_settime(39)
+  call histadd(':', "echo '39'")
+
+  rviminfo Xviminfo
+  call assert_equal("echo '39'", histget(':', -1))
+  call assert_equal("echo '33'", histget(':', -2))
+  call assert_equal("echo '22'", histget(':', -3))
+  call assert_equal("echo '11'", histget(':', -4))
+  call assert_equal("echo '8'", histget(':', -5))
+
+  " Check sorting works when writing with merge.
+  call histdel(':')
+  call test_settime(8)
+  call histadd(':', "echo '8'")
+  call test_settime(15)
+  call histadd(':', "echo '15'")
+  call test_settime(27)
+  call histadd(':', "echo '27'")
+  call test_settime(39)
+  call histadd(':', "echo '39'")
+  wviminfo Xviminfo
+  
+  call histdel(':')
+  rviminfo Xviminfo
+  call assert_equal("echo '39'", histget(':', -1))
+  call assert_equal("echo '33'", histget(':', -2))
+  call assert_equal("echo '27'", histget(':', -3))
+  call assert_equal("echo '22'", histget(':', -4))
+  call assert_equal("echo '15'", histget(':', -5))
+  call assert_equal("echo '11'", histget(':', -6))
+  call assert_equal("echo '8'", histget(':', -7))
+
+  call delete('Xviminfo')
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1911,
+/**/
     1910,
 /**/
     1909,
--- a/src/vim.h
+++ b/src/vim.h
@@ -1076,6 +1076,9 @@ extern char *(*dyn_libintl_textdomain)(c
 #define BARTYPE_VERSION 1
 #define BARTYPE_HISTORY 2
 
+#define VIMINFO_VERSION 2
+#define VIMINFO_VERSION_WITH_HISTORY 2
+
 typedef enum {
     BVAL_NR,
     BVAL_STRING,