diff options
author | Josh Rahm <rahm@google.com> | 2022-09-12 10:58:32 -0600 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2022-09-12 10:58:32 -0600 |
commit | aafb4a11225939a04ffa9966e23eb49a4d35c7a9 (patch) | |
tree | 0f2936088a7b03f923e8827b989cc84db79282f9 | |
parent | b29022c9dd6c5abb210b8b29f91b36965bb533db (diff) | |
parent | 4889fe01b9853cf98363ac226f95b524801ff3cc (diff) | |
download | rneovim-aafb4a11225939a04ffa9966e23eb49a4d35c7a9.tar.gz rneovim-aafb4a11225939a04ffa9966e23eb49a4d35c7a9.tar.bz2 rneovim-aafb4a11225939a04ffa9966e23eb49a4d35c7a9.zip |
Merge branch 'usermarks' into mix
-rw-r--r-- | runtime/autoload/usermark.vim | 7 | ||||
-rw-r--r-- | runtime/lua/vim/usermark.lua | 68 | ||||
-rw-r--r-- | runtime/plugin/usermark.vim | 1 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 3 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 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 | 4 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/options.lua | 7 |
13 files changed, 257 insertions, 7 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 0d34200d89..e45addb790 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -672,9 +672,10 @@ struct file_buffer { #ifdef BACKSLASH_IN_FILENAME char *b_p_csl; ///< 'completeslash' #endif + char *b_p_tfu; ///< 'tagfunc' char *b_p_cfu; ///< 'completefunc' char *b_p_ofu; ///< 'omnifunc' - char *b_p_tfu; ///< 'tagfunc' + char *b_p_umf; ///< 'usermarkfunc' int b_p_eol; ///< 'endofline' int b_p_fixeol; ///< 'fixendofline' int b_p_et; ///< 'expandtab' diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ead01fcbad..8969b2d227 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6410,7 +6410,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } } else if (name[0] == '\'') { // mark - int mname = (uint8_t)name[1]; + int mname = utf_ptr2char(name + 1); const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname); if (fm == NULL || fm->mark.lnum <= 0) { return NULL; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4a139c85d1..19e8dda0a7 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3635,6 +3635,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 5defabc05a..2045f5d28e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3144,7 +3144,7 @@ char *skip_range(const char *cmd, int *ctx) } } if (*cmd != NUL) { - cmd++; + cmd += utf_ptr2len(cmd); } } @@ -3287,13 +3287,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 1561b089a7..d62eb94109 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 f5f30f5a85..8b950300b3 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 6f8aecb3ff..049a3cb50b 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -15,10 +15,12 @@ #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/fold.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -36,6 +38,8 @@ #include "nvim/textobject.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. @@ -48,6 +52,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 @@ -154,7 +184,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 @@ -335,6 +366,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; @@ -423,6 +463,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 ab4db30aaf..540d3b714e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4119,6 +4119,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: @@ -4458,6 +4460,8 @@ void buf_copy_options(buf_T *buf, int flags) COPY_OPT_SCTX(buf, BV_OFU); buf->b_p_tfu = xstrdup(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 5dabc92bfc..fb7bd8588f 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -726,6 +726,7 @@ EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter' #define TPF_DEL 0x010 #define TPF_C0 0x020 #define TPF_C1 0x040 +EXTERN char *p_umf; ///< 'usermarkfunc' EXTERN char *p_tfu; ///< 'tagfunc' EXTERN char *p_spc; ///< 'spellcapcheck' EXTERN char *p_spf; ///< 'spellfile' @@ -910,6 +911,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 1d9a821e20..2acc1297b2 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2700,6 +2700,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'}, |