aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/ex_docmd.c8
-rw-r--r--src/nvim/map.h2
-rw-r--r--src/nvim/mark.c161
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/options.lua7
7 files changed, 179 insertions, 6 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 48e6b61492..e2c93f481d 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -663,6 +663,7 @@ struct file_buffer {
Callback b_ofu_cb; ///< 'omnifunc' callback
char *b_p_tfu; ///< 'tagfunc'
Callback b_tfu_cb; ///< 'tagfunc' callback
+ char *b_p_umf; ///< 'usermarkfunc'
char *b_p_urf; ///< 'userregfunc'
int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e11dd0a2f6..785b727cd3 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3184,7 +3184,7 @@ char *skip_range(const char *cmd, int *ctx)
}
}
if (*cmd != NUL) {
- cmd++;
+ cmd += utf_ptr2len(cmd);
}
}
@@ -3327,13 +3327,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.h b/src/nvim/map.h
index 59991b9b94..9b91d3c7a9 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -39,8 +39,8 @@
//
// NOTE: Keys AND values must be allocated! khash.h does not make a copy.
//
-MAP_DECLS(int, int)
MAP_DECLS(int, ptr_t)
+MAP_DECLS(int, int)
MAP_DECLS(ptr_t, 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 b98935e93d..904eaf32c0 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -15,6 +15,9 @@
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/edit.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
@@ -25,6 +28,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 +46,8 @@
#include "nvim/types.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 +60,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 +192,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 +374,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 +471,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 f236392f4a..97a1c04572 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4002,6 +4002,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_sw);
case PV_TFU:
return (char_u *)&(curbuf->b_p_tfu);
+ case PV_UMF:
+ return (char_u *)&(curbuf->b_p_umf);
case PV_TS:
return (char_u *)&(curbuf->b_p_ts);
case PV_TW:
@@ -4337,6 +4339,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 95304bf904..32733d0c4b 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -716,6 +716,7 @@ EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter'
#define TPF_C0 0x020
#define TPF_C1 0x040
EXTERN char *p_tfu; ///< 'tagfunc'
+EXTERN char *p_umf; ///< 'usermarkfunc'
EXTERN char *p_spc; ///< 'spellcapcheck'
EXTERN char *p_spf; ///< 'spellfile'
EXTERN char *p_spk; ///< 'splitkeep'
@@ -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 a7f7ca2cf7..7d2f9075f6 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='userregfunc', abbreviation='urf',
short_desc=N_("Function used to define behavior of user-defined registers."),
type='string', scope={'buffer'},