To: vim_dev@googlegroups.com Subject: Patch 7.4.1925 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1925 Problem: Viminfo does not merge file marks properly. Solution: Use a timestamp. Add the :clearjumps command. Files: src/mark.c, src/ex_cmds.c, src/ex_docmd.c, src/proto/mark.pro, src/structs.h, src/vim.h, src/ex_cmds.h, src/testdir/test_viminfo.vim *** ../vim-7.4.1924/src/mark.c 2016-01-30 18:51:05.232232015 +0100 --- src/mark.c 2016-06-12 21:17:30.123990602 +0200 *************** *** 106,129 **** return OK; } ! #ifndef EBCDIC ! if (c > 'z') /* some islower() and isupper() cannot handle ! characters above 127 */ ! return FAIL; ! #endif ! if (islower(c)) { i = c - 'a'; curbuf->b_namedm[i] = *pos; return OK; } ! if (isupper(c)) { ! i = c - 'A'; namedfm[i].fmark.mark = *pos; namedfm[i].fmark.fnum = fnum; vim_free(namedfm[i].fname); namedfm[i].fname = NULL; return OK; } return FAIL; --- 106,130 ---- return OK; } ! if (ASCII_ISLOWER(c)) { i = c - 'a'; curbuf->b_namedm[i] = *pos; return OK; } ! if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) { ! 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,189 **** --- 185,193 ---- 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,639 **** --- 638,646 ---- { 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,854 **** --- 856,864 ---- 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,923 **** --- 928,941 ---- 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,1410 **** --- 1418,1616 ---- 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,1428 **** char_u *name; buf_T *buf; xfmark_T *fm; if (get_viminfo_parameter('f') == 0) return; fputs(_("\n# File marks:\n"), fp); /* * 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. */ if (curbuf->b_ffname != NULL && !removable(curbuf->b_ffname)) { --- 1618,1647 ---- 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 the '0 mark to current cursor position. */ if (curbuf->b_ffname != NULL && !removable(curbuf->b_ffname)) { *************** *** 1442,1459 **** namedfm[NMARKS].fmark.mark = curwin->w_cursor; namedfm[NMARKS].fmark.fnum = curbuf->b_fnum; namedfm[NMARKS].fname = NULL; } ! /* 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'); #ifdef FEAT_JUMPLIST /* Write the jumplist with -' */ fputs(_("\n# Jumplist (newest first):\n"), fp); setpcmark(); /* add current cursor position */ cleanup_jumplist(); for (fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1]; fm >= &curwin->w_jumplist[0]; --fm) { --- 1661,1690 ---- 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. 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,1491 **** --- 1717,1730 ---- 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) *** ../vim-7.4.1924/src/ex_cmds.c 2016-06-11 22:20:59.446252026 +0200 --- src/ex_cmds.c 2016-06-12 20:55:58.594111616 +0200 *************** *** 1983,1988 **** --- 1983,1990 ---- */ if (*wp == 'a') { + EMSG2(_("E929: Too many viminfo temp files, like %s!"), + tempname); vim_free(tempname); tempname = NULL; break; *************** *** 2164,2172 **** { if (flags & VIF_WANT_INFO) { - /* Registers are read and newer ones are used when writing. */ if (fp_out != NULL) prepare_viminfo_registers(); eof = read_viminfo_up_to_marks(&vir, flags & VIF_FORCEIT, fp_out != NULL); --- 2166,2178 ---- { if (flags & VIF_WANT_INFO) { 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,2205 **** --- 2206,2212 ---- 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,2783 **** --- 2785,2795 ---- 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) *** ../vim-7.4.1924/src/ex_docmd.c 2016-06-09 22:52:56.207926413 +0200 --- src/ex_docmd.c 2016-06-12 20:33:21.728710974 +0200 *************** *** 474,479 **** --- 474,480 ---- #endif #ifndef FEAT_JUMPLIST # define ex_jumps ex_ni + # define ex_clearjumps ex_ni # define ex_changes ex_ni #endif *** ../vim-7.4.1924/src/proto/mark.pro 2016-01-19 13:21:55.841334333 +0100 --- src/proto/mark.pro 2016-06-12 20:40:12.128282992 +0200 *************** *** 16,21 **** --- 16,22 ---- 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,29 **** --- 25,33 ---- 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); *** ../vim-7.4.1924/src/structs.h 2016-06-06 21:07:48.383578685 +0200 --- src/structs.h 2016-06-12 15:49:28.984338910 +0200 *************** *** 84,90 **** # ifdef FEAT_XCLIPBOARD # include # endif ! # define guicolor_T long_u /* avoid error in prototypes and * make FEAT_TERMGUICOLORS work */ # define INVALCOLOR ((guicolor_T)0x1ffffff) #endif --- 84,90 ---- # ifdef FEAT_XCLIPBOARD # include # endif ! # define guicolor_T long_u /* avoid error in prototypes and * make FEAT_TERMGUICOLORS work */ # define INVALCOLOR ((guicolor_T)0x1ffffff) #endif *************** *** 112,117 **** --- 112,120 ---- { fmark_T fmark; char_u *fname; /* file name, used when fnum == 0 */ + #ifdef FEAT_VIMINFO + time_t time_set; + #endif } xfmark_T; /* *** ../vim-7.4.1924/src/vim.h 2016-06-11 21:04:34.927761279 +0200 --- src/vim.h 2016-06-12 15:16:02.318118048 +0200 *************** *** 1076,1085 **** #define BARTYPE_VERSION 1 #define BARTYPE_HISTORY 2 #define BARTYPE_REGISTER 3 ! #define VIMINFO_VERSION 3 #define VIMINFO_VERSION_WITH_HISTORY 2 #define VIMINFO_VERSION_WITH_REGISTERS 3 typedef enum { BVAL_NR, --- 1076,1087 ---- #define BARTYPE_VERSION 1 #define BARTYPE_HISTORY 2 #define BARTYPE_REGISTER 3 + #define BARTYPE_MARK 4 ! #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, *** ../vim-7.4.1924/src/ex_cmds.h 2016-04-14 18:42:37.330439451 +0200 --- src/ex_cmds.h 2016-06-12 20:32:01.065581129 +0200 *************** *** 316,321 **** --- 316,324 ---- 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), *** ../vim-7.4.1924/src/testdir/test_viminfo.vim 2016-06-11 21:14:14.121484821 +0200 --- src/testdir/test_viminfo.vim 2016-06-12 21:10:08.024815341 +0200 *************** *** 17,25 **** let lines = readfile('Xviminfo') let done = 0 for line in lines ! if line[0] == '|' && line !~ '^|3,' if done == 0 ! call assert_equal('|1,3', line) elseif done == 1 call assert_equal('|copied as-is', line) elseif done == 2 --- 17,25 ---- let lines = readfile('Xviminfo') let done = 0 for line in lines ! if line[0] == '|' && line !~ '^|[234],' if done == 0 ! call assert_equal('|1,4', line) elseif done == 1 call assert_equal('|copied as-is', line) elseif done == 2 *************** *** 217,222 **** --- 217,318 ---- 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 *** ../vim-7.4.1924/src/version.c 2016-06-12 21:18:38.207260690 +0200 --- src/version.c 2016-06-12 21:19:57.294412705 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 1925, /**/ -- Warning label on a superhero Halloween costume: "Caution: Cape does not enable user to fly." /// Bram Moolenaar -- Bram@Moolenaar.net -- 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 ///