mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 14:58:18 +00:00
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 vim/vim#4722)
52410575be
This commit is contained in:
@@ -4164,6 +4164,9 @@ getbufinfo([{dict}])
|
||||
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.
|
||||
listed TRUE if the buffer is listed.
|
||||
lnum current line number in buffer.
|
||||
linecount number of lines in the buffer (only
|
||||
|
@@ -6742,6 +6742,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
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: >
|
||||
|
@@ -1023,6 +1023,7 @@ list of buffers. |unlisted-buffer|
|
||||
# alternate buffer
|
||||
R terminal buffers with a running job
|
||||
F terminal buffers with a finished job
|
||||
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
|
||||
|
@@ -2251,6 +2251,23 @@ int buflist_findpat(
|
||||
return match;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
buf_T *buf;
|
||||
char_u *match;
|
||||
} bufmatch_T;
|
||||
|
||||
/// Compare functions for qsort() below, that compares b_last_used.
|
||||
static int
|
||||
buf_time_compare(const void *s1, const void *s2)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all buffer names that match.
|
||||
@@ -2264,6 +2281,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
|
||||
char_u *p;
|
||||
int attempt;
|
||||
char_u *patc;
|
||||
bufmatch_T *matches = NULL;
|
||||
|
||||
*num_file = 0; // return values in case of FAIL
|
||||
*file = NULL;
|
||||
@@ -2314,15 +2332,25 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
|
||||
} else {
|
||||
p = vim_strsave(p);
|
||||
}
|
||||
if (matches != NULL) {
|
||||
matches[count].buf = buf;
|
||||
matches[count].match = p;
|
||||
count++;
|
||||
} else {
|
||||
(*file)[count++] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count == 0) { // no match found, break here
|
||||
break;
|
||||
}
|
||||
if (round == 1) {
|
||||
*file = xmalloc((size_t)count * sizeof(**file));
|
||||
|
||||
if (options & WILD_BUFLASTUSED) {
|
||||
matches = xmalloc((size_t)count * sizeof(*matches));
|
||||
}
|
||||
}
|
||||
}
|
||||
vim_regfree(regmatch.regprog);
|
||||
@@ -2335,6 +2363,25 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
|
||||
xfree(patc);
|
||||
}
|
||||
|
||||
if (matches != NULL) {
|
||||
if (count > 1) {
|
||||
qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
|
||||
}
|
||||
|
||||
// if the current buffer is first in the list, place it at the end
|
||||
if (matches[0].buf == curbuf) {
|
||||
for (int i = 1; i < count; i++) {
|
||||
(*file)[i-1] = matches[i].match;
|
||||
}
|
||||
(*file)[count-1] = matches[0].match;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
(*file)[i] = matches[i].match;
|
||||
}
|
||||
}
|
||||
xfree(matches);
|
||||
}
|
||||
|
||||
*num_file = count;
|
||||
return count == 0 ? FAIL : OK;
|
||||
}
|
||||
@@ -2595,11 +2642,32 @@ linenr_T buflist_findlnum(buf_T *buf)
|
||||
// List all known file names (for :files and :buffers command).
|
||||
void buflist_list(exarg_T *eap)
|
||||
{
|
||||
buf_T *buf;
|
||||
buf_T *buf = firstbuf;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) {
|
||||
garray_T buflist;
|
||||
buf_T **buflist_data = NULL, **p;
|
||||
|
||||
if (vim_strchr(eap->arg, 't')) {
|
||||
ga_init(&buflist, sizeof(buf_T *), 50);
|
||||
for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
|
||||
ga_grow(&buflist, 1);
|
||||
((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf;
|
||||
}
|
||||
|
||||
qsort(buflist.ga_data, (size_t)buflist.ga_len,
|
||||
sizeof(buf_T *), buf_time_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) {
|
||||
const bool is_terminal = buf->terminal;
|
||||
const bool job_running = buf->terminal && terminal_running(buf->terminal);
|
||||
|
||||
@@ -2660,13 +2728,22 @@ void buflist_list(exarg_T *eap)
|
||||
do {
|
||||
IObuff[len++] = ' ';
|
||||
} while (--i > 0 && len < IOSIZE - 18);
|
||||
if (vim_strchr(eap->arg, 't') && buf->b_last_used) {
|
||||
add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
|
||||
} else {
|
||||
vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
|
||||
_("line %" PRId64),
|
||||
buf == curbuf ? (int64_t)curwin->w_cursor.lnum
|
||||
: (int64_t)buflist_findlnum(buf));
|
||||
}
|
||||
|
||||
msg_outtrans(IObuff);
|
||||
line_breakcheck();
|
||||
}
|
||||
|
||||
if (buflist_data) {
|
||||
ga_clear(&buflist);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -7241,6 +7241,8 @@ dict_T *get_buffer_info(buf_T *buf)
|
||||
tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
|
||||
}
|
||||
|
||||
tv_dict_add_nr(dict, S_LEN("lastused"), buf->b_last_used);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
@@ -947,6 +947,10 @@ static int command_line_execute(VimState *state, int key)
|
||||
// - wildcard expansion is only done when the 'wildchar' key is really
|
||||
// typed, not when it comes from a macro
|
||||
if ((s->c == p_wc && !s->gotesc && KeyTyped) || s->c == p_wcm) {
|
||||
int options = WILD_NO_BEEP;
|
||||
if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
|
||||
options |= WILD_BUFLASTUSED;
|
||||
}
|
||||
if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
|
||||
// if 'wildmode' contains "list" may still need to list
|
||||
if (s->xpc.xp_numfiles > 1
|
||||
@@ -960,10 +964,10 @@ static int command_line_execute(VimState *state, int key)
|
||||
}
|
||||
|
||||
if (wim_flags[s->wim_index] & WIM_LONGEST) {
|
||||
s->res = nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
|
||||
s->res = nextwild(&s->xpc, WILD_LONGEST, options,
|
||||
s->firstc != '@');
|
||||
} else if (wim_flags[s->wim_index] & WIM_FULL) {
|
||||
s->res = nextwild(&s->xpc, WILD_NEXT, WILD_NO_BEEP,
|
||||
s->res = nextwild(&s->xpc, WILD_NEXT, options,
|
||||
s->firstc != '@');
|
||||
} else {
|
||||
s->res = OK; // don't insert 'wildchar' now
|
||||
@@ -975,10 +979,10 @@ static int command_line_execute(VimState *state, int key)
|
||||
// if 'wildmode' first contains "longest", get longest
|
||||
// common part
|
||||
if (wim_flags[0] & WIM_LONGEST) {
|
||||
s->res = nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
|
||||
s->res = nextwild(&s->xpc, WILD_LONGEST, options,
|
||||
s->firstc != '@');
|
||||
} else {
|
||||
s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP,
|
||||
s->res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options,
|
||||
s->firstc != '@');
|
||||
}
|
||||
|
||||
@@ -1016,10 +1020,10 @@ static int command_line_execute(VimState *state, int key)
|
||||
s->did_wild_list = true;
|
||||
|
||||
if (wim_flags[s->wim_index] & WIM_LONGEST) {
|
||||
nextwild(&s->xpc, WILD_LONGEST, WILD_NO_BEEP,
|
||||
nextwild(&s->xpc, WILD_LONGEST, options,
|
||||
s->firstc != '@');
|
||||
} else if (wim_flags[s->wim_index] & WIM_FULL) {
|
||||
nextwild(&s->xpc, WILD_NEXT, WILD_NO_BEEP,
|
||||
nextwild(&s->xpc, WILD_NEXT, options,
|
||||
s->firstc != '@');
|
||||
}
|
||||
} else {
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#define WILD_ALLLINKS 0x200
|
||||
#define WILD_IGNORE_COMPLETESLASH 0x400
|
||||
#define WILD_NOERROR 0x800 // sets EW_NOERROR
|
||||
#define WILD_BUFLASTUSED 0x1000
|
||||
|
||||
/// Present history tables
|
||||
typedef enum {
|
||||
|
@@ -1162,3 +1162,26 @@ int goto_im(void)
|
||||
{
|
||||
return p_im && stuff_empty() && typebuf_typed();
|
||||
}
|
||||
|
||||
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
|
||||
void add_time(char_u *buf, size_t buflen, time_t tt)
|
||||
{
|
||||
struct tm curtime;
|
||||
|
||||
if (time(NULL) - tt >= 100) {
|
||||
os_localtime_r(&tt, &curtime);
|
||||
if (time(NULL) - 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 {
|
||||
int64_t seconds = time(NULL) - tt;
|
||||
vim_snprintf((char *)buf, buflen,
|
||||
NGETTEXT("%" PRId64 " second ago",
|
||||
"%" PRId64 " seconds ago", (uint32_t)seconds),
|
||||
seconds);
|
||||
}
|
||||
}
|
||||
|
@@ -7083,6 +7083,8 @@ static int check_opt_wim(void)
|
||||
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;
|
||||
}
|
||||
|
@@ -277,6 +277,7 @@ enum {
|
||||
#define WIM_FULL 1
|
||||
#define WIM_LONGEST 2
|
||||
#define WIM_LIST 4
|
||||
#define WIM_BUFLASTUSED 8
|
||||
|
||||
// arguments for can_bs()
|
||||
#define BS_INDENT 'i' // "Indent"
|
||||
|
@@ -149,3 +149,10 @@ func Test_getbufinfo_lines()
|
||||
edit Xfoo
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
function Test_getbufinfo_lastused()
|
||||
new Xfoo
|
||||
let info = getbufinfo('Xfoo')[0]
|
||||
call assert_equal(has_key(info, 'lastused'), 1)
|
||||
call assert_equal(type(info.lastused), type(0))
|
||||
endfunc
|
||||
|
@@ -755,3 +755,49 @@ func Test_cmdwin_feedkeys()
|
||||
" This should not generate E488
|
||||
call feedkeys("q:\<CR>", 'x')
|
||||
endfunc
|
||||
|
||||
func Test_buffers_lastused()
|
||||
" check that buffers are sorted by time when wildmode has lastused
|
||||
edit bufc " oldest
|
||||
|
||||
sleep 1200m
|
||||
enew
|
||||
edit bufa " middle
|
||||
|
||||
sleep 1200m
|
||||
enew
|
||||
edit bufb " newest
|
||||
|
||||
enew
|
||||
|
||||
call assert_equal(['bufc', 'bufa', 'bufb'],
|
||||
\ 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
|
||||
|
||||
sleep 1200m
|
||||
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
|
||||
|
@@ -8,3 +8,35 @@ func Test_ex_delete()
|
||||
.dl
|
||||
call assert_equal(['a', 'c'], getline(1, 2))
|
||||
endfunc
|
||||
|
||||
func Test_buffers_lastused()
|
||||
edit bufc " oldest
|
||||
|
||||
sleep 1200m
|
||||
edit bufa " middle
|
||||
|
||||
sleep 1200m
|
||||
edit bufb " newest
|
||||
|
||||
enew
|
||||
|
||||
let ls = split(execute('buffers t', 'silent!'), '\n')
|
||||
let bufs = []
|
||||
for line in ls
|
||||
let bufs += [split(line, '"\s*')[1:2]]
|
||||
endfor
|
||||
|
||||
let names = []
|
||||
for buf in bufs
|
||||
if buf[0] !=# '[No Name]'
|
||||
let names += [buf[0]]
|
||||
endif
|
||||
endfor
|
||||
|
||||
call assert_equal(['bufb', 'bufa', 'bufc'], names)
|
||||
call assert_match('[0-2] seconds ago', bufs[1][1])
|
||||
|
||||
bwipeout bufa
|
||||
bwipeout bufb
|
||||
bwipeout bufc
|
||||
endfunc
|
||||
|
@@ -2441,10 +2441,11 @@ static void u_undo_end(
|
||||
uhp = curbuf->b_u_newhead;
|
||||
}
|
||||
|
||||
if (uhp == NULL)
|
||||
if (uhp == NULL) {
|
||||
*msgbuf = NUL;
|
||||
else
|
||||
u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
|
||||
} else {
|
||||
add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
|
||||
}
|
||||
|
||||
{
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
@@ -2509,7 +2510,7 @@ void ex_undolist(exarg_T *eap)
|
||||
&& uhp->uh_walk != mark) {
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
|
||||
uhp->uh_seq, changes);
|
||||
u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
|
||||
add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
|
||||
uhp->uh_time);
|
||||
if (uhp->uh_save_nr > 0) {
|
||||
while (STRLEN(IObuff) < 33)
|
||||
@@ -2574,30 +2575,6 @@ void ex_undolist(exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct tm curtime;
|
||||
|
||||
if (time(NULL) - tt >= 100) {
|
||||
os_localtime_r(&tt, &curtime);
|
||||
if (time(NULL) - 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 {
|
||||
int64_t seconds = time(NULL) - tt;
|
||||
vim_snprintf((char *)buf, buflen,
|
||||
NGETTEXT("%" PRId64 " second ago",
|
||||
"%" PRId64 " seconds ago", (uint32_t)seconds),
|
||||
seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ":undojoin": continue adding to the last entry list
|
||||
*/
|
||||
|
Reference in New Issue
Block a user