changeset 20615:8eed1e9389bb v8.2.0861

patch 8.2.0861: cannot easily get all the current marks Commit: https://github.com/vim/vim/commit/cfb4b47de08e4437c692d382067dc1692cd83c23 Author: Bram Moolenaar <Bram@vim.org> Date: Sun May 31 15:41:57 2020 +0200 patch 8.2.0861: cannot easily get all the current marks Problem: Cannot easily get all the current marks. Solution: Add getmarklist(). (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/6032)
author Bram Moolenaar <Bram@vim.org>
date Sun, 31 May 2020 15:45:04 +0200
parents 78caf677340d
children 3c58d3e60e98
files runtime/doc/eval.txt runtime/doc/usr_41.txt src/evalfunc.c src/mark.c src/proto/mark.pro src/testdir/test_marks.vim src/version.c
diffstat 7 files changed, 171 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2497,6 +2497,7 @@ getjumplist([{winnr} [, {tabnr}]])
 getline({lnum})			String	line {lnum} of current buffer
 getline({lnum}, {end})		List	lines {lnum} to {end} of current buffer
 getloclist({nr} [, {what}])	List	list of location list items
+getmarklist([{expr}])		List	list of global/local marks
 getmatches([{win}])		List	list of current matches
 getmousepos()			Dict	last known mouse position
 getpid()			Number	process ID of Vim
@@ -5365,6 +5366,25 @@ getloclist({nr} [, {what}])				*getlocli
 					|location-list-file-window| for more
 					details.
 
+getmarklist([{expr}]					*getmarklist()*
+		Without the {expr} argument returns a |List| with information
+		about all the global marks. |mark|
+
+		If the optional {expr} argument is specified, returns the
+		local marks defined in buffer {expr}.  For the use of {expr},
+		see |bufname()|.
+
+		Each item in the retuned List is a |Dict| with the following:
+		    name - name of the mark prefixed by "'"
+		    pos - a |List| with the position of the mark:
+				[bufnum, lnum, col, off]
+			  Refer to |getpos()| for more information.
+		    file - file name
+
+		Refer to |getpos()| for getting information about a specific
+		mark.
+
+
 getmatches([{win}])					*getmatches()*
 		Returns a |List| with all matches previously defined for the
 		current window by |matchadd()| and the |:match| commands.
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -724,6 +724,7 @@ Cursor and mark position:		*cursor-funct
 	getcurpos()		get position of the cursor
 	getpos()		get position of cursor, mark, etc.
 	setpos()		set position of cursor, mark, etc.
+	getmarklist()		list of global/local marks
 	byte2line()		get line number at a specific byte count
 	line2byte()		byte count at a specific line
 	diff_filler()		get the number of filler lines above a line
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -579,6 +579,7 @@ static funcentry_T global_functions[] =
     {"getjumplist",	0, 2, FEARG_1,	  ret_list_any,	f_getjumplist},
     {"getline",		1, 2, FEARG_1,	  ret_f_getline, f_getline},
     {"getloclist",	1, 2, 0,	  ret_list_dict_any, f_getloclist},
+    {"getmarklist",	0, 1, 0,	  ret_list_dict_any,  f_getmarklist},
     {"getmatches",	0, 1, 0,	  ret_list_dict_any, f_getmatches},
     {"getmousepos",	0, 0, 0,	  ret_dict_number, f_getmousepos},
     {"getpid",		0, 0, 0,	  ret_number,	f_getpid},
--- a/src/mark.c
+++ b/src/mark.c
@@ -1412,3 +1412,124 @@ get_namedfm(void)
 {
     return namedfm;
 }
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Add information about mark 'mname' to list 'l'
+ */
+    static int
+add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
+{
+    dict_T	*d;
+    list_T	*lpos;
+
+    if (pos->lnum <= 0)
+	return OK;
+
+    d = dict_alloc();
+    if (d == NULL)
+	return FAIL;
+
+    if (list_append_dict(l, d) == FAIL)
+    {
+	dict_unref(d);
+	return FAIL;
+    }
+
+    lpos = list_alloc();
+    if (lpos == NULL)
+	return FAIL;
+
+    list_append_number(lpos, bufnr);
+    list_append_number(lpos, pos->lnum);
+    list_append_number(lpos, pos->col);
+    list_append_number(lpos, pos->coladd);
+
+    if (dict_add_string(d, "mark", mname) == FAIL
+	    || dict_add_list(d, "pos", lpos) == FAIL
+	    || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
+	return FAIL;
+
+    return OK;
+}
+
+/*
+ * Get information about marks local to a buffer.
+ */
+    static void
+get_buf_local_marks(buf_T *buf, list_T *l)
+{
+    char_u	mname[3] = "' ";
+    int		i;
+
+    // Marks 'a' to 'z'
+    for (i = 0; i < NMARKS; ++i)
+    {
+	mname[1] = 'a' + i;
+	add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
+    }
+
+    // Mark '' is a window local mark and not a buffer local mark
+    add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
+
+    add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
+    add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
+}
+
+/*
+ * Get information about global marks ('A' to 'Z' and '0' to '9')
+ */
+    static void
+get_global_marks(list_T *l)
+{
+    char_u	mname[3] = "' ";
+    int		i;
+    char_u	*name;
+
+    // Marks 'A' to 'Z' and '0' to '9'
+    for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
+    {
+	if (namedfm[i].fmark.fnum != 0)
+	    name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
+	else
+	    name = namedfm[i].fname;
+	if (name != NULL)
+	{
+	    mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
+	    add_mark(l, mname, &namedfm[i].fmark.mark,
+		    namedfm[i].fmark.fnum, name);
+	    if (namedfm[i].fmark.fnum != 0)
+		vim_free(name);
+	}
+    }
+}
+
+/*
+ * getmarklist() function
+ */
+    void
+f_getmarklist(typval_T *argvars, typval_T *rettv)
+{
+    buf_T	*buf = NULL;
+
+    if (rettv_list_alloc(rettv) != OK)
+	return;
+
+    if (argvars[0].v_type == VAR_UNKNOWN)
+    {
+	get_global_marks(rettv->vval.v_list);
+	return;
+    }
+
+    buf = tv_get_buf(&argvars[0], FALSE);
+    if (buf == NULL)
+	return;
+
+    get_buf_local_marks(buf, rettv->vval.v_list);
+}
+#endif
--- a/src/proto/mark.pro
+++ b/src/proto/mark.pro
@@ -27,4 +27,5 @@ void free_jumplist(win_T *wp);
 void set_last_cursor(win_T *win);
 void free_all_marks(void);
 xfmark_T *get_namedfm(void);
+void f_getmarklist(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/testdir/test_marks.vim
+++ b/src/testdir/test_marks.vim
@@ -259,4 +259,29 @@ func Test_file_mark()
   call delete('Xtwo')
 endfunc
 
+" Test for the getmarklist() function
+func Test_getmarklist()
+  new
+  " global marks
+  delmarks A-Z 0-9 \" ^.[]
+  call assert_equal([], getmarklist())
+  call setline(1, ['one', 'two', 'three'])
+  mark A
+  call cursor(3, 5)
+  normal mN
+  call assert_equal([{'file' : '', 'mark' : "'A", 'pos' : [bufnr(), 1, 0, 0]},
+        \ {'file' : '', 'mark' : "'N", 'pos' : [bufnr(), 3, 4, 0]}],
+        \ getmarklist())
+  " buffer local marks
+  delmarks!
+  call assert_equal([{'mark' : "''", 'pos' : [bufnr(), 1, 0, 0]},
+        \ {'mark' : "'\"", 'pos' : [bufnr(), 1, 0, 0]}], getmarklist(bufnr()))
+  call cursor(2, 2)
+  normal mr
+  call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 1, 0]},
+        \ getmarklist(bufnr())[0])
+  call assert_equal([], getmarklist({}))
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    861,
+/**/
     860,
 /**/
     859,