To: vim_dev@googlegroups.com Subject: Patch 8.1.2225 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2225 Problem: The "last used" info of a buffer is under used. Solution: Add "lastused" to getbufinfo(). List buffers sorted by last-used field. (Andi Massimino, closes #4722) Files: runtime/doc/eval.txt, runtime/doc/options.txt, runtime/doc/windows.txt, src/buffer.c, src/evalbuffer.c, src/ex_getln.c, src/misc1.c, src/option.c, src/option.h, src/proto/misc1.pro, src/proto/viminfo.pro, src/testdir/test_bufwintabinfo.vim, src/testdir/test_cmdline.vim, src/testdir/test_excmd.vim, src/undo.c, src/vim.h, src/viminfo.c *** ../vim-8.1.2224/runtime/doc/eval.txt 2019-09-26 23:08:10.501926883 +0200 --- runtime/doc/eval.txt 2019-10-27 04:59:15.196935852 +0100 *************** *** 4772,4777 **** --- 4778,4787 ---- changed TRUE if the buffer is modified. changedtick number of changes made to the buffer. hidden TRUE if the buffer is hidden. + lastused timestamp in seconds, like + |localtime()|, when the buffer was + last used. + {only with the |+viminfo| feature} listed TRUE if the buffer is listed. lnum current line number in buffer. loaded TRUE if the buffer is loaded. *** ../vim-8.1.2224/runtime/doc/options.txt 2019-10-20 18:17:08.363431719 +0200 --- runtime/doc/options.txt 2019-10-27 04:59:47.304750399 +0100 *************** *** 8689,8694 **** --- 8689,8696 ---- complete first match. "list:longest" When more than one match, list all matches and complete till longest common string. + "list:lastused" When more than one buffer matches, sort buffers + by time last used (other than the current buffer). When there is only a single match, it is fully completed in all cases. Examples: > *** ../vim-8.1.2224/runtime/doc/windows.txt 2019-08-04 20:42:39.815198862 +0200 --- runtime/doc/windows.txt 2019-10-27 04:43:57.249937909 +0100 *************** *** 1088,1093 **** --- 1090,1096 ---- R terminal buffers with a running job F terminal buffers with a finished job ? terminal buffers without a job: `:terminal NONE` + t show time last used and sort buffers Combining flags means they are "and"ed together, e.g.: h+ hidden buffers which are modified a+ active buffers which are modified *** ../vim-8.1.2224/src/buffer.c 2019-10-01 17:01:56.342282818 +0200 --- src/buffer.c 2019-10-27 05:04:22.887244105 +0100 *************** *** 2601,2606 **** --- 2601,2613 ---- return match; } + #ifdef FEAT_VIMINFO + typedef struct { + buf_T *buf; + char_u *match; + } bufmatch_T; + #endif + /* * Find all buffer names that match. * For command line expansion of ":buf" and ":sbuf". *************** *** 2619,2624 **** --- 2626,2634 ---- char_u *p; int attempt; char_u *patc; + #ifdef FEAT_VIMINFO + bufmatch_T *matches = NULL; + #endif *num_file = 0; /* return values in case of FAIL */ *file = NULL; *************** *** 2675,2681 **** p = home_replace_save(buf, p); else p = vim_strsave(p); ! (*file)[count++] = p; } } } --- 2685,2700 ---- p = home_replace_save(buf, p); else p = vim_strsave(p); ! #ifdef FEAT_VIMINFO ! if (matches != NULL) ! { ! matches[count].buf = buf; ! matches[count].match = p; ! count++; ! } ! else ! #endif ! (*file)[count++] = p; } } } *************** *** 2691,2696 **** --- 2710,2719 ---- vim_free(patc); return FAIL; } + #ifdef FEAT_VIMINFO + if (options & WILD_BUFLASTUSED) + matches = ALLOC_MULT(bufmatch_T, count); + #endif } } vim_regfree(regmatch.regprog); *************** *** 2701,2706 **** --- 2724,2751 ---- if (patc != pat) vim_free(patc); + #ifdef FEAT_VIMINFO + if (matches != NULL) + { + int i; + if (count > 1) + qsort(matches, count, sizeof(bufmatch_T), buf_compare); + // if the current buffer is first in the list, place it at the end + if (matches[0].buf == curbuf) + { + for (i = 1; i < count; i++) + (*file)[i-1] = matches[i].match; + (*file)[count-1] = matches[0].match; + } + else + { + for (i = 0; i < count; i++) + (*file)[i] = matches[i].match; + } + vim_free(matches); + } + #endif + *num_file = count; return (count == 0 ? FAIL : OK); } *************** *** 3016,3022 **** void buflist_list(exarg_T *eap) { ! buf_T *buf; int len; int i; int ro_char; --- 3061,3067 ---- void buflist_list(exarg_T *eap) { ! buf_T *buf = firstbuf; int len; int i; int ro_char; *************** *** 3026,3032 **** --- 3071,3102 ---- int job_none_open; #endif + #ifdef FEAT_VIMINFO + garray_T buflist; + buf_T **buflist_data = NULL, **p; + + if (vim_strchr(eap->arg, 't')) + { + ga_init2(&buflist, sizeof(buf_T *), 50); + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + { + if (ga_grow(&buflist, 1) == OK) + ((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf; + } + + qsort(buflist.ga_data, (size_t)buflist.ga_len, + sizeof(buf_T *), buf_compare); + + p = buflist_data = (buf_T **)buflist.ga_data; + buf = *p; + } + + for (; buf != NULL && !got_int; buf = buflist_data + ? (++p < buflist_data + buflist.ga_len ? *p : NULL) + : buf->b_next) + #else for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) + #endif { #ifdef FEAT_TERMINAL job_running = term_job_running(buf->b_term); *************** *** 3100,3112 **** do IObuff[len++] = ' '; while (--i > 0 && len < IOSIZE - 18); ! vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), ! _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum : (long)buflist_findlnum(buf)); msg_outtrans(IObuff); out_flush(); /* output one line at a time */ ui_breakcheck(); } } /* --- 3170,3192 ---- do IObuff[len++] = ' '; while (--i > 0 && len < IOSIZE - 18); ! #ifdef FEAT_VIMINFO ! if (vim_strchr(eap->arg, 't') && buf->b_last_used) ! add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used); ! else ! #endif ! vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), ! _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum : (long)buflist_findlnum(buf)); msg_outtrans(IObuff); out_flush(); /* output one line at a time */ ui_breakcheck(); } + + #ifdef FEAT_VIMINFO + if (buflist_data) + ga_clear(&buflist); + #endif } /* *** ../vim-8.1.2224/src/evalbuffer.c 2019-09-07 15:45:09.977228927 +0200 --- src/evalbuffer.c 2019-10-27 04:43:57.253937893 +0100 *************** *** 595,600 **** --- 595,604 ---- } #endif + #ifdef FEAT_VIMINFO + dict_add_number(dict, "lastused", buf->b_last_used); + #endif + return dict; } *** ../vim-8.1.2224/src/ex_getln.c 2019-10-26 20:45:11.450026374 +0200 --- src/ex_getln.c 2019-10-27 04:43:57.253937893 +0100 *************** *** 1407,1412 **** --- 1407,1415 ---- */ if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm) { + int options = WILD_NO_BEEP; + if (wim_flags[wim_index] & WIM_BUFLASTUSED) + options |= WILD_BUFLASTUSED; if (xpc.xp_numfiles > 0) /* typed p_wc at least twice */ { /* if 'wildmode' contains "list" may still need to list */ *************** *** 1419,1428 **** did_wild_list = TRUE; } if (wim_flags[wim_index] & WIM_LONGEST) ! res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, firstc != '@'); else if (wim_flags[wim_index] & WIM_FULL) ! res = nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, firstc != '@'); else res = OK; /* don't insert 'wildchar' now */ --- 1422,1431 ---- did_wild_list = TRUE; } if (wim_flags[wim_index] & WIM_LONGEST) ! res = nextwild(&xpc, WILD_LONGEST, options, firstc != '@'); else if (wim_flags[wim_index] & WIM_FULL) ! res = nextwild(&xpc, WILD_NEXT, options, firstc != '@'); else res = OK; /* don't insert 'wildchar' now */ *************** *** 1434,1443 **** /* if 'wildmode' first contains "longest", get longest * common part */ if (wim_flags[0] & WIM_LONGEST) ! res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, firstc != '@'); else ! res = nextwild(&xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP, firstc != '@'); /* if interrupted while completing, behave like it failed */ --- 1437,1446 ---- /* if 'wildmode' first contains "longest", get longest * common part */ if (wim_flags[0] & WIM_LONGEST) ! res = nextwild(&xpc, WILD_LONGEST, options, firstc != '@'); else ! res = nextwild(&xpc, WILD_EXPAND_KEEP, options, firstc != '@'); /* if interrupted while completing, behave like it failed */ *************** *** 1488,1497 **** redrawcmd(); did_wild_list = TRUE; if (wim_flags[wim_index] & WIM_LONGEST) ! nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, firstc != '@'); else if (wim_flags[wim_index] & WIM_FULL) ! nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, firstc != '@'); } else --- 1491,1500 ---- redrawcmd(); did_wild_list = TRUE; if (wim_flags[wim_index] & WIM_LONGEST) ! nextwild(&xpc, WILD_LONGEST, options, firstc != '@'); else if (wim_flags[wim_index] & WIM_FULL) ! nextwild(&xpc, WILD_NEXT, options, firstc != '@'); } else *** ../vim-8.1.2224/src/misc1.c 2019-10-17 22:58:59.066497012 +0200 --- src/misc1.c 2019-10-27 05:07:14.170365034 +0100 *************** *** 2576,2578 **** --- 2576,2609 ---- ; return path_is_url(p); } + + /* + * Put timestamp "tt" in "buf[buflen]" in a nice format. + */ + void + add_time(char_u *buf, size_t buflen, time_t tt) + { + #ifdef HAVE_STRFTIME + struct tm tmval; + struct tm *curtime; + + if (vim_time() - tt >= 100) + { + curtime = vim_localtime(&tt, &tmval); + if (vim_time() - tt < (60L * 60L * 12L)) + /* within 12 hours */ + (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime); + else + /* longer ago */ + (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime); + } + else + #endif + { + long seconds = (long)(vim_time() - tt); + + vim_snprintf((char *)buf, buflen, + NGETTEXT("%ld second ago", "%ld seconds ago", seconds), + seconds); + } + } *** ../vim-8.1.2224/src/option.c 2019-10-19 20:57:24.956093733 +0200 --- src/option.c 2019-10-27 04:43:57.253937893 +0100 *************** *** 6987,6992 **** --- 6987,6994 ---- new_wim_flags[idx] |= WIM_FULL; else if (i == 4 && STRNCMP(p, "list", 4) == 0) new_wim_flags[idx] |= WIM_LIST; + else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) + new_wim_flags[idx] |= WIM_BUFLASTUSED; else return FAIL; p += i; *** ../vim-8.1.2224/src/option.h 2019-10-17 22:58:59.070496999 +0200 --- src/option.h 2019-10-27 04:43:57.253937893 +0100 *************** *** 338,343 **** --- 338,344 ---- #define WIM_FULL 0x01 #define WIM_LONGEST 0x02 #define WIM_LIST 0x04 + #define WIM_BUFLASTUSED 0x08 // arguments for can_bs() #define BS_INDENT 'i' // "Indent" *** ../vim-8.1.2224/src/proto/misc1.pro 2019-10-09 22:52:49.000043746 +0200 --- src/proto/misc1.pro 2019-10-27 04:43:57.253937893 +0100 *************** *** 46,49 **** --- 46,50 ---- char_u *get_isolated_shell_name(void); int path_is_url(char_u *p); int path_with_url(char_u *fname); + void add_time(char_u *buf, size_t buflen, time_t tt); /* vim: set ft=c : */ *** ../vim-8.1.2224/src/proto/viminfo.pro 2019-07-23 22:15:21.307518880 +0200 --- src/proto/viminfo.pro 2019-10-27 04:43:57.253937893 +0100 *************** *** 3,7 **** --- 3,8 ---- void check_marks_read(void); int read_viminfo(char_u *file, int flags); void write_viminfo(char_u *file, int forceit); + int buf_compare(const void *s1, const void *s2); void ex_viminfo(exarg_T *eap); /* vim: set ft=c : */ *** ../vim-8.1.2224/src/testdir/test_bufwintabinfo.vim 2019-09-06 22:45:47.574271573 +0200 --- src/testdir/test_bufwintabinfo.vim 2019-10-27 04:43:57.253937893 +0100 *************** *** 139,141 **** --- 139,153 ---- set foldlevel=0 endif endfunc + + function Test_getbufinfo_lastused() + call test_settime(1234567) + edit Xtestfile1 + enew + call test_settime(7654321) + edit Xtestfile2 + enew + call assert_equal(getbufinfo('Xtestfile1')[0].lastused, 1234567) + call assert_equal(getbufinfo('Xtestfile2')[0].lastused, 7654321) + call test_settime(0) + endfunc *** ../vim-8.1.2224/src/testdir/test_cmdline.vim 2019-10-12 20:17:24.605773312 +0200 --- src/testdir/test_cmdline.vim 2019-10-27 04:43:57.253937893 +0100 *************** *** 767,769 **** --- 767,814 ---- endtry bw! endfunc + + func Test_buffers_lastused() + " check that buffers are sorted by time when wildmode has lastused + call test_settime(1550020000) " middle + edit bufa + enew + call test_settime(1550030000) " newest + edit bufb + enew + call test_settime(1550010000) " oldest + edit bufc + enew + call test_settime(0) + enew + + call assert_equal(['bufa', 'bufb', 'bufc'], + \ getcompletion('', 'buffer')) + + let save_wildmode = &wildmode + set wildmode=full:lastused + + let cap = "\=execute('let X=getcmdline()')\" + call feedkeys(":b \" .. cap .. "\", 'xt') + call assert_equal('b bufb', X) + call feedkeys(":b \\" .. cap .. "\", 'xt') + call assert_equal('b bufa', X) + call feedkeys(":b \\\" .. cap .. "\", 'xt') + call assert_equal('b bufc', X) + enew + + edit other + call feedkeys(":b \" .. cap .. "\", 'xt') + call assert_equal('b bufb', X) + call feedkeys(":b \\" .. cap .. "\", 'xt') + call assert_equal('b bufa', X) + call feedkeys(":b \\\" .. cap .. "\", 'xt') + call assert_equal('b bufc', X) + enew + + let &wildmode = save_wildmode + + bwipeout bufa + bwipeout bufb + bwipeout bufc + endfunc *** ../vim-8.1.2224/src/testdir/test_excmd.vim 2019-08-06 21:29:25.742634550 +0200 --- src/testdir/test_excmd.vim 2019-10-27 04:43:57.253937893 +0100 *************** *** 19,21 **** --- 19,46 ---- normal vv call assert_fails(":'<,'>echo 1", 'E481:') endfunc + + func Test_buffers_lastused() + call test_settime(localtime() - 2000) " middle + edit bufa + enew + call test_settime(localtime() - 10) " newest + edit bufb + enew + call test_settime(1550010000) " oldest + edit bufc + enew + call test_settime(0) + enew + + let ls = split(execute('buffers t', 'silent!'), '\n') + let bufs = ls->map({i,v->split(v, '"\s*')[1:2]}) + call assert_equal(['bufb', 'bufa', 'bufc'], bufs[1:]->map({i,v->v[0]})) + call assert_match('1[0-3] seconds ago', bufs[1][1]) + call assert_match('\d\d:\d\d:\d\d', bufs[2][1]) + call assert_match('2019/02/1\d \d\d:\d\d:00', bufs[3][1]) + + bwipeout bufa + bwipeout bufb + bwipeout bufc + endfunc *** ../vim-8.1.2224/src/undo.c 2019-09-21 23:09:00.979830687 +0200 --- src/undo.c 2019-10-27 04:43:57.253937893 +0100 *************** *** 106,112 **** static void u_doit(int count); static void u_undoredo(int undo); static void u_undo_end(int did_undo, int absolute); - static void u_add_time(char_u *buf, size_t buflen, time_t tt); static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp); static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp); static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp); --- 106,111 ---- *************** *** 2973,2979 **** if (uhp == NULL) *msgbuf = NUL; else ! u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time); #ifdef FEAT_CONCEAL { --- 2972,2978 ---- if (uhp == NULL) *msgbuf = NUL; else ! add_time(msgbuf, sizeof(msgbuf), uhp->uh_time); #ifdef FEAT_CONCEAL { *************** *** 3050,3056 **** break; vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); ! u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { --- 3049,3055 ---- break; vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); ! add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { *************** *** 3124,3160 **** } } - /* - * Put the timestamp of an undo header in "buf[buflen]" in a nice format. - */ - static void - u_add_time(char_u *buf, size_t buflen, time_t tt) - { - #ifdef HAVE_STRFTIME - struct tm tmval; - struct tm *curtime; - - if (vim_time() - tt >= 100) - { - curtime = vim_localtime(&tt, &tmval); - if (vim_time() - tt < (60L * 60L * 12L)) - /* within 12 hours */ - (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime); - else - /* longer ago */ - (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime); - } - else - #endif - { - long seconds = (long)(vim_time() - tt); - - vim_snprintf((char *)buf, buflen, - NGETTEXT("%ld second ago", "%ld seconds ago", seconds), - seconds); - } - } - /* * ":undojoin": continue adding to the last entry list */ --- 3123,3128 ---- *** ../vim-8.1.2224/src/vim.h 2019-10-26 16:21:34.511468348 +0200 --- src/vim.h 2019-10-27 04:45:59.721441102 +0100 *************** *** 811,816 **** --- 811,817 ---- #define WILD_ALLLINKS 0x200 #define WILD_IGNORE_COMPLETESLASH 0x400 #define WILD_NOERROR 0x800 // sets EW_NOERROR + #define WILD_BUFLASTUSED 0x1000 // Flags for expand_wildcards() #define EW_DIR 0x01 // include directory names *** ../vim-8.1.2224/src/viminfo.c 2019-10-09 22:01:20.599438001 +0200 --- src/viminfo.c 2019-10-27 04:43:57.257937875 +0100 *************** *** 2152,2158 **** /* * Compare functions for qsort() below, that compares b_last_used. */ ! static int buf_compare(const void *s1, const void *s2) { buf_T *buf1 = *(buf_T **)s1; --- 2152,2158 ---- /* * Compare functions for qsort() below, that compares b_last_used. */ ! int buf_compare(const void *s1, const void *s2) { buf_T *buf1 = *(buf_T **)s1; *** ../vim-8.1.2224/src/version.c 2019-10-26 21:33:11.334329088 +0200 --- src/version.c 2019-10-27 04:53:51.290982776 +0100 *************** *** 743,744 **** --- 743,746 ---- { /* Add new patch number below this line */ + /**/ + 2225, /**/ -- Bad fashion can discourage normal people from interacting with the engineer and talking about the cute things their children do. (Scott Adams - The Dilbert principle) /// 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 ///