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
|
/// @return The line string
|
||||||
String buffer_get_line(Buffer buffer, Integer index, Error *err)
|
String buffer_get_line(Buffer buffer, Integer index, Error *err)
|
||||||
{
|
{
|
||||||
String rv = {.size = 0};
|
String rv = { .size = 0 };
|
||||||
Array slice = buffer_get_line_slice(buffer, index, index, true, true, err);
|
|
||||||
|
index = convert_index(index);
|
||||||
|
Array slice = buffer_get_lines(buffer, index, index+1, true, err);
|
||||||
|
|
||||||
if (!err->set && slice.size) {
|
if (!err->set && slice.size) {
|
||||||
rv = slice.items[0].data.string;
|
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)
|
void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
|
||||||
{
|
{
|
||||||
Object l = STRING_OBJ(line);
|
Object l = STRING_OBJ(line);
|
||||||
Array array = {.items = &l, .size = 1};
|
Array array = { .items = &l, .size = 1 };
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a buffer line
|
/// 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)
|
void buffer_del_line(Buffer buffer, Integer index, Error *err)
|
||||||
{
|
{
|
||||||
Array array = ARRAY_DICT_INIT;
|
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
|
/// Retrieves a line range from the buffer
|
||||||
@ -102,17 +106,49 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
|
|||||||
Boolean include_start,
|
Boolean include_start,
|
||||||
Boolean include_end,
|
Boolean include_end,
|
||||||
Error *err)
|
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;
|
Array rv = ARRAY_DICT_INIT;
|
||||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
|
|
||||||
if (!buf || !inbounds(buf, start)) {
|
if (!buf) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
start = normalize_index(buf, start) + (include_start ? 0 : 1);
|
bool oob = false;
|
||||||
include_end = include_end || (end >= buf->b_ml.ml_line_count);
|
start = normalize_index(buf, start, &oob);
|
||||||
end = normalize_index(buf, end) + (include_end ? 1 : 0);
|
end = normalize_index(buf, end, &oob);
|
||||||
|
|
||||||
|
if (strict_indexing && oob) {
|
||||||
|
api_set_error(err, Validation, _("Index out of bounds"));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if (start >= end) {
|
if (start >= end) {
|
||||||
// Return 0-length array
|
// Return 0-length array
|
||||||
@ -152,6 +188,7 @@ end:
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Replaces a line range on the buffer
|
/// Replaces a line range on the buffer
|
||||||
///
|
///
|
||||||
/// @param buffer The buffer handle
|
/// @param buffer The buffer handle
|
||||||
@ -169,6 +206,37 @@ void buffer_set_line_slice(Buffer buffer,
|
|||||||
Boolean include_end,
|
Boolean include_end,
|
||||||
ArrayOf(String) replacement,
|
ArrayOf(String) replacement,
|
||||||
Error *err)
|
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);
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
|
|
||||||
@ -176,14 +244,15 @@ void buffer_set_line_slice(Buffer buffer,
|
|||||||
return;
|
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"));
|
api_set_error(err, Validation, _("Index out of bounds"));
|
||||||
return;
|
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) {
|
if (start > end) {
|
||||||
api_set_error(err,
|
api_set_error(err,
|
||||||
@ -467,8 +536,9 @@ void buffer_insert(Buffer buffer,
|
|||||||
ArrayOf(String) lines,
|
ArrayOf(String) lines,
|
||||||
Error *err)
|
Error *err)
|
||||||
{
|
{
|
||||||
bool end_start = lnum < 0;
|
// "lnum" will be the index of the line after inserting,
|
||||||
buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err);
|
// 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
|
/// 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
|
// 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
|
// 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
|
// Convert the index to a vim line number
|
||||||
index++;
|
index++;
|
||||||
// Fix if > line_count
|
|
||||||
index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index;
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds.
|
static int64_t convert_index(int64_t index)
|
||||||
static bool inbounds(buf_T *buf, int64_t index)
|
|
||||||
{
|
{
|
||||||
linenr_T nlines = buf->b_ml.ml_line_count;
|
return index < 0 ? index - 1 : index;
|
||||||
return index >= -nlines && index < nlines;
|
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,11 @@ describe('buffer_* functions', function()
|
|||||||
eq('', curbuf('get_line', 0))
|
eq('', curbuf('get_line', 0))
|
||||||
end)
|
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')
|
curbuf('set_line', 0, 'line1.a')
|
||||||
eq('', curbuf('get_line', 1))
|
eq(1, curbuf('line_count')) -- sanity
|
||||||
eq('', curbuf('get_line', -2))
|
eq(false, pcall(curbuf, 'get_line', 1))
|
||||||
|
eq(false, pcall(curbuf, 'get_line', -2))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('set_line, del_line: out-of-bounds is an error', function()
|
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))
|
eq({}, curbuf('get_line_slice', -4, -5, true, true))
|
||||||
end)
|
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'})
|
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({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity
|
||||||
|
|
||||||
eq({'c'}, curbuf('get_line_slice', -1, 4, true, true))
|
eq({'c'}, curbuf('get_line_slice', -1, 4, true, true))
|
||||||
eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, 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'}))
|
curbuf('set_line_slice', 4, 5, true, true, {'d'})
|
||||||
eq(false, pcall(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)
|
end)
|
||||||
|
|
||||||
it('works', function()
|
it('works', function()
|
||||||
|
Loading…
Reference in New Issue
Block a user