diff options
Diffstat (limited to 'src/nvim/api/private/helpers.c')
-rw-r--r-- | src/nvim/api/private/helpers.c | 148 |
1 files changed, 131 insertions, 17 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ddcfff0097..9f41393c6b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -111,7 +111,7 @@ bool try_leave(const TryState *const tstate, Error *const err) /// try_enter()/try_leave() pair should be used instead. void try_start(void) { - ++trylevel; + trylevel++; } /// End try block, set the error message if any and return true if an error @@ -396,19 +396,14 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object stringval = value.data.string.data; } - const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_sid = - channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; - current_sctx.sc_lnum = 0; - current_channel_id = channel_id; + WITH_SCRIPT_CONTEXT(channel_id, { + const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) + ? 0 : (type == SREQ_GLOBAL) + ? OPT_GLOBAL : OPT_LOCAL; - const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) - ? 0 : (type == SREQ_GLOBAL) - ? OPT_GLOBAL : OPT_LOCAL; - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); - - current_sctx = save_current_sctx; + set_option_value_for(name.data, numval, stringval, + opt_flags, type, to, err); + }); } @@ -591,8 +586,8 @@ Array string_to_array(const String input, bool crlf) /// @param buffer Buffer handle for a specific buffer, or 0 for the current /// buffer, or -1 to signify global behavior ("all buffers") /// @param is_unmap When true, removes the mapping that matches {lhs}. -void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs, - Dict(keymap) *opts, Error *err) +void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs, + String rhs, Dict(keymap) *opts, Error *err) { LuaRef lua_funcref = LUA_NOREF; bool global = (buffer == -1); @@ -605,6 +600,8 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String return; } + const sctx_T save_current_sctx = api_set_sctx(channel_id); + if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) { lua_funcref = opts->callback.data.luaref; opts->callback.data.luaref = LUA_NOREF; @@ -716,6 +713,7 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success fail_and_free: + current_sctx = save_current_sctx; NLUA_CLEAR_REF(parsed_args.rhs_lua); xfree(parsed_args.rhs); xfree(parsed_args.orig_rhs); @@ -758,6 +756,52 @@ bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Arr return true; } +/// Returns a substring of a buffer line +/// +/// @param buf Buffer handle +/// @param lnum Line number (1-based) +/// @param start_col Starting byte offset into line (0-based) +/// @param end_col Ending byte offset into line (0-based, exclusive) +/// @param replace_nl Replace newlines ('\n') with null ('\0') +/// @param err Error object +/// @return The text between start_col and end_col on line lnum of buffer buf +String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, bool replace_nl, + Error *err) +{ + String rv = STRING_INIT; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Line index is too high"); + return rv; + } + + const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false); + size_t line_length = strlen(bufstr); + + start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col; + end_col = end_col < 0 ? (int64_t)line_length + end_col + 1 : end_col; + + if (start_col >= MAXCOL || end_col >= MAXCOL) { + api_set_error(err, kErrorTypeValidation, "Column index is too high"); + return rv; + } + + if (start_col > end_col) { + api_set_error(err, kErrorTypeValidation, "start_col must be less than end_col"); + return rv; + } + + if ((size_t)start_col >= line_length) { + return rv; + } + + rv = cstrn_to_string(&bufstr[start_col], (size_t)(end_col - start_col)); + if (replace_nl) { + strchrsub(rv.data, '\n', '\0'); + } + + return rv; +} void api_free_string(String value) { @@ -993,8 +1037,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt aco_save_T aco; try_start(); - switch (opt_type) - { + switch (opt_type) { case SREQ_WIN: if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) == FAIL) { @@ -1384,6 +1427,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + if (!uc_validate_name(name.data)) { + api_set_error(err, kErrorTypeValidation, "Invalid command name"); + goto err; + } + if (mb_islower(name.data[0])) { api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); goto err; @@ -1564,3 +1612,69 @@ err: NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); } + +int find_sid(uint64_t channel_id) +{ + switch (channel_id) { + case VIML_INTERNAL_CALL: + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; + case LUA_INTERNAL_CALL: + return SID_LUA; + default: + return SID_API_CLIENT; + } +} + +/// Sets sctx for API calls. +/// +/// @param channel_id api clients id. Used to determine if it's a internal +/// call or a rpc call. +/// @return returns previous value of current_sctx. To be used +/// to be used for restoring sctx to previous state. +sctx_T api_set_sctx(uint64_t channel_id) +{ + sctx_T old_current_sctx = current_sctx; + if (channel_id != VIML_INTERNAL_CALL) { + current_sctx.sc_sid = + channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; + current_sctx.sc_lnum = 0; + } + return old_current_sctx; +} + +// adapted from sign.c:sign_define_init_text. +// TODO(lewis6991): Consider merging +int init_sign_text(char_u **sign_text, char_u *text) +{ + char_u *s; + + char_u *endp = text + (int)STRLEN(text); + + // Count cells and check for non-printable chars + int cells = 0; + for (s = text; s < endp; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { + return FAIL; + } + if (cells < 1) { + return OK; + } + + // Allocate one byte more if we need to pad up + // with a space. + size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); + *sign_text = vim_strnsave(text, len); + + if (cells == 1) { + STRCPY(*sign_text + len - 1, " "); + } + + return OK; +} |