Merge pull request #8660 from phodge/7688-nvim-buf-lines-should-return-empty-list-for-unloaded-buffer

handle unloaded buffers in nvim_buf_*() functions
This commit is contained in:
Björn Linse 2018-08-02 13:28:36 +02:00 committed by GitHub
commit 2b9fc9a13f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 163 additions and 13 deletions

View File

@ -113,9 +113,9 @@ set(NVIM_VERSION_PATCH 2)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level
set(NVIM_API_LEVEL 4) # Bump this after any API change.
set(NVIM_API_LEVEL 5) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE false)
set(NVIM_API_PRERELEASE true)
file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
include(GetGitRevisionDescription)

View File

@ -836,10 +836,26 @@ nvim_get_proc({pid}) *nvim_get_proc()*
Return: ~
Map of process properties, or NIL if process not found.
nvim__inspect_cell({row}, {col}) *nvim__inspect_cell()*
TODO: Documentation
==============================================================================
Buffer Functions *api-buffer*
Unloaded Buffers:~
Buffers may be unloaded by the |:bunload| command or the
buffer's |'bufhidden'| option. When a buffer is unloaded its
file contents are freed from memory and vim cannot operate on
the buffer lines until it is reloaded (usually by opening the
buffer again in a new window). API methods such as
|nvim_buf_get_lines()| and |nvim_buf_line_count()| will be
affected.
You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()|
to check whether a buffer is loaded.
nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
Gets the buffer line count
@ -847,7 +863,8 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
{buffer} Buffer handle
Return: ~
Line count
Line count, or `0` if the buffer has been unloaded (see
|api-buffer|).
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activate updates from this buffer to the current channel.
@ -896,7 +913,8 @@ nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
error.
Return: ~
Array of lines
Array of lines. If the buffer has been unloaded then an
empty array will be returned instead. (See |api-buffer|.)
*nvim_buf_set_lines()*
nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing},
@ -1013,14 +1031,28 @@ nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
{buffer} Buffer handle
{name} Buffer name
nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
Checks if a buffer is valid
nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()*
Checks if a buffer is valid and loaded. See |api-buffer| for
more info about unloaded buffers.
Parameters: ~
{buffer} Buffer handle
Return: ~
true if the buffer is valid, false otherwise
true if the buffer is valid and loaded, false otherwise.
nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
Checks if a buffer is valid.
Note:
Even if a buffer is valid it may have been unloaded. See
|api-buffer| for more info about unloaded buffers.
Parameters: ~
{buffer} Buffer handle
Return: ~
true if the buffer is valid, false otherwise.
nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
Return a tuple (row,col) representing the position of the

View File

@ -413,10 +413,26 @@ def gen_docs(config):
sys.exit(p.returncode)
sections = {}
intros = {}
sep = '=' * text_width
base = os.path.join(out_dir, 'xml')
dom = minidom.parse(os.path.join(base, 'index.xml'))
# generate docs for section intros
for compound in dom.getElementsByTagName('compound'):
if compound.getAttribute('kind') != 'group':
continue
groupname = get_text(find_first(compound, 'name'))
groupxml = os.path.join(base, '%s.xml' % compound.getAttribute('refid'))
desc = find_first(minidom.parse(groupxml), 'detaileddescription')
if desc:
doc = parse_parblock(desc)
if doc:
intros[groupname] = doc
for compound in dom.getElementsByTagName('compound'):
if compound.getAttribute('kind') != 'file':
continue
@ -437,6 +453,11 @@ def gen_docs(config):
name = name.title()
doc = ''
intro = intros.get('api-%s' % name.lower())
if intro:
doc += '\n\n' + intro
if functions:
doc += '\n\n' + functions

View File

@ -31,11 +31,27 @@
# include "api/buffer.c.generated.h"
#endif
/// \defgroup api-buffer
///
/// Unloaded Buffers:~
///
/// Buffers may be unloaded by the |:bunload| command or the buffer's
/// |'bufhidden'| option. When a buffer is unloaded its file contents are freed
/// from memory and vim cannot operate on the buffer lines until it is reloaded
/// (usually by opening the buffer again in a new window). API methods such as
/// |nvim_buf_get_lines()| and |nvim_buf_line_count()| will be affected.
///
/// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check
/// whether a buffer is loaded.
/// Gets the buffer line count
///
/// @param buffer Buffer handle
/// @param[out] err Error details, if any
/// @return Line count
/// @return Line count, or \`0` if the buffer has been unloaded (see
/// |api-buffer|).
Integer nvim_buf_line_count(Buffer buffer, Error *err)
FUNC_API_SINCE(1)
{
@ -45,6 +61,11 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
return 0;
}
// return sentinel value if the buffer isn't loaded
if (buf->b_ml.ml_mfp == NULL) {
return 0;
}
return buf->b_ml.ml_line_count;
}
@ -205,7 +226,8 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
/// @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
/// @return Array of lines. If the buffer has been unloaded then an empty array
/// will be returned instead. (See |api-buffer|.)
ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
Buffer buffer,
Integer start,
@ -221,6 +243,11 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
return rv;
}
// return sentinel value if the buffer isn't loaded
if (buf->b_ml.ml_mfp == NULL) {
return rv;
}
bool oob = false;
start = normalize_index(buf, start, &oob);
end = normalize_index(buf, end, &oob);
@ -745,10 +772,27 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
}
}
/// Checks if a buffer is valid
/// Checks if a buffer is valid and loaded. See |api-buffer| for more info
/// about unloaded buffers.
///
/// @param buffer Buffer handle
/// @return true if the buffer is valid, false otherwise
/// @return true if the buffer is valid and loaded, false otherwise.
Boolean nvim_buf_is_loaded(Buffer buffer)
FUNC_API_SINCE(5)
{
Error stub = ERROR_INIT;
buf_T *buf = find_buffer_by_handle(buffer, &stub);
api_clear_error(&stub);
return buf && buf->b_ml.ml_mfp != NULL;
}
/// Checks if a buffer is valid.
///
/// @note Even if a buffer is valid it may have been unloaded. See |api-buffer|
/// for more info about unloaded buffers.
///
/// @param buffer Buffer handle
/// @return true if the buffer is valid, false otherwise.
Boolean nvim_buf_is_valid(Buffer buffer)
FUNC_API_SINCE(1)
{

View File

@ -35,8 +35,37 @@ describe('api/buf', function()
-- There's always at least one line
eq(1, curbuf_depr('line_count'))
end)
end)
it('line_count has defined behaviour for unloaded buffers', function()
-- we'll need to know our bufnr for when it gets unloaded
local bufnr = curbuf('get_number')
-- replace the buffer contents with these three lines
request('nvim_buf_set_lines', bufnr, 0, -1, 1, {"line1", "line2", "line3", "line4"})
-- check the line count is correct
eq(4, request('nvim_buf_line_count', bufnr))
-- force unload the buffer (this will discard changes)
command('new')
command('bunload! '..bufnr)
-- line count for an unloaded buffer should always be 0
eq(0, request('nvim_buf_line_count', bufnr))
end)
it('get_lines has defined behaviour for unloaded buffers', function()
-- we'll need to know our bufnr for when it gets unloaded
local bufnr = curbuf('get_number')
-- replace the buffer contents with these three lines
buffer('set_lines', bufnr, 0, -1, 1, {"line1", "line2", "line3", "line4"})
-- confirm that getting lines works
eq({"line2", "line3"}, buffer('get_lines', bufnr, 1, 3, 1))
-- force unload the buffer (this will discard changes)
command('new')
command('bunload! '..bufnr)
-- attempting to get lines now always gives empty list
eq({}, buffer('get_lines', bufnr, 1, 3, 1))
-- it's impossible to get out-of-bounds errors for an unloaded buffer
eq({}, buffer('get_lines', bufnr, 8888, 9999, 1))
end)
end)
describe('{get,set,del}_line', function()
it('works', function()
@ -70,7 +99,6 @@ describe('api/buf', function()
end)
end)
describe('{get,set}_line_slice', function()
it('get_line_slice: out-of-bounds returns empty array', function()
curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'})
@ -345,6 +373,31 @@ describe('api/buf', function()
end)
end)
describe('is_loaded', function()
it('works', function()
-- record our buffer number for when we unload it
local bufnr = curbuf('get_number')
-- api should report that the buffer is loaded
ok(buffer('is_loaded', bufnr))
-- hide the current buffer by switching to a new empty buffer
-- Careful! we need to modify the buffer first or vim will just reuse it
buffer('set_lines', bufnr, 0, -1, 1, {'line1'})
command('hide enew')
-- confirm the buffer is hidden, but still loaded
local infolist = nvim('eval', 'getbufinfo('..bufnr..')')
eq(1, #infolist)
eq(1, infolist[1].hidden)
eq(1, infolist[1].loaded)
-- now force unload the buffer
command('bunload! '..bufnr)
-- confirm the buffer is unloaded
infolist = nvim('eval', 'getbufinfo('..bufnr..')')
eq(0, infolist[1].loaded)
-- nvim_buf_is_loaded() should also report the buffer as unloaded
eq(false, buffer('is_loaded', bufnr))
end)
end)
describe('is_valid', function()
it('works', function()
nvim('command', 'new')