diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2022-08-21 18:49:46 -0600 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2022-08-21 18:49:46 -0600 |
commit | 8436383af96dc7afa3596fc22c012d68e76f47f8 (patch) | |
tree | b67c79f3e01a6e48117ef9ec3e919ee1bbafbf11 | |
parent | 629169462a82f0fbb7a8911a4554894537d6776c (diff) | |
download | rneovim-8436383af96dc7afa3596fc22c012d68e76f47f8.tar.gz rneovim-8436383af96dc7afa3596fc22c012d68e76f47f8.tar.bz2 rneovim-8436383af96dc7afa3596fc22c012d68e76f47f8.zip |
feat(usermark); implement "user marks", i.e. marks whose behavior can be defined by the user.
(Neo)vim has many different marks defined, but sometimes this may not be
completely adequate.
This change give the user the ability to define behavior for marks which
are not built in to (Neo)vim directly. This is accomplished through a
new option called the "usermarkfunc."
The usermarkfunc points to a vimscript function that takes an "action"
paramter (either "get" or "set") and a mark name.
a basic implementation that re-implements global mark behavior for user
marks would look something like:
let s:marks = {}
function UserMarkFunc(action, mark)
if a:action == "set"
let [n, lnum, col, off, curswant] = getcurpos()
let s:marks[a:mark] =
\ { "line": lnum, "col": col, "file": expand("%:p") }
else
return s:marks[a:mark]
endif
endfunction
set usermarkfunc=UserMarkFunc
of course the user could make the behavior be whatever.
It should also be noted that any valid unicode character can now be a
mark. It is not just limited to ASCII characters.
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 8 | ||||
-rw-r--r-- | src/nvim/map.c | 1 | ||||
-rw-r--r-- | src/nvim/map.h | 1 | ||||
-rw-r--r-- | src/nvim/mark.c | 159 | ||||
-rw-r--r-- | src/nvim/option.c | 5 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 7 |
8 files changed, 178 insertions, 5 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3ef5ea7e02..9457051947 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -712,6 +712,7 @@ struct file_buffer { char_u *b_p_cfu; ///< 'completefunc' char_u *b_p_ofu; ///< 'omnifunc' char_u *b_p_tfu; ///< 'tagfunc' + char_u *b_p_umf; ///< 'usermarkfunc' int b_p_eol; ///< 'endofline' int b_p_fixeol; ///< 'fixendofline' int b_p_et; ///< 'expandtab' diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4ac9847e53..0dde82c235 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4017,7 +4017,7 @@ char *skip_range(const char *cmd, int *ctx) } } if (*cmd != NUL) { - ++cmd; + cmd += utf_ptr2len(cmd); } } @@ -4162,13 +4162,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 d3058a5d52..907c1d5996 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -163,6 +163,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 845daac3f7..92c9ebcc34 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -34,6 +34,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 1fe3327b29..344d55a473 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -17,11 +17,13 @@ #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -39,6 +41,8 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/vim.h" +#include "nvim/map.h" +#include "nvim/eval/userfunc.h" /* * This file contains routines to maintain and manipulate marks. @@ -55,6 +59,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 @@ -164,7 +194,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); } /* @@ -349,6 +380,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; @@ -437,6 +477,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.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 12c5889703..4958c10220 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -142,6 +142,7 @@ static char_u *p_cpt; static char_u *p_cfu; static char_u *p_ofu; static char_u *p_tfu; +static char_u *p_umf; static int p_eol; static int p_fixeol; static int p_et; @@ -6273,6 +6274,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: @@ -6596,6 +6599,8 @@ void buf_copy_options(buf_T *buf, int flags) COPY_OPT_SCTX(buf, BV_OFU); buf->b_p_tfu = vim_strsave(p_tfu); COPY_OPT_SCTX(buf, BV_TFU); + buf->b_p_umf = vim_strsave(p_umf); + COPY_OPT_SCTX(buf, BV_UMF); buf->b_p_sts = p_sts; COPY_OPT_SCTX(buf, BV_STS); buf->b_p_sts_nopaste = p_sts_nopaste; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 237288fbad..92ab7489bb 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -838,6 +838,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 2f941f5d0c..5e4c73a5db 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2691,6 +2691,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'}, |