aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/buffer.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/api/buffer.c
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-usermarks.tar.gz
rneovim-usermarks.tar.bz2
rneovim-usermarks.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/api/buffer.c')
-rw-r--r--src/nvim/api/buffer.c171
1 files changed, 136 insertions, 35 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 5e90e40dd3..fe9e6077d6 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -3,25 +3,30 @@
// Some of this code was adapted from 'if_py_both.h' from the original
// vim source
+
+#include <assert.h>
#include <lauxlib.h>
-#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
+#include "lua.h"
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
+#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
@@ -29,9 +34,10 @@
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/ops.h"
+#include "nvim/pos.h"
+#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/buffer.c.generated.h"
@@ -78,7 +84,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
///
/// Example (Lua): capture buffer updates in a global `events` variable
/// (use "print(vim.inspect(events))" to see its contents):
-/// <pre>
+/// <pre>lua
/// events = {}
/// vim.api.nvim_buf_attach(0, false, {
/// on_lines=function(...) table.insert(events, {...}) end})
@@ -271,6 +277,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
Integer start,
Integer end,
Boolean strict_indexing,
+ lua_State *lstate,
Error *err)
FUNC_API_SINCE(1)
{
@@ -300,21 +307,18 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
return rv;
}
- rv.size = (size_t)(end - start);
- rv.items = xcalloc(rv.size, sizeof(Object));
+ size_t size = (size_t)(end - start);
+
+ init_line_array(lstate, &rv, size);
- if (!buf_collect_lines(buf, rv.size, start,
- (channel_id != VIML_INTERNAL_CALL), &rv, err)) {
+ if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv,
+ lstate, err)) {
goto end;
}
end:
if (ERROR_SET(err)) {
- for (size_t i = 0; i < rv.size; i++) {
- xfree(rv.items[i].data.string.data);
- }
-
- xfree(rv.items);
+ api_free_array(rv);
rv.items = NULL;
}
@@ -355,6 +359,8 @@ static bool check_string_array(Array arr, bool disallow_nl, Error *err)
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
+/// @see |nvim_buf_set_text()|
+///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
@@ -527,6 +533,8 @@ end:
///
/// Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire lines.
///
+/// @see |nvim_buf_set_lines()|
+///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start_row First line index
@@ -570,7 +578,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
char *str_at_end = NULL;
// Another call to ml_get_buf() may free the line, so make a copy.
- str_at_start = xstrdup((char *)ml_get_buf(buf, (linenr_T)start_row, false));
+ str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
size_t len_at_start = strlen(str_at_start);
if (start_col < 0 || (size_t)start_col > len_at_start) {
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
@@ -578,7 +586,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
}
// Another call to ml_get_buf() may free the line, so make a copy.
- str_at_end = xstrdup((char *)ml_get_buf(buf, (linenr_T)end_row, false));
+ str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row, false));
size_t len_at_end = strlen(str_at_end);
if (end_col < 0 || (size_t)end_col > len_at_end) {
api_set_error(err, kErrorTypeValidation, "end_col out of bounds");
@@ -608,7 +616,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
for (int64_t i = 1; i < end_row - start_row; i++) {
int64_t lnum = start_row + i;
- const char *bufline = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ const char *bufline = ml_get_buf(buf, (linenr_T)lnum, false);
old_byte += (bcount_t)(strlen(bufline)) + 1;
}
old_byte += (bcount_t)end_col + 1;
@@ -786,7 +794,8 @@ early_end:
ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
Integer start_row, Integer start_col,
Integer end_row, Integer end_col,
- Dictionary opts, Error *err)
+ Dictionary opts, lua_State *lstate,
+ Error *err)
FUNC_API_SINCE(9)
{
Array rv = ARRAY_DICT_INIT;
@@ -826,33 +835,37 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
+ size_t size = (size_t)(end_row - start_row) + 1;
+
+ init_line_array(lstate, &rv, size);
+
if (start_row == end_row) {
- String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err);
+ String line = buf_get_text(buf, start_row, start_col, end_col, err);
if (ERROR_SET(err)) {
- return rv;
+ goto end;
}
-
- ADD(rv, STRING_OBJ(line));
+ push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl);
return rv;
}
- rv.size = (size_t)(end_row - start_row) + 1;
- rv.items = xcalloc(rv.size, sizeof(Object));
+ String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err);
+
+ push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl);
- rv.items[0] = STRING_OBJ(buf_get_text(buf, start_row, start_col, MAXCOL - 1, replace_nl, err));
if (ERROR_SET(err)) {
goto end;
}
- if (rv.size > 2) {
- Array tmp = ARRAY_DICT_INIT;
- tmp.items = &rv.items[1];
- if (!buf_collect_lines(buf, rv.size - 2, start_row + 1, replace_nl, &tmp, err)) {
+ if (size > 2) {
+ if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate,
+ err)) {
goto end;
}
}
- rv.items[rv.size - 1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err));
+ str = buf_get_text(buf, end_row, 0, end_col, err);
+ push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl);
+
if (ERROR_SET(err)) {
goto end;
}
@@ -945,7 +958,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// @param[out] err Error details, if any
/// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
-ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err)
+ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
FUNC_API_SINCE(3)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -954,7 +967,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, Stri
return (Array)ARRAY_DICT_INIT;
}
- return keymap_array(mode, buf, channel_id == LUA_INTERNAL_CALL);
+ return keymap_array(mode, buf);
}
/// Sets a buffer-local |mapping| for the given mode.
@@ -1021,7 +1034,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
-String nvim_buf_get_name(Buffer buffer, Error *err)
+String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
String rv = STRING_INIT;
@@ -1031,7 +1044,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
return rv;
}
- return cstr_to_string((char *)buf->b_ffname);
+ return cstr_as_string(buf->b_ffname);
}
/// Sets the full file name for a buffer
@@ -1386,3 +1399,91 @@ static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bo
index++;
return index;
}
+
+/// Initialise a string array either:
+/// - on the Lua stack (as a table) (if lstate is not NULL)
+/// - as an API array object (if lstate is NULL).
+///
+/// @param lstate Lua state. When NULL the Array is initialized instead.
+/// @param a Array to initialize
+/// @param size Size of array
+static inline void init_line_array(lua_State *lstate, Array *a, size_t size)
+{
+ if (lstate) {
+ lua_createtable(lstate, (int)size, 0);
+ } else {
+ a->size = size;
+ a->items = xcalloc(a->size, sizeof(Object));
+ }
+}
+
+/// Push a string onto either the Lua stack (as a table element) or an API array object.
+///
+/// For Lua, a table of the correct size must be created first.
+/// API array objects must be pre allocated.
+///
+/// @param lstate Lua state. When NULL the Array is pushed to instead.
+/// @param a Array to push onto when not using Lua
+/// @param s String to push
+/// @param len Size of string
+/// @param idx 0-based index to place s
+/// @param replace_nl Replace newlines ('\n') with null ('\0')
+static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx,
+ bool replace_nl)
+{
+ if (lstate) {
+ // Vim represents NULs as NLs
+ if (s && replace_nl && strchr(s, '\n')) {
+ char *tmp = xmemdupz(s, len);
+ strchrsub(tmp, '\n', '\0');
+ lua_pushlstring(lstate, tmp, len);
+ xfree(tmp);
+ } else {
+ lua_pushlstring(lstate, s, len);
+ }
+ lua_rawseti(lstate, -2, idx + 1);
+ } else {
+ String str = STRING_INIT;
+ if (s) {
+ str = cbuf_to_string(s, len);
+ if (replace_nl) {
+ // Vim represents NULs as NLs, but this may confuse clients.
+ strchrsub(str.data, '\n', '\0');
+ }
+ }
+
+ a->items[idx] = STRING_OBJ(str);
+ }
+}
+
+/// Collects `n` buffer lines into array `l` and/or lua_State `lstate`, optionally replacing
+/// newlines with NUL.
+///
+/// @param buf Buffer to get lines from
+/// @param n Number of lines to collect
+/// @param replace_nl Replace newlines ("\n") with NUL
+/// @param start Line number to start from
+/// @param start_idx First index to push to
+/// @param[out] l If not NULL, Lines are copied here
+/// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack
+/// @param err[out] Error, if any
+/// @return true unless `err` was set
+bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl,
+ Array *l, lua_State *lstate, Error *err)
+{
+ for (size_t i = 0; i < n; i++) {
+ linenr_T lnum = start + (linenr_T)i;
+
+ if (lnum >= MAXLNUM) {
+ if (err != NULL) {
+ api_set_error(err, kErrorTypeValidation, "Line index is too high");
+ }
+ return false;
+ }
+
+ char *bufstr = ml_get_buf(buf, lnum, false);
+ push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl);
+ }
+
+ return true;
+}