summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt4
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--runtime/doc/windows.txt1
-rw-r--r--src/buffer.c88
-rw-r--r--src/evalbuffer.c4
-rw-r--r--src/ex_getln.c15
-rw-r--r--src/misc1.c31
-rw-r--r--src/option.c2
-rw-r--r--src/option.h1
-rw-r--r--src/proto/misc1.pro1
-rw-r--r--src/proto/viminfo.pro1
-rw-r--r--src/testdir/test_bufwintabinfo.vim12
-rw-r--r--src/testdir/test_cmdline.vim45
-rw-r--r--src/testdir/test_excmd.vim25
-rw-r--r--src/undo.c36
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h1
-rw-r--r--src/viminfo.c2
18 files changed, 228 insertions, 45 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index a167d5f56..f50c8c5b8 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4778,6 +4778,10 @@ 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.
+ {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.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 029a0727f..6e4ff1d6f 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -8689,6 +8689,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: >
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 0c37cf539..813de1b66 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -1090,6 +1090,7 @@ list of buffers. |unlisted-buffer|
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
diff --git a/src/buffer.c b/src/buffer.c
index 86f6ffced..15fbc173e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2601,6 +2601,13 @@ buflist_findpat(
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,6 +2626,9 @@ ExpandBufnames(
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,7 +2685,16 @@ ExpandBufnames(
p = home_replace_save(buf, p);
else
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);
return FAIL;
}
+#ifdef FEAT_VIMINFO
+ if (options & WILD_BUFLASTUSED)
+ matches = ALLOC_MULT(bufmatch_T, count);
+#endif
}
}
vim_regfree(regmatch.regprog);
@@ -2701,6 +2724,28 @@ ExpandBufnames(
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,7 +3061,7 @@ buflist_findlnum(buf_T *buf)
void
buflist_list(exarg_T *eap)
{
- buf_T *buf;
+ buf_T *buf = firstbuf;
int len;
int i;
int ro_char;
@@ -3026,7 +3071,32 @@ buflist_list(exarg_T *eap)
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,13 +3170,23 @@ buflist_list(exarg_T *eap)
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
+#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
}
/*
diff --git a/src/evalbuffer.c b/src/evalbuffer.c
index 35c9ed245..a82b89727 100644
--- a/src/evalbuffer.c
+++ b/src/evalbuffer.c
@@ -595,6 +595,10 @@ get_buffer_info(buf_T *buf)
}
#endif
+#ifdef FEAT_VIMINFO
+ dict_add_number(dict, "lastused", buf->b_last_used);
+#endif
+
return dict;
}
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 9c98f99ba..5cc1c3489 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -1407,6 +1407,9 @@ getcmdline_int(
*/
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,10 +1422,10 @@ getcmdline_int(
did_wild_list = TRUE;
}
if (wim_flags[wim_index] & WIM_LONGEST)
- res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
+ res = nextwild(&xpc, WILD_LONGEST, options,
firstc != '@');
else if (wim_flags[wim_index] & WIM_FULL)
- res = nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP,
+ res = nextwild(&xpc, WILD_NEXT, options,
firstc != '@');
else
res = OK; /* don't insert 'wildchar' now */
@@ -1434,10 +1437,10 @@ getcmdline_int(
/* if 'wildmode' first contains "longest", get longest
* common part */
if (wim_flags[0] & WIM_LONGEST)
- res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
+ res = nextwild(&xpc, WILD_LONGEST, options,
firstc != '@');
else
- res = nextwild(&xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP,
+ res = nextwild(&xpc, WILD_EXPAND_KEEP, options,
firstc != '@');
/* if interrupted while completing, behave like it failed */
@@ -1488,10 +1491,10 @@ getcmdline_int(
redrawcmd();
did_wild_list = TRUE;
if (wim_flags[wim_index] & WIM_LONGEST)
- nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP,
+ nextwild(&xpc, WILD_LONGEST, options,
firstc != '@');
else if (wim_flags[wim_index] & WIM_FULL)
- nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP,
+ nextwild(&xpc, WILD_NEXT, options,
firstc != '@');
}
else
diff --git a/src/misc1.c b/src/misc1.c
index 0e182555a..74c8eaa66 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -2576,3 +2576,34 @@ path_with_url(char_u *fname)
;
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);
+ }
+}
diff --git a/src/option.c b/src/option.c
index 3314a3c4b..1140a01ac 100644
--- a/src/option.c
+++ b/src/option.c
@@ -6987,6 +6987,8 @@ 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;
p += i;
diff --git a/src/option.h b/src/option.h
index 254ca6a28..3ee997913 100644
--- a/src/option.h
+++ b/src/option.h
@@ -338,6 +338,7 @@
#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"
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index 1a06d7ed7..2f7bd0388 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -46,4 +46,5 @@ int goto_im(void);
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 : */
diff --git a/src/proto/viminfo.pro b/src/proto/viminfo.pro
index a1f03373d..b1f97f6ed 100644
--- a/src/proto/viminfo.pro
+++ b/src/proto/viminfo.pro
@@ -3,5 +3,6 @@ int get_viminfo_parameter(int type);
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 : */
diff --git a/src/testdir/test_bufwintabinfo.vim b/src/testdir/test_bufwintabinfo.vim
index ee22ebd45..b4b8a109b 100644
--- a/src/testdir/test_bufwintabinfo.vim
+++ b/src/testdir/test_bufwintabinfo.vim
@@ -139,3 +139,15 @@ function Test_get_win_options()
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
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 55f35e7dd..57dff4167 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -767,3 +767,48 @@ func Test_cmdwin_bug()
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 = "\<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
diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim
index 509d78dfb..992fc3de0 100644
--- a/src/testdir/test_excmd.vim
+++ b/src/testdir/test_excmd.vim
@@ -19,3 +19,28 @@ func Test_range_error()
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
diff --git a/src/undo.c b/src/undo.c
index 2736f2a33..64fb2050c 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -106,7 +106,6 @@ static void u_getbot(void);
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);
@@ -2973,7 +2972,7 @@ u_undo_end(
if (uhp == NULL)
*msgbuf = NUL;
else
- u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
+ add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
#ifdef FEAT_CONCEAL
{
@@ -3050,7 +3049,7 @@ ex_undolist(exarg_T *eap UNUSED)
break;
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)
{
@@ -3125,37 +3124,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
*/
void
diff --git a/src/version.c b/src/version.c
index 62a48880d..3f36958a6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2225,
+/**/
2224,
/**/
2223,
diff --git a/src/vim.h b/src/vim.h
index 917dbfa72..230f5b3f8 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -811,6 +811,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
#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
diff --git a/src/viminfo.c b/src/viminfo.c
index b16282856..a49bb01da 100644
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -2152,7 +2152,7 @@ write_viminfo_filemarks(FILE *fp)
/*
* Compare functions for qsort() below, that compares b_last_used.
*/
- static int
+ int
buf_compare(const void *s1, const void *s2)
{
buf_T *buf1 = *(buf_T **)s1;