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:
Björn Linse 2016-01-23 20:30:26 +01:00
parent b8643f69c1
commit 51c7818d42
2 changed files with 108 additions and 29 deletions

View File

@ -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;
}

View File

@ -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()