aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/usermark.vim7
-rw-r--r--runtime/lua/vim/usermark.lua68
-rw-r--r--runtime/plugin/usermark.vim1
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/eval/funcs.c1
-rw-r--r--src/nvim/ex_docmd.c8
-rw-r--r--src/nvim/map.c1
-rw-r--r--src/nvim/map.h1
-rw-r--r--src/nvim/mark.c158
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/options.lua7
12 files changed, 254 insertions, 5 deletions
diff --git a/runtime/autoload/usermark.vim b/runtime/autoload/usermark.vim
new file mode 100644
index 0000000000..b1b4113d1a
--- /dev/null
+++ b/runtime/autoload/usermark.vim
@@ -0,0 +1,7 @@
+" This is used for the default userreg function.
+
+lua vim.usermark = require('vim.usermark')
+
+function! usermark#func(action, mark) abort
+ return v:lua.vim.usermark.fn(a:action, a:mark)
+endfunction
diff --git a/runtime/lua/vim/usermark.lua b/runtime/lua/vim/usermark.lua
new file mode 100644
index 0000000000..0d1ec0ae0f
--- /dev/null
+++ b/runtime/lua/vim/usermark.lua
@@ -0,0 +1,68 @@
+-- Defualt implementation of the usermarkfunc. This default implementation is
+-- extensible and allows other plugins to register handlers for different
+-- registers.
+--
+-- The default handler behaves just as a normal register would.
+
+local vim = assert(vim)
+local usermark = {}
+
+-- Returns a "default handler" which behaves like normal global marks. When a
+-- call to set() is made, it stores the current line and col of the cursor and
+-- the filename of the current file.
+function usermark._default_handler()
+ local d = {}
+
+ -- Called when a mark is recalled using the "'" command. Just returns what was
+ -- stored before or nothing if it was never set before.
+ function d.get(self, mark)
+ return self.content or {}
+ end
+
+ -- Called when a mark is set using the "m" command. Stores the current cursor
+ -- position to be recalled at a later time.
+ function d.set(self, mark)
+ local r,c = unpack(vim.api.nvim_win_get_cursor(0))
+ local file = vim.fn.expand("%:p")
+
+ self.content = {
+ line = r;
+ col = c;
+ }
+
+ if file ~= '' then
+ self.content.file = file
+ end
+ end
+
+ return d
+end
+
+-- The store for register default handler
+usermark._marktable = {}
+
+-- Function for the 'usermarkfunc'. Will defer to the handler associated with
+-- the provided mark.
+--
+-- If not handler is registered to a given mark, the default handler is used,
+-- which is a re-implementation of standard mark behavior.
+function usermark.fn(action, mark)
+ if not usermark._marktable[mark] then
+ usermark._marktable[mark] = usermark._default_handler()
+ end
+
+ if action == "get" then
+ return usermark._marktable[mark]:get(mark)
+ else
+ usermark._marktable[mark]:set(mark)
+ return nil
+ end
+end
+
+-- Registers a handler with a mark. Gets and sets will then defer to this
+-- handler when determining the mark's behavior.
+function usermark.register_handler(mark, handler)
+ usermark._marktable[mark] = handler
+end
+
+return usermark
diff --git a/runtime/plugin/usermark.vim b/runtime/plugin/usermark.vim
new file mode 100644
index 0000000000..917e7510f1
--- /dev/null
+++ b/runtime/plugin/usermark.vim
@@ -0,0 +1 @@
+set usermarkfunc=usermark#func
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index f01edd1ad2..fa76377c4e 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -657,6 +657,7 @@ struct file_buffer {
#ifdef BACKSLASH_IN_FILENAME
char *b_p_csl; ///< 'completeslash'
#endif
+ char *b_p_umf; ///< 'usermarkfunc'
char *b_p_cfu; ///< 'completefunc'
Callback b_cfu_cb; ///< 'completefunc' callback
char *b_p_ofu; ///< 'omnifunc'
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 5a31288ecd..a6dc41d0f3 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3137,6 +3137,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"title",
"user-commands", // was accidentally included in 5.4
"user_commands",
+ "usermarks",
"vartabs",
"vertsplit",
"vimscript-1",
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index a24e8458a6..5d271e4ef6 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3195,7 +3195,7 @@ char *skip_range(const char *cmd, int *ctx)
}
}
if (*cmd != NUL) {
- cmd++;
+ cmd += utf_ptr2len(cmd);
}
}
@@ -3338,13 +3338,13 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
goto error;
}
if (skip) {
- cmd++;
+ cmd += utfc_ptr2len(cmd);
} else {
// Only accept a mark in another file when it is
// used by itself: ":'M".
MarkGet flag = to_other_file && cmd[1] == NUL ? kMarkAll : kMarkBufLocal;
- fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd);
- cmd++;
+ fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, utf_ptr2char(cmd));
+ cmd += utf_ptr2len(cmd);
if (fm != NULL && fm->fnum != curbuf->handle) {
// Jumped to another file.
lnum = curwin->w_cursor.lnum;
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 191a459863..24066659a8 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -162,6 +162,7 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
}
+MAP_IMPL(int, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 92f0b32255..6a548ad30b 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -39,6 +39,7 @@
//
// NOTE: Keys AND values must be allocated! khash.h does not make a copy.
//
+MAP_DECLS(int, ptr_t)
MAP_DECLS(int, int)
MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index f1a1f25e6c..0c7f5efd89 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -26,6 +26,7 @@
#include "nvim/globals.h"
#include "nvim/highlight_defs.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -42,6 +43,8 @@
#include "nvim/textobject.h"
#include "nvim/undo_defs.h"
#include "nvim/vim.h"
+#include "nvim/map.h"
+#include "nvim/eval/userfunc.h"
// This file contains routines to maintain and manipulate marks.
@@ -54,6 +57,32 @@
/// Global marks (marks with file number or name)
static xfmark_T namedfm[NGLOBALMARKS];
+static struct {
+ Map(int, ptr_t) named;
+} usermarks;
+bool usermarks_init;
+
+static xfmark_T* lookup_user_mark(int mark)
+{
+ if (!usermarks_init) {
+ map_init(int, ptr_t, &usermarks.named);
+ usermarks_init = 1;
+ }
+
+ xfmark_T **ret =
+ (xfmark_T**) map_ref(int, ptr_t)(&usermarks.named, mark, true);
+
+ if (ret) {
+ if (! (*ret)) {
+ *ret = xcalloc(sizeof(xfmark_T), 1);
+ }
+
+ return *ret;
+ }
+
+ return NULL;
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mark.c.generated.h"
#endif
@@ -160,7 +189,8 @@ int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt)
RESET_XFMARK(namedfm + i, *pos, fnum, view, NULL);
return OK;
}
- return FAIL;
+
+ return mark_set_user(buf, c);
}
// Set the previous context mark to the current position and add it to the
@@ -341,6 +371,15 @@ fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name)
// Local Marks
fm = mark_get_local(buf, win, name);
}
+
+ if (!fm) {
+ // Get usermark.
+ xfmark_T* xm = mark_get_user(buf, name);
+ if (xm) {
+ fm = &xm->fmark;
+ }
+ }
+
if (fmp != NULL && fm != NULL) {
*fmp = *fm;
return fmp;
@@ -429,6 +468,123 @@ fmark_T *mark_get_local(buf_T *buf, win_T *win, int name)
return mark;
}
+/// Loads the mark 'out' with the results from calling the usermarkfunc.
+///
+/// @param umf String for the usermarkfunc
+/// @param name name for the mark
+/// @param[out] out the mark to write the results to.
+static int call_umf(
+ const char* umf, int name, typval_T* out, const char* get_or_set_a)
+{
+ char markname_str[5];
+ char get_or_set[4];
+ int len;
+
+ strncpy(get_or_set, get_or_set_a, sizeof(get_or_set));
+ get_or_set[3] = 0;
+
+ len = (*utf_char2len)(name);
+ markname_str[len] = 0;
+ utf_char2bytes(name, markname_str);
+
+ typval_T args[3];
+ args[0].v_type = VAR_STRING;
+ args[1].v_type = VAR_STRING;
+ args[2].v_type = VAR_UNKNOWN;
+
+ args[0].vval.v_string = get_or_set;
+ args[1].vval.v_string = markname_str;
+
+ funcexe_T funcexe = FUNCEXE_INIT;
+ funcexe.fe_evaluate = true;
+
+ return call_func(umf, -1, out, 2, args, &funcexe);
+}
+
+static int typval_to_xfmark(buf_T* buf, xfmark_T* out, typval_T* in)
+{
+ varnumber_T line;
+ varnumber_T col;
+ char* filename = NULL;
+
+ switch (in->v_type) {
+ case VAR_DICT:
+ line = tv_dict_get_number(in->vval.v_dict, "line");
+ col = tv_dict_get_number(in->vval.v_dict, "col");
+ filename = tv_dict_get_string(in->vval.v_dict, "file", true);
+ break;
+
+ case VAR_NUMBER:
+ line = in->vval.v_number;
+ col = 1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ free_xfmark(*out);
+ memset(out, 0, sizeof(*out));
+
+ out->fname = filename;
+ out->fmark.mark.col = (int) col;
+ out->fmark.mark.lnum = (int) line;
+ out->fmark.fnum = 0;
+ out->fmark.timestamp = os_time();
+
+ return 0;
+}
+
+/// Gets marks that are defined by the user.
+///
+/// @param buf the buffer
+/// @param name name fo the mark
+xfmark_T *mark_get_user(buf_T* buf, int name)
+{
+ const char* umf = (const char*) buf->b_p_umf;
+
+ if (!umf) {
+ return NULL;
+ }
+
+ xfmark_T* mark = lookup_user_mark(name);
+ if (mark) {
+ typval_T* typval = xcalloc(sizeof(typval_T), 1);
+ call_umf(umf, name, typval, "get");
+ typval_to_xfmark(buf, mark, typval);
+ tv_free(typval);
+
+ if (mark->fname) {
+ buf_T* buffer =
+ buflist_new(
+ mark->fname, NULL, mark->fmark.mark.lnum, BLN_CURBUF | BLN_LISTED);
+
+ if (buffer) {
+ mark->fmark.fnum = buffer->b_fnum;
+ }
+ } else {
+ mark->fmark.fnum = buf->b_fnum;
+ }
+ }
+
+ return mark;
+}
+
+int mark_set_user(buf_T* buf, int name)
+{
+ const char* umf = (const char*) buf->b_p_umf;
+
+ if (!umf) {
+ return FAIL;
+ }
+
+ typval_T* out = xcalloc(sizeof(typval_T), 1);
+ call_umf(umf, name, out, "set");
+ tv_free(out);
+
+ return OK;
+}
+
/// Get marks that are actually motions but return them as marks
///
/// Gets the following motions as marks: '{', '}', '(', ')'
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 01a5c7677f..1e7cd62d22 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4003,6 +4003,8 @@ static char_u *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return (char_u *)&(buf->b_p_sw);
case PV_TFU:
return (char_u *)&(buf->b_p_tfu);
+ case PV_UMF:
+ return (char_u *)&(buf->b_p_umf);
case PV_TS:
return (char_u *)&(buf->b_p_ts);
case PV_TW:
@@ -4342,6 +4344,8 @@ void buf_copy_options(buf_T *buf, int flags)
set_buflocal_ofu_callback(buf);
buf->b_p_tfu = xstrdup(p_tfu);
COPY_OPT_SCTX(buf, BV_TFU);
+ buf->b_p_umf = xstrdup(p_umf);
+ COPY_OPT_SCTX(buf, BV_UMF);
set_buflocal_tfu_callback(buf);
buf->b_p_sts = p_sts;
COPY_OPT_SCTX(buf, BV_STS);
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index d190fc5999..16b46dca7a 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -398,6 +398,7 @@ EXTERN long p_channel; ///< 'channel'
EXTERN char *p_cink; ///< 'cinkeys'
EXTERN char *p_cinsd; ///< 'cinscopedecls'
EXTERN char *p_cinw; ///< 'cinwords'
+EXTERN char *p_umf; ///< 'usermarkfunc'
EXTERN char *p_cfu; ///< 'completefunc'
EXTERN char *p_ofu; ///< 'omnifunc'
EXTERN char *p_tsrfu; ///< 'thesaurusfunc'
@@ -903,6 +904,7 @@ enum {
BV_SW,
BV_SWF,
BV_TFU,
+ BV_UMF,
BV_TSRFU,
BV_TAGS,
BV_TC,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 387ccd0888..bfbfdd938e 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2629,6 +2629,13 @@ return {
defaults={if_true=4000}
},
{
+ full_name='usermarkfunc', abbreviation='umf',
+ short_desc=N_("function used to perform jumps/sets to user marks."),
+ type='string', scope={'buffer'},
+ varname='p_umf',
+ defaults={if_true=""}
+ },
+ {
full_name='varsofttabstop', abbreviation='vsts',
short_desc=N_("list of numbers of spaces that <Tab> uses while editing"),
type='string', list='comma', scope={'buffer'},