]> granicus.if.org Git - vim/commitdiff
patch 7.4.1925 v7.4.1925
authorBram Moolenaar <Bram@vim.org>
Sun, 12 Jun 2016 19:20:54 +0000 (21:20 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 12 Jun 2016 19:20:54 +0000 (21:20 +0200)
Problem:    Viminfo does not merge file marks properly.
Solution:   Use a timestamp.  Add the :clearjumps command.

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

index b3b920939d3483934915795be9bf3492b42deab4..519f40c16772286ef1fe570f3fb4073dbf7c3ecc 100644 (file)
@@ -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, int flags)
     {
        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, int flags)
        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 got_encoding, int force, int writing)
                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)
index e2b30f2f69bad3d5b2469f04bd17d4c05ed1ed17..5053e602e6110bea7e9182920d858243f03ff0de 100644 (file)
@@ -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),
index 55c093fb092243849b1ec820d23e2f00c13dc62c..ad4ba7c4e547dca4b5324b899c01d244a8e1d774 100644 (file)
@@ -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
 
index 346594773015ed3288fdcc1659f824bfdaf226f0..34825a65749446efa1eda154f7bb725d05774a63 100644 (file)
@@ -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 force)
            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)
index 9a3e9e9e0d67c82ba0004178573fcea3342f9d04..f24c0355ebd91536badae6c03295ebff6ac33bad 100644 (file)
@@ -16,6 +16,7 @@ char_u *fm_getname(fmark_T *fmark, int lead_len);
 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);
index ac99e9158d7cabeccca659460e6b3876a8f7357e..6075ee2e0a1493844c63a03345d9f449e27564de 100644 (file)
@@ -84,7 +84,7 @@ typedef struct file_buffer    buf_T;  /* forward declaration */
 # 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;
 
 /*
index b68b8ce5863f70220218c98679f3c596ae40892a..19be47dd1684c5db802213709a28659833304401 100644 (file)
@@ -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
index 01b9c4fcbf71493a5cfc15378ddae9e33b9cbefe..9c821d492b1edaab7c85a51c2806d17ce54b6dc4 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1925,
 /**/
     1924,
 /**/
index 863c2e211edf5f406d6195876fd308cb8fa5adc5..7c933d5976016e74075d67c4fbb1dcc827ee1ea7 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -1076,10 +1076,12 @@ extern char *(*dyn_libintl_textdomain)(const char *domainname);
 #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,