aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api/buffer.c')
-rw-r--r--src/nvim/api/buffer.c407
1 files changed, 189 insertions, 218 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 2d5403d4b8..806b649ce6 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -22,6 +22,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -35,7 +36,6 @@
# include "api/buffer.c.generated.h"
#endif
-
/// \defgroup api-buffer
///
/// \brief For more information on buffers, see |buffers|
@@ -51,8 +51,7 @@
/// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check
/// whether a buffer is loaded.
-
-/// Gets the buffer line count
+/// Returns the number of lines in the given buffer.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
@@ -133,7 +132,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - buffer handle
/// - on_reload: Lua callback invoked on reload. The entire buffer
/// content should be considered changed. Args:
-/// - the string "detach"
+/// - the string "reload"
/// - buffer handle
/// - utf_sizes: include UTF-32 and UTF-16 size of the replaced
/// region, as args to `on_lines`.
@@ -247,7 +246,7 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *e
return;
}
- redraw_buf_range_later(buf, (linenr_T)first+1, (linenr_T)last);
+ redraw_buf_range_later(buf, (linenr_T)first + 1, (linenr_T)last);
}
/// Gets a line-range from the buffer.
@@ -262,7 +261,7 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *e
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
-/// @param end Last line index (exclusive)
+/// @param end Last line index, exclusive
/// @param strict_indexing Whether out-of-bounds should be an error.
/// @param[out] err Error details, if any
/// @return Array of lines, or empty array for unloaded buffer.
@@ -287,8 +286,8 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
}
bool oob = false;
- start = normalize_index(buf, start, &oob);
- end = normalize_index(buf, end, &oob);
+ start = normalize_index(buf, start, true, &oob);
+ end = normalize_index(buf, end, true, &oob);
if (strict_indexing && oob) {
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
@@ -358,7 +357,7 @@ static bool check_string_array(Array arr, bool disallow_nl, Error *err)
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
-/// @param end Last line index (exclusive)
+/// @param end Last line index, exclusive
/// @param strict_indexing Whether out-of-bounds should be an error.
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
@@ -374,15 +373,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
}
bool oob = false;
- start = normalize_index(buf, start, &oob);
- end = normalize_index(buf, end, &oob);
+ start = normalize_index(buf, start, true, &oob);
+ end = normalize_index(buf, end, true, &oob);
if (strict_indexing && oob) {
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
return;
}
-
if (start > end) {
api_set_error(err,
kErrorTypeValidation,
@@ -423,7 +421,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- bcount_t deleted_bytes = get_region_bytecount(curbuf, start, end, 0, 0);
+ bcount_t deleted_bytes = get_region_bytecount(curbuf, (linenr_T)start, (linenr_T)end, 0, 0);
// If the size of the range is reducing (ie, new_len < old_len) we
// need to delete some old_len. We do this at the start, by
@@ -453,7 +451,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) {
+ if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
@@ -473,7 +471,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) {
+ if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to insert line");
goto end;
}
@@ -493,14 +491,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
mark_adjust((linenr_T)start,
(linenr_T)(end - 1),
MAXLNUM,
- (long)extra,
+ (linenr_T)extra,
kExtmarkNOOP);
- extmark_splice(curbuf, (int)start-1, 0, (int)(end-start), 0,
+ extmark_splice(curbuf, (int)start - 1, 0, (int)(end - start), 0,
deleted_bytes, (int)new_len, 0, inserted_bytes,
kExtmarkUndo);
- changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
+ changed_lines((linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
end:
@@ -515,24 +513,25 @@ end:
/// Sets (replaces) a range in the buffer
///
-/// This is recommended over nvim_buf_set_lines when only modifying parts of a
-/// line, as extmarks will be preserved on non-modified parts of the touched
+/// This is recommended over |nvim_buf_set_lines()| when only modifying parts of
+/// a line, as extmarks will be preserved on non-modified parts of the touched
/// lines.
///
-/// Indexing is zero-based and end-exclusive.
+/// Indexing is zero-based. Row indices are end-inclusive, and column indices
+/// are end-exclusive.
///
-/// To insert text at a given index, set `start` and `end` ranges to the same
-/// index. To delete a range, set `replacement` to an array containing
-/// an empty string, or simply an empty array.
+/// To insert text at a given `(row, column)` location, use `start_row = end_row
+/// = row` and `start_col = end_col = col`. To delete the text in a range, use
+/// `replacement = {}`.
///
-/// Prefer nvim_buf_set_lines when adding or deleting entire lines only.
+/// Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire lines.
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start_row First line index
-/// @param start_column First column
-/// @param end_row Last line index
-/// @param end_column Last column
+/// @param start_col Starting column (byte offset) on first line
+/// @param end_row Last line index, inclusive
+/// @param end_col Ending column (byte offset) on last line, exclusive
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col,
@@ -554,25 +553,25 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
// check range is ordered and everything!
// start_row, end_row within buffer len (except add text past the end?)
- start_row = normalize_index(buf, start_row, &oob);
- if (oob || start_row == buf->b_ml.ml_line_count + 1) {
+ start_row = normalize_index(buf, start_row, false, &oob);
+ if (oob) {
api_set_error(err, kErrorTypeValidation, "start_row out of bounds");
return;
}
- end_row = normalize_index(buf, end_row, &oob);
- if (oob || end_row == buf->b_ml.ml_line_count + 1) {
+ end_row = normalize_index(buf, end_row, false, &oob);
+ if (oob) {
api_set_error(err, kErrorTypeValidation, "end_row out of bounds");
return;
}
- char *str_at_start = (char *)ml_get_buf(buf, start_row, false);
+ char *str_at_start = (char *)ml_get_buf(buf, (linenr_T)start_row, false);
if (start_col < 0 || (size_t)start_col > strlen(str_at_start)) {
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
return;
}
- char *str_at_end = (char *)ml_get_buf(buf, end_row, false);
+ char *str_at_end = (char *)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");
@@ -598,21 +597,20 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
if (start_row == end_row) {
old_byte = (bcount_t)end_col - start_col;
} else {
- const char *bufline;
old_byte += (bcount_t)strlen(str_at_start) - start_col;
for (int64_t i = 1; i < end_row - start_row; i++) {
int64_t lnum = start_row + i;
- bufline = (char *)ml_get_buf(buf, lnum, false);
- old_byte += (bcount_t)(strlen(bufline))+1;
+ const char *bufline = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ old_byte += (bcount_t)(strlen(bufline)) + 1;
}
- old_byte += (bcount_t)end_col+1;
+ old_byte += (bcount_t)end_col + 1;
}
String first_item = replacement.items[0].data.string;
- String last_item = replacement.items[replacement.size-1].data.string;
+ String last_item = replacement.items[replacement.size - 1].data.string;
- size_t firstlen = (size_t)start_col+first_item.size;
+ size_t firstlen = (size_t)start_col + first_item.size;
size_t last_part_len = strlen(str_at_end) - (size_t)end_col;
if (replacement.size == 1) {
firstlen += last_part_len;
@@ -620,32 +618,32 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
char *first = xmallocz(firstlen);
char *last = NULL;
memcpy(first, str_at_start, (size_t)start_col);
- memcpy(first+start_col, first_item.data, first_item.size);
- memchrsub(first+start_col, NUL, NL, first_item.size);
+ memcpy(first + start_col, first_item.data, first_item.size);
+ memchrsub(first + start_col, NUL, NL, first_item.size);
if (replacement.size == 1) {
- memcpy(first+start_col+first_item.size, str_at_end+end_col, last_part_len);
+ memcpy(first + start_col + first_item.size, str_at_end + end_col, last_part_len);
} else {
- last = xmallocz(last_item.size+last_part_len);
+ last = xmallocz(last_item.size + last_part_len);
memcpy(last, last_item.data, last_item.size);
memchrsub(last, NUL, NL, last_item.size);
- memcpy(last+last_item.size, str_at_end+end_col, last_part_len);
+ memcpy(last + last_item.size, str_at_end + end_col, last_part_len);
}
char **lines = xcalloc(new_len, sizeof(char *));
lines[0] = first;
new_byte += (bcount_t)(first_item.size);
- for (size_t i = 1; i < new_len-1; i++) {
+ for (size_t i = 1; i < new_len - 1; i++) {
const String l = replacement.items[i].data.string;
// Fill lines[i] with l's contents. Convert NULs to newlines as required by
// NL-used-for-NUL.
lines[i] = xmemdupz(l.data, l.size);
memchrsub(lines[i], NUL, NL, l.size);
- new_byte += (bcount_t)(l.size)+1;
+ new_byte += (bcount_t)(l.size) + 1;
}
if (replacement.size > 1) {
- lines[replacement.size-1] = last;
- new_byte += (bcount_t)(last_item.size)+1;
+ lines[replacement.size - 1] = last;
+ new_byte += (bcount_t)(last_item.size) + 1;
}
try_start();
@@ -665,7 +663,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
}
ptrdiff_t extra = 0; // lines added to text, can be negative
- size_t old_len = (size_t)(end_row-start_row+1);
+ size_t old_len = (size_t)(end_row - start_row + 1);
// If the size of the range is reducing (ie, new_len < old_len) we
// need to delete some old_len. We do this at the start, by
@@ -694,7 +692,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
}
- if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) {
+ if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
@@ -712,7 +710,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
}
- if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) {
+ if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to insert line");
goto end;
}
@@ -728,19 +726,17 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
mark_adjust((linenr_T)start_row,
(linenr_T)end_row,
MAXLNUM,
- (long)extra,
+ (linenr_T)extra,
kExtmarkNOOP);
colnr_T col_extent = (colnr_T)(end_col
- ((end_row == start_row) ? start_col : 0));
- extmark_splice(buf, (int)start_row-1, (colnr_T)start_col,
- (int)(end_row-start_row), col_extent, old_byte,
- (int)new_len-1, (colnr_T)last_item.size, new_byte,
+ extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
+ (int)(end_row - start_row), col_extent, old_byte,
+ (int)new_len - 1, (colnr_T)last_item.size, new_byte,
kExtmarkUndo);
-
- changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1,
- (long)extra, true);
+ changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
// adjust cursor like an extmark ( i e it was inside last_part_len)
if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) {
@@ -757,6 +753,109 @@ end:
try_end(err);
}
+/// Gets a range from the buffer.
+///
+/// This differs from |nvim_buf_get_lines()| in that it allows retrieving only
+/// portions of a line.
+///
+/// Indexing is zero-based. Row indices are end-inclusive, and column indices
+/// are end-exclusive.
+///
+/// Prefer |nvim_buf_get_lines()| when retrieving entire lines.
+///
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param start_row First line index
+/// @param start_col Starting column (byte offset) on first line
+/// @param end_row Last line index, inclusive
+/// @param end_col Ending column (byte offset) on last line, exclusive
+/// @param opts Optional parameters. Currently unused.
+/// @param[out] err Error details, if any
+/// @return Array of lines, or empty array for unloaded buffer.
+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)
+ FUNC_API_SINCE(9)
+{
+ Array rv = ARRAY_DICT_INIT;
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return rv;
+ }
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return rv;
+ }
+
+ // return sentinel value if the buffer isn't loaded
+ if (buf->b_ml.ml_mfp == NULL) {
+ return rv;
+ }
+
+ bool oob = false;
+ start_row = normalize_index(buf, start_row, false, &oob);
+ end_row = normalize_index(buf, end_row, false, &oob);
+
+ if (oob) {
+ api_set_error(err, kErrorTypeValidation, "Index out of bounds");
+ return rv;
+ }
+
+ // nvim_buf_get_lines doesn't care if the start row is greater than the end
+ // row (it will just return an empty array), but nvim_buf_get_text does in
+ // order to maintain symmetry with nvim_buf_set_text.
+ if (start_row > end_row) {
+ api_set_error(err, kErrorTypeValidation, "start is higher than end");
+ return rv;
+ }
+
+ bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
+
+ if (start_row == end_row) {
+ String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err);
+ if (ERROR_SET(err)) {
+ return rv;
+ }
+
+ ADD(rv, STRING_OBJ(line));
+ return rv;
+ }
+
+ rv.size = (size_t)(end_row - start_row) + 1;
+ rv.items = xcalloc(rv.size, sizeof(Object));
+
+ 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)) {
+ goto end;
+ }
+ }
+
+ rv.items[rv.size - 1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err));
+ if (ERROR_SET(err)) {
+ goto end;
+ }
+
+end:
+ if (ERROR_SET(err)) {
+ api_free_array(rv);
+ rv.size = 0;
+ rv.items = NULL;
+ }
+
+ return rv;
+}
+
/// Returns the byte offset of a line (0-indexed). |api-indexing|
///
/// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte.
@@ -789,7 +888,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
return 0;
}
- return ml_find_line_or_offset(buf, (int)index+1, NULL, true);
+ return ml_find_line_or_offset(buf, (int)index + 1, NULL, true);
}
/// Gets a buffer-scoped (b:) variable.
@@ -852,11 +951,11 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, Stri
/// @see |nvim_set_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
-void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts,
- Error *err)
+void nvim_buf_set_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, String rhs,
+ Dict(keymap) *opts, Error *err)
FUNC_API_SINCE(6)
{
- modify_keymap(buffer, false, mode, lhs, rhs, opts, err);
+ modify_keymap(channel_id, buffer, false, mode, lhs, rhs, opts, err);
}
/// Unmaps a buffer-local |mapping| for the given mode.
@@ -864,42 +963,11 @@ void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dic
/// @see |nvim_del_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
-void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
+void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, Error *err)
FUNC_API_SINCE(6)
{
String rhs = { .data = "", .size = 0 };
- modify_keymap(buffer, true, mode, lhs, rhs, NULL, err);
-}
-
-/// Gets a map of buffer-local |user-commands|.
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param opts Optional parameters. Currently not used.
-/// @param[out] err Error details, if any.
-///
-/// @returns Map of maps describing commands.
-Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
- FUNC_API_SINCE(4)
-{
- bool global = (buffer == -1);
- bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
- if (ERROR_SET(err)) {
- return (Dictionary)ARRAY_DICT_INIT;
- }
-
- if (global) {
- if (builtin) {
- api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
- return (Dictionary)ARRAY_DICT_INIT;
- }
- return commands_array(NULL);
- }
-
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (builtin || !buf) {
- return (Dictionary)ARRAY_DICT_INIT;
- }
- return commands_array(buf);
+ modify_keymap(channel_id, buffer, true, mode, lhs, rhs, NULL, err);
}
/// Sets a buffer-scoped (b:) variable
@@ -937,45 +1005,6 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
dict_set_var(buf->b_vars, name, NIL, true, false, err);
}
-
-/// Gets a buffer option value
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param name Option name
-/// @param[out] err Error details, if any
-/// @return Option value
-Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
-
- if (!buf) {
- return (Object)OBJECT_INIT;
- }
-
- return get_option_from(buf, SREQ_BUF, name, err);
-}
-
-/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
-/// works if there's a global fallback)
-///
-/// @param channel_id
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param name Option name
-/// @param value Option value
-/// @param[out] err Error details, if any
-void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
-
- if (!buf) {
- return;
- }
-
- set_option_to(channel_id, buf, SREQ_BUF, name, value, err);
-}
-
/// Gets the full file name for the buffer
///
/// @param buffer Buffer handle, or 0 for current buffer
@@ -1013,7 +1042,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
// Using aucmd_*: autocommands will be executed by rename_buffer
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- int ren_ret = rename_buffer((char_u *)name.data);
+ int ren_ret = rename_buffer(name.data);
aucmd_restbuf(&aco);
if (try_end(err)) {
@@ -1127,17 +1156,17 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
return res;
}
- pos_T *pos = getmark_buf(buf, *name.data, false);
+ fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
- // pos point to NULL when there's no mark with name
- if (pos == NULL) {
+ // fm is NULL when there's no mark with the given name
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
*name.data);
return res;
}
- // pos->lnum is 0 when the mark is not valid in the buffer, or is not set.
- if (pos->lnum != 0) {
+ // mark.lnum is 0 when the mark is not valid in the buffer, or is not set.
+ if (fm->mark.lnum != 0 && fm->fnum == buf->handle) {
// since the mark belongs to the buffer delete it.
res = set_mark(buf, name, 0, 0, err);
}
@@ -1210,31 +1239,29 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
- pos_T *posp;
+ fmark_T *fm;
+ pos_T pos;
char mark = *name.data;
- try_start();
- bufref_T save_buf;
- switch_buffer(&save_buf, buf);
- posp = getmark(mark, false);
- restore_buffer(&save_buf);
-
- if (try_end(err)) {
- return rv;
- }
-
- if (posp == NULL) {
+ fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name");
return rv;
}
+ // (0, 0) uppercase/file mark set in another buffer.
+ if (fm->fnum != buf->handle) {
+ pos.lnum = 0;
+ pos.col = 0;
+ } else {
+ pos = fm->mark;
+ }
- ADD(rv, INTEGER_OBJ(posp->lnum));
- ADD(rv, INTEGER_OBJ(posp->col));
+ ADD(rv, INTEGER_OBJ(pos.lnum));
+ ADD(rv, INTEGER_OBJ(pos.col));
return rv;
}
-
/// call a function with buffer as temporary current buffer
///
/// This temporarily switches current buffer to "buffer".
@@ -1273,63 +1300,6 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
return res;
}
-/// Create a new user command |user-commands| in the given buffer.
-///
-/// @param buffer Buffer handle, or 0 for current buffer.
-/// @param[out] err Error details, if any.
-/// @see nvim_add_user_command
-void nvim_buf_add_user_command(Buffer buffer, String name, Object command,
- Dict(user_command) *opts, Error *err)
- FUNC_API_SINCE(9)
-{
- buf_T *target_buf = find_buffer_by_handle(buffer, err);
- if (ERROR_SET(err)) {
- return;
- }
-
- buf_T *save_curbuf = curbuf;
- curbuf = target_buf;
- add_user_command(name, command, opts, UC_BUFFER, err);
- curbuf = save_curbuf;
-}
-
-/// Delete a buffer-local user-defined command.
-///
-/// Only commands created with |:command-buffer| or
-/// |nvim_buf_add_user_command()| can be deleted with this function.
-///
-/// @param buffer Buffer handle, or 0 for current buffer.
-/// @param name Name of the command to delete.
-/// @param[out] err Error details, if any.
-void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
- FUNC_API_SINCE(9)
-{
- garray_T *gap;
- if (buffer == -1) {
- gap = &ucmds;
- } else {
- buf_T *buf = find_buffer_by_handle(buffer, err);
- gap = &buf->b_ucmds;
- }
-
- for (int i = 0; i < gap->ga_len; i++) {
- ucmd_T *cmd = USER_CMD_GA(gap, i);
- if (!STRCMP(name.data, cmd->uc_name)) {
- free_ucmd(cmd);
-
- gap->ga_len -= 1;
-
- if (i < gap->ga_len) {
- memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
- }
-
- return;
- }
- }
-
- api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
-}
-
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;
@@ -1386,16 +1356,17 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
}
// Normalizes 0-based indexes to buffer line numbers
-static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
+static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bool *oob)
{
- int64_t line_count = buf->b_ml.ml_line_count;
+ assert(buf->b_ml.ml_line_count > 0);
+ int64_t max_index = buf->b_ml.ml_line_count + (int)end_exclusive - 1;
// Fix if < 0
- index = index < 0 ? line_count + index +1 : index;
+ index = index < 0 ? max_index + index + 1 : index;
// Check for oob
- if (index > line_count) {
+ if (index > max_index) {
*oob = true;
- index = line_count;
+ index = max_index;
} else if (index < 0) {
*oob = true;
index = 0;