# HG changeset patch # User Christian Brabandt # Date 1467561605 -7200 # Node ID 1003973c99df1b5bd2da39a73ab644b8a632ad7c # Parent d72229478e52f345c823e800bb539559cdfdcf08 commit https://github.com/vim/vim/commit/ab9c89b68dcbdb3fbda8c5a50dd90caca64f1bfd Author: Bram Moolenaar Date: Sun Jul 3 17:47:26 2016 +0200 patch 7.4.1988 Problem: When updating viminfo with file marks there is no time order. Solution: Remember the time when a buffer was last used, store marks for the most recently used buffers. diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -1619,6 +1619,9 @@ enter_buffer(buf_T *buf) if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) (void)did_set_spelllang(curwin); #endif +#ifdef FEAT_VIMINFO + curbuf->b_last_used = vim_time(); +#endif redraw_later(NOT_VALID); } diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2148,10 +2148,11 @@ viminfo_filename(char_u *file) static void do_viminfo(FILE *fp_in, FILE *fp_out, int flags) { - int count = 0; int eof = FALSE; vir_T vir; int merge = FALSE; + int do_copy_marks = FALSE; + garray_T buflist; if ((vir.vir_line = alloc(LSIZE)) == NULL) return; @@ -2183,7 +2184,11 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in while (!(eof = viminfo_readline(&vir)) && vir.vir_line[0] != '>') ; - } + + do_copy_marks = (flags & + (VIF_WANT_MARKS | VIF_GET_OLDFILES | VIF_FORCEIT)); + } + if (fp_out != NULL) { /* Write the info: */ @@ -2209,11 +2214,18 @@ do_viminfo(FILE *fp_in, FILE *fp_out, in finish_viminfo_marks(); write_viminfo_bufferlist(fp_out); write_viminfo_barlines(&vir, fp_out); - count = write_viminfo_marks(fp_out); - } - if (fp_in != NULL - && (flags & (VIF_WANT_MARKS | VIF_GET_OLDFILES | VIF_FORCEIT))) - copy_viminfo_marks(&vir, fp_out, count, eof, flags); + + if (do_copy_marks) + ga_init2(&buflist, sizeof(buf_T *), 50); + write_viminfo_marks(fp_out, do_copy_marks ? &buflist : NULL); + } + + if (do_copy_marks) + { + copy_viminfo_marks(&vir, fp_out, &buflist, eof, flags); + if (fp_out != NULL) + ga_clear(&buflist); + } vim_free(vir.vir_line); #ifdef FEAT_MBYTE @@ -4287,6 +4299,10 @@ do_ecmd( msg_scrolled_ign = FALSE; } +#ifdef FEAT_VIMINFO + curbuf->b_last_used = vim_time(); +#endif + if (command != NULL) do_cmdline(command, NULL, NULL, DOCMD_VERBOSE); diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1273,6 +1273,9 @@ main_loop( if (need_maketitle) maketitle(); #endif +#ifdef FEAT_VIMINFO + curbuf->b_last_used = vim_time(); +#endif /* display message after redraw */ if (keep_msg != NULL) { diff --git a/src/mark.c b/src/mark.c --- a/src/mark.c +++ b/src/mark.c @@ -1799,16 +1799,54 @@ removable(char_u *name) return retval; } -static void write_one_mark(FILE *fp_out, int c, pos_T *pos); + static void +write_one_mark(FILE *fp_out, int c, pos_T *pos) +{ + if (pos->lnum != 0) + fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col); +} + + + static void +write_buffer_marks(buf_T *buf, FILE *fp_out) +{ + int i; + pos_T pos; + + home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE); + fprintf(fp_out, "\n> "); + viminfo_writestring(fp_out, IObuff); + + /* Write the last used timestamp as the lnum of the non-existing mark '*'. + * Older Vims will ignore it and/or copy it. */ + pos.lnum = (linenr_T)buf->b_last_used; + pos.col = 0; + write_one_mark(fp_out, '*', &pos); + + write_one_mark(fp_out, '"', &buf->b_last_cursor); + write_one_mark(fp_out, '^', &buf->b_last_insert); + write_one_mark(fp_out, '.', &buf->b_last_change); +#ifdef FEAT_JUMPLIST + /* changelist positions are stored oldest first */ + for (i = 0; i < buf->b_changelistlen; ++i) + { + /* skip duplicates */ + if (i == 0 || !equalpos(buf->b_changelist[i - 1], buf->b_changelist[i])) + write_one_mark(fp_out, '+', &buf->b_changelist[i]); + } +#endif + for (i = 0; i < NMARKS; i++) + write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]); +} /* * Write all the named marks for all buffers. - * Return the number of buffers for which marks have been written. + * When "buflist" is not NULL fill it with the buffers for which marks are to + * be written. */ - int -write_viminfo_marks(FILE *fp_out) + void +write_viminfo_marks(FILE *fp_out, garray_T *buflist) { - int count; buf_T *buf; int is_mark_set; int i; @@ -1826,7 +1864,6 @@ write_viminfo_marks(FILE *fp_out) #endif fputs(_("\n# History of marks within files (newest to oldest):\n"), fp_out); - count = 0; for (buf = firstbuf; buf != NULL; buf = buf->b_next) { /* @@ -1850,42 +1887,35 @@ write_viminfo_marks(FILE *fp_out) if (is_mark_set && buf->b_ffname != NULL && buf->b_ffname[0] != NUL && !removable(buf->b_ffname)) { - home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE); - fprintf(fp_out, "\n> "); - viminfo_writestring(fp_out, IObuff); - write_one_mark(fp_out, '"', &buf->b_last_cursor); - write_one_mark(fp_out, '^', &buf->b_last_insert); - write_one_mark(fp_out, '.', &buf->b_last_change); -#ifdef FEAT_JUMPLIST - /* changelist positions are stored oldest first */ - for (i = 0; i < buf->b_changelistlen; ++i) - { - /* skip duplicates */ - if (i == 0 || !equalpos(buf->b_changelist[i - 1], - buf->b_changelist[i])) - write_one_mark(fp_out, '+', &buf->b_changelist[i]); - } -#endif - for (i = 0; i < NMARKS; i++) - write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]); - count++; + if (buflist == NULL) + write_buffer_marks(buf, fp_out); + else if (ga_grow(buflist, 1) == OK) + ((buf_T **)buflist->ga_data)[buflist->ga_len++] = buf; } } } - - return count; } - static void -write_one_mark(FILE *fp_out, int c, pos_T *pos) +/* + * Compare functions for qsort() below, that compares b_last_used. + */ + static int +#ifdef __BORLANDC__ +_RTLENTRYF +#endif +buf_compare(const void *s1, const void *s2) { - if (pos->lnum != 0) - fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col); + buf_T *buf1 = *(buf_T **)s1; + buf_T *buf2 = *(buf_T **)s2; + + if (buf1->b_last_used == buf2->b_last_used) + return 0; + return buf1->b_last_used > buf2->b_last_used ? -1 : 1; } /* * Handle marks in the viminfo file: - * fp_out != NULL: copy marks for buffers not in buffer list + * fp_out != NULL: copy marks, in time order with buffers in "buflist". * fp_out == NULL && (flags & VIF_WANT_MARKS): read marks for curbuf only * fp_out == NULL && (flags & VIF_GET_OLDFILES | VIF_FORCEIT): fill v:oldfiles */ @@ -1893,7 +1923,7 @@ write_one_mark(FILE *fp_out, int c, pos_ copy_viminfo_marks( vir_T *virp, FILE *fp_out, - int count, + garray_T *buflist, int eof, int flags) { @@ -1910,11 +1940,22 @@ copy_viminfo_marks( #ifdef FEAT_EVAL list_T *list = NULL; #endif + int count = 0; + int buflist_used = 0; + buf_T *buflist_buf = NULL; if ((name_buf = alloc(LSIZE)) == NULL) return; *name_buf = NUL; + if (fp_out != NULL && buflist->ga_len > 0) + { + /* Sort the list of buffers on b_last_used. */ + qsort(buflist->ga_data, (size_t)buflist->ga_len, + sizeof(buf_T *), buf_compare); + buflist_buf = ((buf_T **)buflist->ga_data)[0]; + } + #ifdef FEAT_EVAL if (fp_out == NULL && (flags & (VIF_GET_OLDFILES | VIF_FORCEIT))) { @@ -1986,14 +2027,70 @@ copy_viminfo_marks( } /* - * copy marks if the buffer has not been loaded + * Copy marks if the buffer has not been loaded. */ if (buf == NULL || !buf->b_marks_read) { - copy_marks_out = TRUE; + int did_read_line = FALSE; + + if (buflist_buf != NULL) + { + /* Read the next line. If it has the "*" mark compare the + * time stamps. Write entries from "buflist" that are + * newer. */ + if (!(eof = viminfo_readline(virp)) && line[0] == TAB) + { + did_read_line = TRUE; + if (line[1] == '*') + { + long ltime; + + sscanf((char *)line + 2, "%ld ", <ime); + while ((time_T)ltime < buflist_buf->b_last_used) + { + write_buffer_marks(buflist_buf, fp_out); + if (++count >= num_marked_files) + break; + if (++buflist_used == buflist->ga_len) + { + buflist_buf = NULL; + break; + } + buflist_buf = + ((buf_T **)buflist->ga_data)[buflist_used]; + } + } + else + { + /* No timestamp, must be written by an older Vim. + * Assume all remaining buffers are older then + * ours. */ + while (count < num_marked_files + && buflist_used < buflist->ga_len) + { + buflist_buf = ((buf_T **)buflist->ga_data) + [buflist_used++]; + write_buffer_marks(buflist_buf, fp_out); + ++count; + } + buflist_buf = NULL; + } + + if (count >= num_marked_files) + { + vim_free(str); + break; + } + } + } + fputs("\n> ", fp_out); viminfo_writestring(fp_out, str); + if (did_read_line) + fputs((char *)line, fp_out); + count++; + copy_marks_out = TRUE; } } vim_free(str); @@ -2031,6 +2128,11 @@ copy_viminfo_marks( curbuf->b_changelistlen - 1] = pos; #endif break; + + /* Using the line number for the last-used + * timestamp. */ + case '*': curbuf->b_last_used = pos.lnum; break; + default: if ((i = line[1] - 'a') >= 0 && i < NMARKS) curbuf->b_namedm[i] = pos; } @@ -2039,6 +2141,7 @@ copy_viminfo_marks( else if (copy_marks_out) fputs((char *)line, fp_out); } + if (load_marks) { #ifdef FEAT_JUMPLIST @@ -2053,6 +2156,16 @@ copy_viminfo_marks( break; } } + + if (fp_out != NULL) + /* Write any remaining entries from buflist. */ + while (count < num_marked_files && buflist_used < buflist->ga_len) + { + buflist_buf = ((buf_T **)buflist->ga_data)[buflist_used++]; + write_buffer_marks(buflist_buf, fp_out); + ++count; + } + vim_free(name_buf); } #endif /* FEAT_VIMINFO */ diff --git a/src/proto/mark.pro b/src/proto/mark.pro --- a/src/proto/mark.pro +++ b/src/proto/mark.pro @@ -30,6 +30,6 @@ 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); -void copy_viminfo_marks(vir_T *virp, FILE *fp_out, int count, int eof, int flags); +void write_viminfo_marks(FILE *fp_out, garray_T *buflist); +void copy_viminfo_marks(vir_T *virp, FILE *fp_out, garray_T *buflist, int eof, int flags); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1771,6 +1771,10 @@ struct file_buffer long b_mtime_read; /* last change time when reading */ off_T b_orig_size; /* size of original file in bytes */ int b_orig_mode; /* mode of original file */ +#ifdef FEAT_VIMINFO + time_T b_last_used; /* time when the buffer was last used; used + * for viminfo */ +#endif pos_T b_namedm[NMARKS]; /* current named marks (mark.c) */ 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 @@ -395,3 +395,33 @@ func Test_viminfo_bad_syntax() call delete('Xviminfo') endfunc +func Test_viminfo_file_marks() + silent! bwipe test_viminfo.vim + silent! bwipe Xviminfo + + call test_settime(10) + edit ten + call test_settime(25) + edit again + call test_settime(30) + edit thirty + wviminfo Xviminfo + + call test_settime(20) + edit twenty + call test_settime(35) + edit again + call test_settime(40) + edit fourty + wviminfo Xviminfo + + sp Xviminfo + 1 + for name in ['fourty', 'again', 'thirty', 'twenty', 'ten'] + /^> + call assert_equal(name, substitute(getline('.'), '.*/', '', '')) + endfor + close + + call delete('Xviminfo') +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1988, +/**/ 1987, /**/ 1986,