mirror of
https://github.com/neovim/neovim
synced 2025-07-17 17:51:48 +00:00
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:
@ -113,9 +113,9 @@ set(NVIM_VERSION_PATCH 2)
|
|||||||
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
||||||
|
|
||||||
# API level
|
# 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_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)
|
file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
|
@ -836,10 +836,26 @@ nvim_get_proc({pid}) *nvim_get_proc()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
Map of process properties, or NIL if process not found.
|
Map of process properties, or NIL if process not found.
|
||||||
|
|
||||||
|
nvim__inspect_cell({row}, {col}) *nvim__inspect_cell()*
|
||||||
|
TODO: Documentation
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Buffer Functions *api-buffer*
|
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()*
|
nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
|
||||||
Gets the buffer line count
|
Gets the buffer line count
|
||||||
|
|
||||||
@ -847,7 +863,8 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
|
|||||||
{buffer} Buffer handle
|
{buffer} Buffer handle
|
||||||
|
|
||||||
Return: ~
|
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()*
|
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
|
||||||
Activate updates from this buffer to the current channel.
|
Activate updates from this buffer to the current channel.
|
||||||
@ -896,7 +913,8 @@ nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
|
|||||||
error.
|
error.
|
||||||
|
|
||||||
Return: ~
|
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()*
|
||||||
nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing},
|
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
|
{buffer} Buffer handle
|
||||||
{name} Buffer name
|
{name} Buffer name
|
||||||
|
|
||||||
nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
|
nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()*
|
||||||
Checks if a buffer is valid
|
Checks if a buffer is valid and loaded. See |api-buffer| for
|
||||||
|
more info about unloaded buffers.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
{buffer} Buffer handle
|
{buffer} Buffer handle
|
||||||
|
|
||||||
Return: ~
|
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()*
|
nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
|
||||||
Return a tuple (row,col) representing the position of the
|
Return a tuple (row,col) representing the position of the
|
||||||
|
@ -413,10 +413,26 @@ def gen_docs(config):
|
|||||||
sys.exit(p.returncode)
|
sys.exit(p.returncode)
|
||||||
|
|
||||||
sections = {}
|
sections = {}
|
||||||
|
intros = {}
|
||||||
sep = '=' * text_width
|
sep = '=' * text_width
|
||||||
|
|
||||||
base = os.path.join(out_dir, 'xml')
|
base = os.path.join(out_dir, 'xml')
|
||||||
dom = minidom.parse(os.path.join(base, 'index.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'):
|
for compound in dom.getElementsByTagName('compound'):
|
||||||
if compound.getAttribute('kind') != 'file':
|
if compound.getAttribute('kind') != 'file':
|
||||||
continue
|
continue
|
||||||
@ -437,6 +453,11 @@ def gen_docs(config):
|
|||||||
name = name.title()
|
name = name.title()
|
||||||
|
|
||||||
doc = ''
|
doc = ''
|
||||||
|
|
||||||
|
intro = intros.get('api-%s' % name.lower())
|
||||||
|
if intro:
|
||||||
|
doc += '\n\n' + intro
|
||||||
|
|
||||||
if functions:
|
if functions:
|
||||||
doc += '\n\n' + functions
|
doc += '\n\n' + functions
|
||||||
|
|
||||||
|
@ -31,11 +31,27 @@
|
|||||||
# include "api/buffer.c.generated.h"
|
# include "api/buffer.c.generated.h"
|
||||||
#endif
|
#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
|
/// Gets the buffer line count
|
||||||
///
|
///
|
||||||
/// @param buffer Buffer handle
|
/// @param buffer Buffer handle
|
||||||
/// @param[out] err Error details, if any
|
/// @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)
|
Integer nvim_buf_line_count(Buffer buffer, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
@ -45,6 +61,11 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
|
|||||||
return 0;
|
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;
|
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 end Last line index (exclusive)
|
||||||
/// @param strict_indexing Whether out-of-bounds should be an error.
|
/// @param strict_indexing Whether out-of-bounds should be an error.
|
||||||
/// @param[out] err Error details, if any
|
/// @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,
|
ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
|
||||||
Buffer buffer,
|
Buffer buffer,
|
||||||
Integer start,
|
Integer start,
|
||||||
@ -221,6 +243,11 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return sentinel value if the buffer isn't loaded
|
||||||
|
if (buf->b_ml.ml_mfp == NULL) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
bool oob = false;
|
bool oob = false;
|
||||||
start = normalize_index(buf, start, &oob);
|
start = normalize_index(buf, start, &oob);
|
||||||
end = normalize_index(buf, end, &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
|
/// @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)
|
Boolean nvim_buf_is_valid(Buffer buffer)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
|
@ -35,8 +35,37 @@ describe('api/buf', function()
|
|||||||
-- There's always at least one line
|
-- There's always at least one line
|
||||||
eq(1, curbuf_depr('line_count'))
|
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)
|
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()
|
describe('{get,set,del}_line', function()
|
||||||
it('works', function()
|
it('works', function()
|
||||||
@ -70,7 +99,6 @@ describe('api/buf', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
describe('{get,set}_line_slice', function()
|
describe('{get,set}_line_slice', function()
|
||||||
it('get_line_slice: out-of-bounds returns empty array', function()
|
it('get_line_slice: out-of-bounds returns empty array', function()
|
||||||
curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'})
|
curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'})
|
||||||
@ -345,6 +373,31 @@ describe('api/buf', function()
|
|||||||
end)
|
end)
|
||||||
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()
|
describe('is_valid', function()
|
||||||
it('works', function()
|
it('works', function()
|
||||||
nvim('command', 'new')
|
nvim('command', 'new')
|
||||||
|
Reference in New Issue
Block a user