mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
api/buffer: introduce buffer_[gs]et_lines with new indexing convention.
-1 is index past the end, and -2 is the index of the last element. This eliminates the need for include_start/include_end. Allow the handling of out-of-bounds to be configurable.
This commit is contained in:
parent
b8643f69c1
commit
51c7818d42
@ -51,8 +51,10 @@ Integer buffer_line_count(Buffer buffer, Error *err)
|
||||
/// @return The line string
|
||||
String buffer_get_line(Buffer buffer, Integer index, Error *err)
|
||||
{
|
||||
String rv = {.size = 0};
|
||||
Array slice = buffer_get_line_slice(buffer, index, index, true, true, err);
|
||||
String rv = { .size = 0 };
|
||||
|
||||
index = convert_index(index);
|
||||
Array slice = buffer_get_lines(buffer, index, index+1, true, err);
|
||||
|
||||
if (!err->set && slice.size) {
|
||||
rv = slice.items[0].data.string;
|
||||
@ -72,8 +74,9 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
|
||||
void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
|
||||
{
|
||||
Object l = STRING_OBJ(line);
|
||||
Array array = {.items = &l, .size = 1};
|
||||
buffer_set_line_slice(buffer, index, index, true, true, array, err);
|
||||
Array array = { .items = &l, .size = 1 };
|
||||
index = convert_index(index);
|
||||
buffer_set_lines(buffer, index, index+1, true, array, err);
|
||||
}
|
||||
|
||||
/// Deletes a buffer line
|
||||
@ -84,7 +87,8 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
|
||||
void buffer_del_line(Buffer buffer, Integer index, Error *err)
|
||||
{
|
||||
Array array = ARRAY_DICT_INIT;
|
||||
buffer_set_line_slice(buffer, index, index, true, true, array, err);
|
||||
index = convert_index(index);
|
||||
buffer_set_lines(buffer, index, index+1, true, array, err);
|
||||
}
|
||||
|
||||
/// Retrieves a line range from the buffer
|
||||
@ -102,17 +106,49 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
|
||||
Boolean include_start,
|
||||
Boolean include_end,
|
||||
Error *err)
|
||||
{
|
||||
start = convert_index(start) + !include_start;
|
||||
end = convert_index(end) + include_end;
|
||||
return buffer_get_lines(buffer, start , end, false, err);
|
||||
}
|
||||
|
||||
|
||||
/// Retrieves a line range from the buffer
|
||||
///
|
||||
/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
|
||||
/// as length+1+index, i e -1 refers to the index past the end. So to get the
|
||||
/// last element set start=-2 and end=-1.
|
||||
///
|
||||
/// Out-of-bounds indices are clamped to the nearest valid value, unless
|
||||
/// `strict_indexing` is set.
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param start The first line index
|
||||
/// @param end The last line index (exclusive)
|
||||
/// @param strict_indexing whether out-of-bounds should be an error.
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
/// @return An array of lines
|
||||
ArrayOf(String) buffer_get_lines(Buffer buffer,
|
||||
Integer start,
|
||||
Integer end,
|
||||
Boolean strict_indexing,
|
||||
Error *err)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
if (!buf || !inbounds(buf, start)) {
|
||||
if (!buf) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
start = normalize_index(buf, start) + (include_start ? 0 : 1);
|
||||
include_end = include_end || (end >= buf->b_ml.ml_line_count);
|
||||
end = normalize_index(buf, end) + (include_end ? 1 : 0);
|
||||
bool oob = false;
|
||||
start = normalize_index(buf, start, &oob);
|
||||
end = normalize_index(buf, end, &oob);
|
||||
|
||||
if (strict_indexing && oob) {
|
||||
api_set_error(err, Validation, _("Index out of bounds"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (start >= end) {
|
||||
// Return 0-length array
|
||||
@ -152,6 +188,7 @@ end:
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/// Replaces a line range on the buffer
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
@ -169,6 +206,37 @@ void buffer_set_line_slice(Buffer buffer,
|
||||
Boolean include_end,
|
||||
ArrayOf(String) replacement,
|
||||
Error *err)
|
||||
{
|
||||
start = convert_index(start) + !include_start;
|
||||
end = convert_index(end) + include_end;
|
||||
buffer_set_lines(buffer, start, end, false, replacement, err);
|
||||
}
|
||||
|
||||
|
||||
/// Replaces line range on the buffer
|
||||
///
|
||||
/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
|
||||
/// as length+1+index, i e -1 refers to the index past the end. So to change
|
||||
/// or delete the last element set start=-2 and end=-1.
|
||||
///
|
||||
/// To insert lines at a given index, set both start and end to the same index.
|
||||
/// To delete a range of lines, set replacement to an empty array.
|
||||
///
|
||||
/// Out-of-bounds indices are clamped to the nearest valid value, unless
|
||||
/// `strict_indexing` is set.
|
||||
///
|
||||
/// @param buffer The buffer handle
|
||||
/// @param start The first line index
|
||||
/// @param end The last line index (exclusive)
|
||||
/// @param strict_indexing whether out-of-bounds should be an error.
|
||||
/// @param replacement An array of lines to use as replacement
|
||||
/// @param[out] err Details of an error that may have occurred
|
||||
void buffer_set_lines(Buffer buffer,
|
||||
Integer start,
|
||||
Integer end,
|
||||
Boolean strict_indexing,
|
||||
ArrayOf(String) replacement,
|
||||
Error *err)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
|
||||
@ -176,14 +244,15 @@ void buffer_set_line_slice(Buffer buffer,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inbounds(buf, start)) {
|
||||
bool oob = false;
|
||||
start = normalize_index(buf, start, &oob);
|
||||
end = normalize_index(buf, end, &oob);
|
||||
|
||||
if (strict_indexing && oob) {
|
||||
api_set_error(err, Validation, _("Index out of bounds"));
|
||||
return;
|
||||
}
|
||||
|
||||
start = normalize_index(buf, start) + (include_start ? 0 : 1);
|
||||
include_end = include_end || (end >= buf->b_ml.ml_line_count);
|
||||
end = normalize_index(buf, end) + (include_end ? 1 : 0);
|
||||
|
||||
if (start > end) {
|
||||
api_set_error(err,
|
||||
@ -467,8 +536,9 @@ void buffer_insert(Buffer buffer,
|
||||
ArrayOf(String) lines,
|
||||
Error *err)
|
||||
{
|
||||
bool end_start = lnum < 0;
|
||||
buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err);
|
||||
// "lnum" will be the index of the line after inserting,
|
||||
// no matter if it is negative or not
|
||||
buffer_set_lines(buffer, lnum, lnum, true, lines, err);
|
||||
}
|
||||
|
||||
/// Return a tuple (row,col) representing the position of the named mark
|
||||
@ -632,20 +702,26 @@ 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)
|
||||
static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
|
||||
{
|
||||
int64_t line_count = buf->b_ml.ml_line_count;
|
||||
// Fix if < 0
|
||||
index = index < 0 ? buf->b_ml.ml_line_count + index : index;
|
||||
index = index < 0 ? line_count + index +1 : index;
|
||||
|
||||
// Check for oob
|
||||
if (index > line_count) {
|
||||
*oob = true;
|
||||
index = line_count;
|
||||
} else if (index < 0) {
|
||||
*oob = true;
|
||||
index = 0;
|
||||
}
|
||||
// Convert the index to a vim line number
|
||||
index++;
|
||||
// Fix if > line_count
|
||||
index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index;
|
||||
return index;
|
||||
}
|
||||
|
||||
// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds.
|
||||
static bool inbounds(buf_T *buf, int64_t index)
|
||||
static int64_t convert_index(int64_t index)
|
||||
{
|
||||
linenr_T nlines = buf->b_ml.ml_line_count;
|
||||
return index >= -nlines && index < nlines;
|
||||
return index < 0 ? index - 1 : index;
|
||||
}
|
||||
|
@ -35,10 +35,11 @@ describe('buffer_* functions', function()
|
||||
eq('', curbuf('get_line', 0))
|
||||
end)
|
||||
|
||||
it('get_line: out-of-bounds returns empty string', function()
|
||||
it('get_line: out-of-bounds is an error', function()
|
||||
curbuf('set_line', 0, 'line1.a')
|
||||
eq('', curbuf('get_line', 1))
|
||||
eq('', curbuf('get_line', -2))
|
||||
eq(1, curbuf('line_count')) -- sanity
|
||||
eq(false, pcall(curbuf, 'get_line', 1))
|
||||
eq(false, pcall(curbuf, 'get_line', -2))
|
||||
end)
|
||||
|
||||
it('set_line, del_line: out-of-bounds is an error', function()
|
||||
@ -68,14 +69,16 @@ describe('buffer_* functions', function()
|
||||
eq({}, curbuf('get_line_slice', -4, -5, true, true))
|
||||
end)
|
||||
|
||||
it('set_line_slice: out-of-bounds is an error', function()
|
||||
it('set_line_slice: out-of-bounds extends past end', function()
|
||||
curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'})
|
||||
eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity
|
||||
|
||||
eq({'c'}, curbuf('get_line_slice', -1, 4, true, true))
|
||||
eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, true, true))
|
||||
eq(false, pcall(curbuf, 'set_line_slice', 4, 5, true, true, {'d'}))
|
||||
eq(false, pcall(curbuf, 'set_line_slice', -4, -5, true, true, {'d'}))
|
||||
curbuf('set_line_slice', 4, 5, true, true, {'d'})
|
||||
eq({'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true))
|
||||
curbuf('set_line_slice', -4, -5, true, true, {'e'})
|
||||
eq({'e', 'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true))
|
||||
end)
|
||||
|
||||
it('works', function()
|
||||
|
Loading…
Reference in New Issue
Block a user