aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/private/helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api/private/helpers.c')
-rw-r--r--src/nvim/api/private/helpers.c148
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;
+}