# HG changeset patch # User Christian Brabandt # Date 1465759806 -7200 # Node ID 78712a2f687acf9a46d2dce2c796d6999ee8cc8f # Parent 0b359cb19c27ec3f0d9c537c05352478fbac771a commit https://github.com/vim/vim/commit/2d35899721da0e9359a9fe1059554f8c4ea7f0c1 Author: Bram Moolenaar 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. diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- 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) diff --git a/src/ex_cmds.h b/src/ex_cmds.h --- 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), diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- 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 diff --git a/src/mark.c b/src/mark.c --- 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) diff --git a/src/proto/mark.pro b/src/proto/mark.pro --- 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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -84,7 +84,7 @@ typedef struct file_buffer buf_T; /* fo # ifdef FEAT_XCLIPBOARD # include # 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; /* diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim --- 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 \" + call assert_equal('last pos', getline('.')) + exe "normal \" + " duplicate for 'time 30' was removed + call assert_equal('time 20', getline('.')) + exe "normal \" + call assert_equal('time 15', getline('.')) + exe "normal \" + call assert_equal('time 10', getline('.')) + exe "normal \" + call assert_equal('time 05', getline('.')) + + bwipe! + call delete('Xviminfo') +endfunc + func Test_viminfo_encoding() if !has('multi_byte') return diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim.h b/src/vim.h --- 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,