1
0
forked from aniani/vim

patch 8.1.2225: the "last used" info of a buffer is under used

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)
This commit is contained in:
Bram Moolenaar
2019-10-27 05:12:45 +01:00
parent dfded98f87
commit 52410575be
18 changed files with 228 additions and 45 deletions

View File

@@ -4778,6 +4778,10 @@ getbufinfo([{dict}])
changed TRUE if the buffer is modified. changed TRUE if the buffer is modified.
changedtick number of changes made to the buffer. changedtick number of changes made to the buffer.
hidden TRUE if the buffer is hidden. 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. listed TRUE if the buffer is listed.
lnum current line number in buffer. lnum current line number in buffer.
loaded TRUE if the buffer is loaded. loaded TRUE if the buffer is loaded.

View File

@@ -8689,6 +8689,8 @@ A jump table for the options with a short description can be found at |Q_op|.
complete first match. complete first match.
"list:longest" When more than one match, list all matches and "list:longest" When more than one match, list all matches and
complete till longest common string. 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. When there is only a single match, it is fully completed in all cases.
Examples: > Examples: >

View File

@@ -1090,6 +1090,7 @@ list of buffers. |unlisted-buffer|
R terminal buffers with a running job R terminal buffers with a running job
F terminal buffers with a finished job F terminal buffers with a finished job
? terminal buffers without a job: `:terminal NONE` ? 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.: Combining flags means they are "and"ed together, e.g.:
h+ hidden buffers which are modified h+ hidden buffers which are modified
a+ active buffers which are modified a+ active buffers which are modified

View File

@@ -2601,6 +2601,13 @@ buflist_findpat(
return match; return match;
} }
#ifdef FEAT_VIMINFO
typedef struct {
buf_T *buf;
char_u *match;
} bufmatch_T;
#endif
/* /*
* Find all buffer names that match. * Find all buffer names that match.
* For command line expansion of ":buf" and ":sbuf". * For command line expansion of ":buf" and ":sbuf".
@@ -2619,6 +2626,9 @@ ExpandBufnames(
char_u *p; char_u *p;
int attempt; int attempt;
char_u *patc; char_u *patc;
#ifdef FEAT_VIMINFO
bufmatch_T *matches = NULL;
#endif
*num_file = 0; /* return values in case of FAIL */ *num_file = 0; /* return values in case of FAIL */
*file = NULL; *file = NULL;
@@ -2675,7 +2685,16 @@ ExpandBufnames(
p = home_replace_save(buf, p); p = home_replace_save(buf, p);
else else
p = vim_strsave(p); p = vim_strsave(p);
(*file)[count++] = p; #ifdef FEAT_VIMINFO
if (matches != NULL)
{
matches[count].buf = buf;
matches[count].match = p;
count++;
}
else
#endif
(*file)[count++] = p;
} }
} }
} }
@@ -2691,6 +2710,10 @@ ExpandBufnames(
vim_free(patc); vim_free(patc);
return FAIL; return FAIL;
} }
#ifdef FEAT_VIMINFO
if (options & WILD_BUFLASTUSED)
matches = ALLOC_MULT(bufmatch_T, count);
#endif
} }
} }
vim_regfree(regmatch.regprog); vim_regfree(regmatch.regprog);
@@ -2701,6 +2724,28 @@ ExpandBufnames(
if (patc != pat) if (patc != pat)
vim_free(patc); 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; *num_file = count;
return (count == 0 ? FAIL : OK); return (count == 0 ? FAIL : OK);
} }
@@ -3016,7 +3061,7 @@ buflist_findlnum(buf_T *buf)
void void
buflist_list(exarg_T *eap) buflist_list(exarg_T *eap)
{ {
buf_T *buf; buf_T *buf = firstbuf;
int len; int len;
int i; int i;
int ro_char; int ro_char;
@@ -3026,7 +3071,32 @@ buflist_list(exarg_T *eap)
int job_none_open; int job_none_open;
#endif #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) for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
#endif
{ {
#ifdef FEAT_TERMINAL #ifdef FEAT_TERMINAL
job_running = term_job_running(buf->b_term); job_running = term_job_running(buf->b_term);
@@ -3100,13 +3170,23 @@ buflist_list(exarg_T *eap)
do do
IObuff[len++] = ' '; IObuff[len++] = ' ';
while (--i > 0 && len < IOSIZE - 18); while (--i > 0 && len < IOSIZE - 18);
vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), #ifdef FEAT_VIMINFO
_("line %ld"), buf == curbuf ? curwin->w_cursor.lnum 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)); : (long)buflist_findlnum(buf));
msg_outtrans(IObuff); msg_outtrans(IObuff);
out_flush(); /* output one line at a time */ out_flush(); /* output one line at a time */
ui_breakcheck(); ui_breakcheck();
} }
#ifdef FEAT_VIMINFO
if (buflist_data)
ga_clear(&buflist);
#endif
} }
/* /*

View File

@@ -595,6 +595,10 @@ get_buffer_info(buf_T *buf)
} }
#endif #endif
#ifdef FEAT_VIMINFO
dict_add_number(dict, "lastused", buf->b_last_used);
#endif
return dict; return dict;
} }

View File

@@ -1407,6 +1407,9 @@ getcmdline_int(
*/ */
if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm) 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 (xpc.xp_numfiles > 0) /* typed p_wc at least twice */
{ {
/* if 'wildmode' contains "list" may still need to list */ /* if 'wildmode' contains "list" may still need to list */
@@ -1419,10 +1422,10 @@ getcmdline_int(
did_wild_list = TRUE; did_wild_list = TRUE;
} }
if (wim_flags[wim_index] & WIM_LONGEST) if (wim_flags[wim_index] & WIM_LONGEST)
res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, res = nextwild(&xpc, WILD_LONGEST, options,
firstc != '@'); firstc != '@');
else if (wim_flags[wim_index] & WIM_FULL) else if (wim_flags[wim_index] & WIM_FULL)
res = nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, res = nextwild(&xpc, WILD_NEXT, options,
firstc != '@'); firstc != '@');
else else
res = OK; /* don't insert 'wildchar' now */ res = OK; /* don't insert 'wildchar' now */
@@ -1434,10 +1437,10 @@ getcmdline_int(
/* if 'wildmode' first contains "longest", get longest /* if 'wildmode' first contains "longest", get longest
* common part */ * common part */
if (wim_flags[0] & WIM_LONGEST) if (wim_flags[0] & WIM_LONGEST)
res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, res = nextwild(&xpc, WILD_LONGEST, options,
firstc != '@'); firstc != '@');
else else
res = nextwild(&xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP, res = nextwild(&xpc, WILD_EXPAND_KEEP, options,
firstc != '@'); firstc != '@');
/* if interrupted while completing, behave like it failed */ /* if interrupted while completing, behave like it failed */
@@ -1488,10 +1491,10 @@ getcmdline_int(
redrawcmd(); redrawcmd();
did_wild_list = TRUE; did_wild_list = TRUE;
if (wim_flags[wim_index] & WIM_LONGEST) if (wim_flags[wim_index] & WIM_LONGEST)
nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, nextwild(&xpc, WILD_LONGEST, options,
firstc != '@'); firstc != '@');
else if (wim_flags[wim_index] & WIM_FULL) else if (wim_flags[wim_index] & WIM_FULL)
nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, nextwild(&xpc, WILD_NEXT, options,
firstc != '@'); firstc != '@');
} }
else else

View File

@@ -2576,3 +2576,34 @@ path_with_url(char_u *fname)
; ;
return path_is_url(p); 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);
}
}

View File

@@ -6987,6 +6987,8 @@ check_opt_wim(void)
new_wim_flags[idx] |= WIM_FULL; new_wim_flags[idx] |= WIM_FULL;
else if (i == 4 && STRNCMP(p, "list", 4) == 0) else if (i == 4 && STRNCMP(p, "list", 4) == 0)
new_wim_flags[idx] |= WIM_LIST; new_wim_flags[idx] |= WIM_LIST;
else if (i == 8 && STRNCMP(p, "lastused", 8) == 0)
new_wim_flags[idx] |= WIM_BUFLASTUSED;
else else
return FAIL; return FAIL;
p += i; p += i;

View File

@@ -338,6 +338,7 @@
#define WIM_FULL 0x01 #define WIM_FULL 0x01
#define WIM_LONGEST 0x02 #define WIM_LONGEST 0x02
#define WIM_LIST 0x04 #define WIM_LIST 0x04
#define WIM_BUFLASTUSED 0x08
// arguments for can_bs() // arguments for can_bs()
#define BS_INDENT 'i' // "Indent" #define BS_INDENT 'i' // "Indent"

View File

@@ -46,4 +46,5 @@ int goto_im(void);
char_u *get_isolated_shell_name(void); char_u *get_isolated_shell_name(void);
int path_is_url(char_u *p); int path_is_url(char_u *p);
int path_with_url(char_u *fname); int path_with_url(char_u *fname);
void add_time(char_u *buf, size_t buflen, time_t tt);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@@ -3,5 +3,6 @@ int get_viminfo_parameter(int type);
void check_marks_read(void); void check_marks_read(void);
int read_viminfo(char_u *file, int flags); int read_viminfo(char_u *file, int flags);
void write_viminfo(char_u *file, int forceit); void write_viminfo(char_u *file, int forceit);
int buf_compare(const void *s1, const void *s2);
void ex_viminfo(exarg_T *eap); void ex_viminfo(exarg_T *eap);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@@ -139,3 +139,15 @@ function Test_get_win_options()
set foldlevel=0 set foldlevel=0
endif endif
endfunc 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

View File

@@ -767,3 +767,48 @@ func Test_cmdwin_bug()
endtry endtry
bw! bw!
endfunc 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 = "\<c-r>=execute('let X=getcmdline()')\<cr>"
call feedkeys(":b \<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufb', X)
call feedkeys(":b \<tab>\<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufa', X)
call feedkeys(":b \<tab>\<tab>\<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufc', X)
enew
edit other
call feedkeys(":b \<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufb', X)
call feedkeys(":b \<tab>\<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufa', X)
call feedkeys(":b \<tab>\<tab>\<tab>" .. cap .. "\<esc>", 'xt')
call assert_equal('b bufc', X)
enew
let &wildmode = save_wildmode
bwipeout bufa
bwipeout bufb
bwipeout bufc
endfunc

View File

@@ -19,3 +19,28 @@ func Test_range_error()
normal vv normal vv
call assert_fails(":'<,'>echo 1", 'E481:') call assert_fails(":'<,'>echo 1", 'E481:')
endfunc 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

View File

@@ -106,7 +106,6 @@ static void u_getbot(void);
static void u_doit(int count); static void u_doit(int count);
static void u_undoredo(int undo); static void u_undoredo(int undo);
static void u_undo_end(int did_undo, int absolute); 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_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_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); static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
@@ -2973,7 +2972,7 @@ u_undo_end(
if (uhp == NULL) if (uhp == NULL)
*msgbuf = NUL; *msgbuf = NUL;
else else
u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time); add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
#ifdef FEAT_CONCEAL #ifdef FEAT_CONCEAL
{ {
@@ -3050,7 +3049,7 @@ ex_undolist(exarg_T *eap UNUSED)
break; break;
vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ", vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
uhp->uh_seq, changes); uhp->uh_seq, changes);
u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
uhp->uh_time); uhp->uh_time);
if (uhp->uh_save_nr > 0) if (uhp->uh_save_nr > 0)
{ {
@@ -3124,37 +3123,6 @@ ex_undolist(exarg_T *eap UNUSED)
} }
} }
/*
* 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 * ":undojoin": continue adding to the last entry list
*/ */

View File

@@ -741,6 +741,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2225,
/**/ /**/
2224, 2224,
/**/ /**/

View File

@@ -811,6 +811,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
#define WILD_ALLLINKS 0x200 #define WILD_ALLLINKS 0x200
#define WILD_IGNORE_COMPLETESLASH 0x400 #define WILD_IGNORE_COMPLETESLASH 0x400
#define WILD_NOERROR 0x800 // sets EW_NOERROR #define WILD_NOERROR 0x800 // sets EW_NOERROR
#define WILD_BUFLASTUSED 0x1000
// Flags for expand_wildcards() // Flags for expand_wildcards()
#define EW_DIR 0x01 // include directory names #define EW_DIR 0x01 // include directory names

View File

@@ -2152,7 +2152,7 @@ write_viminfo_filemarks(FILE *fp)
/* /*
* Compare functions for qsort() below, that compares b_last_used. * Compare functions for qsort() below, that compares b_last_used.
*/ */
static int int
buf_compare(const void *s1, const void *s2) buf_compare(const void *s1, const void *s2)
{ {
buf_T *buf1 = *(buf_T **)s1; buf_T *buf1 = *(buf_T **)s1;