changeset 9284:78712a2f687a v7.4.1925

commit https://github.com/vim/vim/commit/2d35899721da0e9359a9fe1059554f8c4ea7f0c1 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jun 12 21:20:54 2016 +0200 patch 7.4.1925 Problem: Viminfo does not merge file marks properly. Solution: Use a timestamp. Add the :clearjumps command.
author Christian Brabandt <cb@256bit.org>
date Sun, 12 Jun 2016 21:30:06 +0200
parents 0b359cb19c27
children 3e510396a743
files src/ex_cmds.c src/ex_cmds.h src/ex_docmd.c src/mark.c src/proto/mark.pro src/structs.h src/testdir/test_viminfo.vim src/version.c src/vim.h
diffstat 9 files changed, 380 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -1983,6 +1983,8 @@ write_viminfo(char_u *file, int forceit)
 		     */
 		    if (*wp == 'a')
 		    {
+			EMSG2(_("E929: Too many viminfo temp files, like %s!"),
+								    tempname);
 			vim_free(tempname);
 			tempname = NULL;
 			break;
@@ -2164,9 +2166,13 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in
     {
 	if (flags & VIF_WANT_INFO)
 	{
-	    /* Registers are read and newer ones are used when writing. */
 	    if (fp_out != NULL)
+	    {
+		/* Registers and marks are read and kept separate from what
+		 * this Vim is using.  They are merged when writing. */
 		prepare_viminfo_registers();
+		prepare_viminfo_marks();
+	    }
 
 	    eof = read_viminfo_up_to_marks(&vir,
 					 flags & VIF_FORCEIT, fp_out != NULL);
@@ -2200,6 +2206,7 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in
 	write_viminfo_varlist(fp_out);
 #endif
 	write_viminfo_filemarks(fp_out);
+	finish_viminfo_marks();
 	write_viminfo_bufferlist(fp_out);
 	write_viminfo_barlines(&vir, fp_out);
 	count = write_viminfo_marks(fp_out);
@@ -2778,6 +2785,11 @@ read_viminfo_barline(vir_T *virp, int go
 		handle_viminfo_register(&values, force);
 		break;
 
+	    case BARTYPE_MARK:
+		barline_parse(virp, p, &values);
+		handle_viminfo_mark(&values, force);
+		break;
+
 	    default:
 		/* copy unrecognized line (for future use) */
 		if (writing)
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -316,6 +316,9 @@ EX(CMD_clast,		"clast",	ex_cc,
 EX(CMD_close,		"close",	ex_close,
 			BANG|RANGE|NOTADR|COUNT|TRLBAR|CMDWIN,
 			ADDR_WINDOWS),
+EX(CMD_clearjumps,	"clearjumps",	ex_clearjumps,
+			TRLBAR|CMDWIN,
+			ADDR_LINES),
 EX(CMD_cmap,		"cmap",		ex_map,
 			EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN,
 			ADDR_LINES),
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -474,6 +474,7 @@ static void	ex_folddo(exarg_T *eap);
 #endif
 #ifndef FEAT_JUMPLIST
 # define ex_jumps		ex_ni
+# define ex_clearjumps		ex_ni
 # define ex_changes		ex_ni
 #endif
 
--- a/src/mark.c
+++ b/src/mark.c
@@ -106,24 +106,25 @@ setmark_pos(int c, pos_T *pos, int fnum)
 	return OK;
     }
 
-#ifndef EBCDIC
-    if (c > 'z')	    /* some islower() and isupper() cannot handle
-				characters above 127 */
-	return FAIL;
-#endif
-    if (islower(c))
+    if (ASCII_ISLOWER(c))
     {
 	i = c - 'a';
 	curbuf->b_namedm[i] = *pos;
 	return OK;
     }
-    if (isupper(c))
+    if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
     {
-	i = c - 'A';
+	if (VIM_ISDIGIT(c))
+	    i = c - '0' + NMARKS;
+	else
+	    i = c - 'A';
 	namedfm[i].fmark.mark = *pos;
 	namedfm[i].fmark.fnum = fnum;
 	vim_free(namedfm[i].fname);
 	namedfm[i].fname = NULL;
+#ifdef FEAT_VIMINFO
+	namedfm[i].time_set = vim_time();
+#endif
 	return OK;
     }
     return FAIL;
@@ -184,6 +185,9 @@ setpcmark(void)
     fm->fmark.mark = curwin->w_pcmark;
     fm->fmark.fnum = curbuf->b_fnum;
     fm->fname = NULL;
+# ifdef FEAT_VIMINFO
+    fm->time_set = vim_time();
+# endif
 #endif
 }
 
@@ -634,6 +638,9 @@ clrallmarks(buf_T *buf)
 	{
 	    namedfm[i].fmark.mark.lnum = 0;
 	    namedfm[i].fname = NULL;
+#ifdef FEAT_VIMINFO
+	    namedfm[i].time_set = 0;
+#endif
 	}
 
     for (i = 0; i < NMARKS; i++)
@@ -849,6 +856,9 @@ ex_delmarks(exarg_T *eap)
 			namedfm[n].fmark.mark.lnum = 0;
 			vim_free(namedfm[n].fname);
 			namedfm[n].fname = NULL;
+#ifdef FEAT_VIMINFO
+			namedfm[n].time_set = 0;
+#endif
 		    }
 		}
 	    }
@@ -918,6 +928,14 @@ ex_jumps(exarg_T *eap UNUSED)
 	MSG_PUTS("\n>");
 }
 
+    void
+ex_clearjumps(exarg_T *eap UNUSED)
+{
+    free_jumplist(curwin);
+    curwin->w_jumplistlen = 0;
+    curwin->w_jumplistidx = 0;
+}
+
 /*
  * print the changelist
  */
@@ -1400,11 +1418,199 @@ read_viminfo_filemark(vir_T *virp, int f
 	    vim_free(fm->fname);
 	    fm->fname = viminfo_readstring(virp, (int)(str - virp->vir_line),
 								       FALSE);
+	    fm->time_set = 0;
 	}
     }
     return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
 }
 
+static xfmark_T *vi_namedfm = NULL;
+#ifdef FEAT_JUMPLIST
+static xfmark_T *vi_jumplist = NULL;
+static int vi_jumplist_len = 0;
+#endif
+
+/*
+ * Prepare for reading viminfo marks when writing viminfo later.
+ */
+    void
+prepare_viminfo_marks(void)
+{
+    vi_namedfm = (xfmark_T *)alloc_clear((NMARKS + EXTRA_MARKS)
+						     * (int)sizeof(xfmark_T));
+#ifdef FEAT_JUMPLIST
+    vi_jumplist = (xfmark_T *)alloc_clear(JUMPLISTSIZE
+						     * (int)sizeof(xfmark_T));
+    vi_jumplist_len = 0;
+#endif
+}
+
+    void
+finish_viminfo_marks(void)
+{
+    int		i;
+
+    if (vi_namedfm != NULL)
+    {
+	for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
+	    vim_free(vi_namedfm[i].fname);
+	vim_free(vi_namedfm);
+	vi_namedfm = NULL;
+    }
+#ifdef FEAT_JUMPLIST
+    if (vi_jumplist != NULL)
+    {
+	for (i = 0; i < vi_jumplist_len; ++i)
+	    vim_free(vi_jumplist[i].fname);
+	vim_free(vi_jumplist);
+	vi_jumplist = NULL;
+    }
+#endif
+}
+
+/*
+ * Accept a new style mark line from the viminfo, store it when it's new.
+ */
+    void
+handle_viminfo_mark(garray_T *values, int force)
+{
+    bval_T	*vp = (bval_T *)values->ga_data;
+    int		name;
+    linenr_T	lnum;
+    colnr_T	col;
+    time_t	timestamp;
+    xfmark_T	*fm = NULL;
+
+    /* Check the format:
+     * |{bartype},{name},{lnum},{col},{timestamp},{filename} */
+    if (values->ga_len < 5
+	    || vp[0].bv_type != BVAL_NR
+	    || vp[1].bv_type != BVAL_NR
+	    || vp[2].bv_type != BVAL_NR
+	    || vp[3].bv_type != BVAL_NR
+	    || vp[4].bv_type != BVAL_STRING)
+	return;
+
+    name = vp[0].bv_nr;
+    if (name != '\'' && !VIM_ISDIGIT(name) && !ASCII_ISUPPER(name))
+	return;
+    lnum = vp[1].bv_nr;
+    col = vp[2].bv_nr;
+    if (lnum <= 0 || col < 0)
+	return;
+    timestamp = (time_t)vp[3].bv_nr;
+
+    if (name == '\'')
+    {
+#ifdef FEAT_JUMPLIST
+	if (vi_jumplist != NULL)
+	{
+	    if (vi_jumplist_len < JUMPLISTSIZE)
+		fm = &vi_jumplist[vi_jumplist_len++];
+	}
+	else
+	{
+	    int idx;
+	    int i;
+
+	    /* If we have a timestamp insert it in the right place. */
+	    if (timestamp != 0)
+	    {
+		for (idx = curwin->w_jumplistlen - 1; idx >= 0; --idx)
+		    if (curwin->w_jumplist[idx].time_set < timestamp)
+			break;
+	    }
+	    else if (curwin->w_jumplistlen < JUMPLISTSIZE)
+		/* insert as oldest entry */
+		idx = 0;
+	    else
+		idx = -1;
+
+	    if (idx >= 0)
+	    {
+		if (curwin->w_jumplistlen == JUMPLISTSIZE)
+		{
+		    /* Drop the oldest entry. */
+		    vim_free(curwin->w_jumplist[0].fname);
+		    for (i = 0; i < idx; ++i)
+			curwin->w_jumplist[i] = curwin->w_jumplist[i + 1];
+		}
+		else
+		{
+		    /* Move newer entries forward. */
+		    ++idx;
+		    for (i = curwin->w_jumplistlen; i > idx; --i)
+			curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
+		    ++curwin->w_jumplistidx;
+		    ++curwin->w_jumplistlen;
+		}
+		fm = &curwin->w_jumplist[idx];
+		fm->fmark.mark.lnum = 0;
+		fm->fname = NULL;
+		fm->time_set = 0;
+	    }
+	}
+#endif
+    }
+    else
+    {
+	int idx;
+
+	if (VIM_ISDIGIT(name))
+	{
+	    if (vi_namedfm != NULL)
+		idx = name - '0' + NMARKS;
+	    else
+	    {
+		int i;
+
+		/* Do not use the name from the viminfo file, insert in time
+		 * order. */
+		for (idx = NMARKS; idx < NMARKS + EXTRA_MARKS; ++idx)
+		    if (namedfm[idx].time_set < timestamp)
+			break;
+		if (idx == NMARKS + EXTRA_MARKS)
+		    /* All existing entries are newer. */
+		    return;
+		i = NMARKS + EXTRA_MARKS - 1;
+
+		vim_free(namedfm[i].fname);
+		for ( ; i > idx; --i)
+		    namedfm[i] = namedfm[i - 1];
+		namedfm[idx].fname = NULL;
+	    }
+	}
+	else
+	    idx = name - 'A';
+	if (vi_namedfm != NULL)
+	    fm = &vi_namedfm[idx];
+	else
+	    fm = &namedfm[idx];
+    }
+
+    if (fm != NULL)
+    {
+	if (vi_namedfm != NULL || fm->time_set < timestamp || force)
+	{
+	    fm->fmark.mark.lnum = lnum;
+	    fm->fmark.mark.col = col;
+#ifdef FEAT_VIRTUALEDIT
+	    fm->fmark.mark.coladd = 0;
+#endif
+	    fm->fmark.fnum = 0;
+	    vim_free(fm->fname);
+	    if (vp[4].bv_allocated)
+	    {
+		fm->fname = vp[4].bv_string;
+		vp[4].bv_string = NULL;
+	    }
+	    else
+		fm->fname = vim_strsave(vp[4].bv_string);
+	    fm->time_set = timestamp;
+	}
+    }
+}
+
     void
 write_viminfo_filemarks(FILE *fp)
 {
@@ -1412,17 +1618,30 @@ write_viminfo_filemarks(FILE *fp)
     char_u	*name;
     buf_T	*buf;
     xfmark_T	*fm;
+    int		vi_idx;
+    int		idx;
 
     if (get_viminfo_parameter('f') == 0)
 	return;
 
     fputs(_("\n# File marks:\n"), fp);
 
+    /* Write the filemarks 'A - 'Z */
+    for (i = 0; i < NMARKS; i++)
+    {
+	if (vi_namedfm != NULL && (vi_namedfm[i].time_set > namedfm[i].time_set
+					  || namedfm[i].fmark.mark.lnum == 0))
+	    fm = &vi_namedfm[i];
+	else
+	    fm = &namedfm[i];
+	write_one_filemark(fp, fm, '\'', i + 'A');
+    }
+
     /*
      * Find a mark that is the same file and position as the cursor.
      * That one, or else the last one is deleted.
      * Move '0 to '1, '1 to '2, etc. until the matching one or '9
-     * Set '0 mark to current cursor position.
+     * Set the '0 mark to current cursor position.
      */
     if (curbuf->b_ffname != NULL && !removable(curbuf->b_ffname))
     {
@@ -1442,18 +1661,30 @@ write_viminfo_filemarks(FILE *fp)
 	namedfm[NMARKS].fmark.mark = curwin->w_cursor;
 	namedfm[NMARKS].fmark.fnum = curbuf->b_fnum;
 	namedfm[NMARKS].fname = NULL;
+	namedfm[NMARKS].time_set = vim_time();
     }
 
-    /* Write the filemarks '0 - '9 and 'A - 'Z */
-    for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
-	write_one_filemark(fp, &namedfm[i], '\'',
-				     i < NMARKS ? i + 'A' : i - NMARKS + '0');
+    /* Write the filemarks '0 - '9.  Newest (highest timestamp) first. */
+    vi_idx = NMARKS;
+    idx = NMARKS;
+    for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
+    {
+	if (vi_namedfm != NULL
+		&& vi_namedfm[vi_idx].fmark.mark.lnum != 0
+		&& (vi_namedfm[vi_idx].time_set > namedfm[idx].time_set
+		    || namedfm[idx].fmark.mark.lnum == 0))
+	    fm = &vi_namedfm[vi_idx++];
+	else
+	    fm = &namedfm[idx++];
+	write_one_filemark(fp, fm, '\'', i - NMARKS + '0');
+    }
 
 #ifdef FEAT_JUMPLIST
     /* Write the jumplist with -' */
     fputs(_("\n# Jumplist (newest first):\n"), fp);
     setpcmark();	/* add current cursor position */
     cleanup_jumplist();
+    /* TODO: when vi_jumplist != NULL merge the two lists. */
     for (fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
 					   fm >= &curwin->w_jumplist[0]; --fm)
     {
@@ -1486,6 +1717,14 @@ write_one_filemark(
 	fprintf(fp, "%c%c  %ld  %ld  ", c1, c2, (long)fm->fmark.mark.lnum,
 						    (long)fm->fmark.mark.col);
 	viminfo_writestring(fp, name);
+
+	/* Barline: |{bartype},{name},{lnum},{col},{timestamp},{filename}
+	 * size up to filename: 8 + 3 * 20 */
+	fprintf(fp, "|%d,%d,%ld,%ld,%ld,", BARTYPE_MARK, c2,
+		(long)fm->fmark.mark.lnum, (long)fm->fmark.mark.col,
+		(long)fm->time_set);
+	barline_writestring(fp, name, LSIZE - 70);
+	putc('\n', fp);
     }
 
     if (fm->fmark.fnum != 0)
--- a/src/proto/mark.pro
+++ b/src/proto/mark.pro
@@ -16,6 +16,7 @@ char_u *fm_getname(fmark_T *fmark, int l
 void do_marks(exarg_T *eap);
 void ex_delmarks(exarg_T *eap);
 void ex_jumps(exarg_T *eap);
+void ex_clearjumps(exarg_T *eap);
 void ex_changes(exarg_T *eap);
 void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after);
 void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount);
@@ -24,6 +25,9 @@ void free_jumplist(win_T *wp);
 void set_last_cursor(win_T *win);
 void free_all_marks(void);
 int read_viminfo_filemark(vir_T *virp, int force);
+void prepare_viminfo_marks(void);
+void finish_viminfo_marks(void);
+void handle_viminfo_mark(garray_T *values, int force);
 void write_viminfo_filemarks(FILE *fp);
 int removable(char_u *name);
 int write_viminfo_marks(FILE *fp_out);
--- a/src/structs.h
+++ b/src/structs.h
@@ -84,7 +84,7 @@ typedef struct file_buffer	buf_T;  /* fo
 # ifdef FEAT_XCLIPBOARD
 #  include <X11/Intrinsic.h>
 # endif
-# define guicolor_T long_u		/* avoid error in prototypes and 
+# define guicolor_T long_u		/* avoid error in prototypes and
 					 * make FEAT_TERMGUICOLORS work */
 # define INVALCOLOR ((guicolor_T)0x1ffffff)
 #endif
@@ -112,6 +112,9 @@ typedef struct xfilemark
 {
     fmark_T	fmark;
     char_u	*fname;		/* file name, used when fnum == 0 */
+#ifdef FEAT_VIMINFO
+    time_t	time_set;
+#endif
 } xfmark_T;
 
 /*
--- a/src/testdir/test_viminfo.vim
+++ b/src/testdir/test_viminfo.vim
@@ -17,9 +17,9 @@ function Test_read_and_write()
   let lines = readfile('Xviminfo')
   let done = 0
   for line in lines
-    if line[0] == '|' && line !~ '^|3,'
+    if line[0] == '|' && line !~ '^|[234],'
       if done == 0
-	call assert_equal('|1,3', line)
+	call assert_equal('|1,4', line)
       elseif done == 1
 	call assert_equal('|copied as-is', line)
       elseif done == 2
@@ -217,6 +217,102 @@ func Test_viminfo_registers()
   call delete('Xviminfo')
 endfunc
 
+func Test_viminfo_marks()
+  sp bufa
+  let bufa = bufnr('%')
+  sp bufb
+  let bufb = bufnr('%')
+
+  call test_settime(8)
+  call setpos("'A", [bufa, 1, 1, 0])
+  call test_settime(20)
+  call setpos("'B", [bufb, 9, 1, 0])
+  call setpos("'C", [bufa, 7, 1, 0])
+
+  delmark 0-9
+  call test_settime(25)
+  call setpos("'1", [bufb, 12, 1, 0])
+  call test_settime(35)
+  call setpos("'0", [bufa, 11, 1, 0])
+
+  call test_settime(45)
+  wviminfo Xviminfo
+
+  " Writing viminfo inserts the '0 mark.
+  call assert_equal([bufb, 1, 1, 0], getpos("'0"))
+  call assert_equal([bufa, 11, 1, 0], getpos("'1"))
+  call assert_equal([bufb, 12, 1, 0], getpos("'2"))
+
+  call test_settime(4)
+  call setpos("'A", [bufa, 9, 1, 0])
+  call test_settime(30)
+  call setpos("'B", [bufb, 2, 3, 0])
+  delmark C
+
+  delmark 0-9
+  call test_settime(30)
+  call setpos("'1", [bufb, 22, 1, 0])
+  call test_settime(55)
+  call setpos("'0", [bufa, 21, 1, 0])
+
+  rviminfo Xviminfo
+
+  call assert_equal([bufa, 1, 1, 0], getpos("'A"))
+  call assert_equal([bufb, 2, 3, 0], getpos("'B"))
+  call assert_equal([bufa, 7, 1, 0], getpos("'C"))
+
+  " numbered marks are merged
+  call assert_equal([bufa, 21, 1, 0], getpos("'0"))  " time 55
+  call assert_equal([bufb, 1, 1, 0], getpos("'1"))  " time 45
+  call assert_equal([bufa, 11, 1, 0], getpos("'2")) " time 35
+  call assert_equal([bufb, 22, 1, 0], getpos("'3")) " time 30
+  call assert_equal([bufb, 12, 1, 0], getpos("'4")) " time 25
+
+  call delete('Xviminfo')
+  exe 'bwipe ' . bufa
+  exe 'bwipe ' . bufb
+endfunc
+
+func Test_viminfo_jumplist()
+  split testbuf
+  clearjumps
+  call setline(1, ['time 05', 'time 10', 'time 15', 'time 20', 'time 30', 'last pos'])
+  call cursor(2, 1)
+  call test_settime(10)
+  exe "normal /20\r"
+  call test_settime(20)
+  exe "normal /30\r"
+  call test_settime(30)
+  exe "normal /last pos\r"
+  wviminfo Xviminfo
+
+  clearjumps
+  call cursor(1, 1)
+  call test_settime(5)
+  exe "normal /15\r"
+  call test_settime(15)
+  exe "normal /last pos\r"
+  call test_settime(40)
+  exe "normal ?30\r"
+  rviminfo Xviminfo
+
+  call assert_equal('time 30', getline('.'))
+  exe "normal \<C-O>"
+  call assert_equal('last pos', getline('.'))
+  exe "normal \<C-O>"
+  " duplicate for 'time 30' was removed
+  call assert_equal('time 20', getline('.'))
+  exe "normal \<C-O>"
+  call assert_equal('time 15', getline('.'))
+  exe "normal \<C-O>"
+  call assert_equal('time 10', getline('.'))
+  exe "normal \<C-O>"
+  call assert_equal('time 05', getline('.'))
+
+  bwipe!
+  call delete('Xviminfo')
+endfunc
+
 func Test_viminfo_encoding()
   if !has('multi_byte')
     return
--- 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 */
 /**/
+    1925,
+/**/
     1924,
 /**/
     1923,
--- a/src/vim.h
+++ b/src/vim.h
@@ -1076,10 +1076,12 @@ extern char *(*dyn_libintl_textdomain)(c
 #define BARTYPE_VERSION 1
 #define BARTYPE_HISTORY 2
 #define BARTYPE_REGISTER 3
+#define BARTYPE_MARK 4
 
-#define VIMINFO_VERSION 3
+#define VIMINFO_VERSION 4
 #define VIMINFO_VERSION_WITH_HISTORY 2
 #define VIMINFO_VERSION_WITH_REGISTERS 3
+#define VIMINFO_VERSION_WITH_MARKS 4
 
 typedef enum {
     BVAL_NR,