aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c206
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/api/tabpage.c68
-rw-r--r--src/nvim/api/vim.c193
-rw-r--r--src/nvim/api/window.c120
-rw-r--r--src/nvim/buffer.c54
-rw-r--r--src/nvim/charset.c39
-rw-r--r--src/nvim/eval.c40
-rw-r--r--src/nvim/ex_cmds.c111
-rw-r--r--src/nvim/ex_docmd.c28
-rw-r--r--src/nvim/ex_getln.c13
-rw-r--r--src/nvim/option_defs.h5
-rw-r--r--src/nvim/screen.c20
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/testdir/test_tabpage.vim58
-rw-r--r--src/nvim/version.c85
-rw-r--r--src/nvim/window.c56
17 files changed, 654 insertions, 446 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 09d717bff6..eaaae943d2 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -28,9 +28,9 @@
/// Gets the buffer line count
///
-/// @param buffer The buffer handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The line count
+/// @param buffer Buffer handle
+/// @param[out] err Error details, if any
+/// @return Line count
Integer nvim_buf_line_count(Buffer buffer, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -50,10 +50,10 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// for negative indices use
/// "nvim_buf_get_lines(buffer, index-1, index, true)"
///
-/// @param buffer The buffer handle
-/// @param index The line index
-/// @param[out] err Details of an error that may have occurred
-/// @return The line string
+/// @param buffer Buffer handle
+/// @param index Line index
+/// @param[out] err Error details, if any
+/// @return Line string
String buffer_get_line(Buffer buffer, Integer index, Error *err)
{
String rv = { .size = 0 };
@@ -78,10 +78,10 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// for negative indices use
/// "nvim_buf_set_lines(buffer, index-1, index, true, [line])"
///
-/// @param buffer The buffer handle
-/// @param index The line index
-/// @param line The new line.
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param index Line index
+/// @param line Contents of the new line
+/// @param[out] err Error details, if any
void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
{
Object l = STRING_OBJ(line);
@@ -97,9 +97,9 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
/// "nvim_buf_set_lines(buffer, index, index+1, true, [])"
/// for negative indices use
/// "nvim_buf_set_lines(buffer, index-1, index, true, [])"
-/// @param buffer The buffer handle
-/// @param index The line index
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer buffer handle
+/// @param index line index
+/// @param[out] err Error details, if any
void buffer_del_line(Buffer buffer, Integer index, Error *err)
{
Array array = ARRAY_DICT_INIT;
@@ -113,13 +113,13 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err)
/// where newstart = start + int(not include_start) - int(start < 0)
/// newend = end + int(include_end) - int(end < 0)
/// int(bool) = 1 if bool is true else 0
-/// @param buffer The buffer handle
-/// @param start The first line index
-/// @param end The last line index
-/// @param include_start True if the slice includes the `start` parameter
-/// @param include_end True if the slice includes the `end` parameter
-/// @param[out] err Details of an error that may have occurred
-/// @return An array of lines
+/// @param buffer Buffer handle
+/// @param start First line index
+/// @param end Last line index
+/// @param include_start True if the slice includes the `start` parameter
+/// @param include_end True if the slice includes the `end` parameter
+/// @param[out] err Error details, if any
+/// @return Array of lines
ArrayOf(String) buffer_get_line_slice(Buffer buffer,
Integer start,
Integer end,
@@ -142,12 +142,12 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
/// 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
+/// @param buffer Buffer handle
+/// @param start First line index
+/// @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
ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
Buffer buffer,
Integer start,
@@ -219,14 +219,14 @@ end:
/// newend = end + int(include_end) + int(end < 0)
/// int(bool) = 1 if bool is true else 0
///
-/// @param buffer The buffer handle
-/// @param start The first line index
-/// @param end The last line index
-/// @param include_start True if the slice includes the `start` parameter
-/// @param include_end True if the slice includes the `end` parameter
-/// @param replacement An array of lines to use as replacement(A 0-length
-// array will simply delete the line range)
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param start First line index
+/// @param end Last line index
+/// @param include_start True if the slice includes the `start` parameter
+/// @param include_end True if the slice includes the `end` parameter
+/// @param replacement Array of lines to use as replacement (0-length
+// array will delete the line range)
+/// @param[out] err Error details, if any
void buffer_set_line_slice(Buffer buffer,
Integer start,
Integer end,
@@ -253,12 +253,12 @@ void buffer_set_line_slice(Buffer buffer,
/// 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
+/// @param buffer Buffer handle
+/// @param start First line index
+/// @param end Last line index (exclusive)
+/// @param strict_indexing Whether out-of-bounds should be an error.
+/// @param replacement Array of lines to use as replacement
+/// @param[out] err Error details, if any
void nvim_buf_set_lines(uint64_t channel_id,
Buffer buffer,
Integer start,
@@ -411,10 +411,10 @@ end:
/// Gets a buffer-scoped (b:) variable.
///
-/// @param buffer The buffer handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The variable value
+/// @param buffer Buffer handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Variable value
Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -428,10 +428,10 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
/// Sets a buffer-scoped (b:) variable
///
-/// @param buffer The buffer handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -445,9 +445,9 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
/// Removes a buffer-scoped (b:) variable
///
-/// @param buffer The buffer handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
void nvim_buf_del_var(Buffer buffer, String name, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -463,11 +463,11 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
///
/// @deprecated
///
-/// @param buffer The buffer handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value or nil if there was no previous value.
+/// @param buffer Buffer handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
+/// @return Old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
@@ -486,10 +486,10 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param buffer The buffer handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @param buffer Buffer handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Old value
Object buffer_del_var(Buffer buffer, String name, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -504,10 +504,10 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
/// Gets a buffer option value
///
-/// @param buffer The buffer handle
-/// @param name The option name
-/// @param[out] err Details of an error that may have occurred
-/// @return The option value
+/// @param buffer Buffer handle
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value
Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -519,13 +519,13 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
return get_option_from(buf, SREQ_BUF, name, err);
}
-/// Sets a buffer option value. Passing 'nil' as value deletes the option(only
+/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
/// works if there's a global fallback)
///
-/// @param buffer The buffer handle
-/// @param name The option name
-/// @param value The option value
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param name Option name
+/// @param value Option value
+/// @param[out] err Error details, if any
void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -539,9 +539,9 @@ void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err)
/// Gets the buffer number
///
-/// @param buffer The buffer handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The buffer number
+/// @param buffer Buffer handle
+/// @param[out] err Error details, if any
+/// @return Buffer number
Integer nvim_buf_get_number(Buffer buffer, Error *err)
{
Integer rv = 0;
@@ -556,9 +556,9 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
/// Gets the full file name for the buffer
///
-/// @param buffer The buffer handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The buffer name
+/// @param buffer Buffer handle
+/// @param[out] err Error details, if any
+/// @return Buffer name
String nvim_buf_get_name(Buffer buffer, Error *err)
{
String rv = STRING_INIT;
@@ -573,9 +573,9 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
/// Sets the full file name for a buffer
///
-/// @param buffer The buffer handle
-/// @param name The buffer name
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param name Buffer name
+/// @param[out] err Error details, if any
void nvim_buf_set_name(Buffer buffer, String name, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -603,7 +603,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
/// Checks if a buffer is valid
///
-/// @param buffer The buffer handle
+/// @param buffer Buffer handle
/// @return true if the buffer is valid, false otherwise
Boolean nvim_buf_is_valid(Buffer buffer)
{
@@ -615,11 +615,11 @@ Boolean nvim_buf_is_valid(Buffer buffer)
///
/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines)
///
-/// @param buffer The buffer handle
-/// @param lnum Insert the lines after `lnum`. If negative, it will append
-/// to the end of the buffer.
-/// @param lines An array of lines
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param lnum Insert the lines after `lnum`. If negative, appends to
+/// the end of the buffer.
+/// @param lines Array of lines
+/// @param[out] err Error details, if any
void buffer_insert(Buffer buffer,
Integer lnum,
ArrayOf(String) lines,
@@ -632,10 +632,10 @@ void buffer_insert(Buffer buffer,
/// Return a tuple (row,col) representing the position of the named mark
///
-/// @param buffer The buffer handle
-/// @param name The mark's name
-/// @param[out] err Details of an error that may have occurred
-/// @return The (row, col) tuple
+/// @param buffer Buffer handle
+/// @param name Mark name
+/// @param[out] err Error details, if any
+/// @return (row, col) tuple
ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
{
Array rv = ARRAY_DICT_INIT;
@@ -694,15 +694,15 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// request an unique src_id at initialization, and later asynchronously add and
/// clear highlights in response to buffer changes.
///
-/// @param buffer The buffer handle
-/// @param src_id Source group to use or 0 to use a new group,
-/// or -1 for ungrouped highlight
-/// @param hl_group Name of the highlight group to use
-/// @param line The line to highlight
-/// @param col_start Start of range of columns to highlight
-/// @param col_end End of range of columns to highlight,
-/// or -1 to highlight to end of line
-/// @param[out] err Details of an error that may have occurred
+/// @param buffer Buffer handle
+/// @param src_id Source group to use or 0 to use a new group,
+/// or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line Line to highlight
+/// @param col_start Start of range of columns to highlight
+/// @param col_end End of range of columns to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Error details, if any
/// @return The src_id that was used
Integer nvim_buf_add_highlight(Buffer buffer,
Integer src_id,
@@ -740,12 +740,12 @@ Integer nvim_buf_add_highlight(Buffer buffer,
/// To clear a source group in the entire buffer, pass in 1 and -1 to
/// line_start and line_end respectively.
///
-/// @param buffer The buffer handle
-/// @param src_id Highlight source group to clear, or -1 to clear all groups.
+/// @param buffer Buffer handle
+/// @param src_id Highlight source group to clear, or -1 to clear all.
/// @param line_start Start of range of lines to clear
-/// @param line_end End of range of lines to clear (exclusive)
-/// or -1 to clear to end of file.
-/// @param[out] err Details of an error that may have occurred
+/// @param line_end End of range of lines to clear (exclusive) or -1 to clear
+/// to end of file.
+/// @param[out] err Error details, if any
void nvim_buf_clear_highlight(Buffer buffer,
Integer src_id,
Integer line_start,
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index c0ee735d1a..208c3b53c8 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -18,6 +18,7 @@
#include "nvim/map.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
+#include "nvim/version.h"
#include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h"
@@ -763,6 +764,7 @@ Dictionary api_metadata(void)
static Dictionary metadata = ARRAY_DICT_INIT;
if (!metadata.size) {
+ PUT(metadata, "version", DICTIONARY_OBJ(version_dict()));
init_function_metadata(&metadata);
init_error_type_metadata(&metadata);
init_type_metadata(&metadata);
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index fa00988ae3..9e61ec1871 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -11,9 +11,9 @@
/// Gets the windows in a tabpage
///
-/// @param tabpage The tabpage
-/// @param[out] err Details of an error that may have occurred
-/// @return The windows in `tabpage`
+/// @param tabpage Tabpage
+/// @param[out] err Error details, if any
+/// @return List of windows in `tabpage`
ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err)
{
Array rv = ARRAY_DICT_INIT;
@@ -39,10 +39,10 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err)
/// Gets a tab-scoped (t:) variable
///
-/// @param tabpage The tab page handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The variable value
+/// @param tabpage Tabpage handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Variable value
Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err)
{
tabpage_T *tab = find_tab_by_handle(tabpage, err);
@@ -56,10 +56,10 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err)
/// Sets a tab-scoped (t:) variable
///
-/// @param tabpage handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
+/// @param tabpage Tabpage handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
void nvim_tabpage_set_var(Tabpage tabpage,
String name,
Object value,
@@ -76,9 +76,9 @@ void nvim_tabpage_set_var(Tabpage tabpage,
/// Removes a tab-scoped (t:) variable
///
-/// @param tabpage handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
+/// @param tabpage Tabpage handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err)
{
tabpage_T *tab = find_tab_by_handle(tabpage, err);
@@ -94,11 +94,11 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err)
///
/// @deprecated
///
-/// @param tabpage handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value or nil if there was no previous value.
+/// @param tabpage Tabpage handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
+/// @return Old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
@@ -117,10 +117,10 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param tabpage handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @param tabpage Tabpage handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Old value
Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
{
tabpage_T *tab = find_tab_by_handle(tabpage, err);
@@ -132,11 +132,11 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
return dict_set_value(tab->tp_vars, name, NIL, true, true, err);
}
-/// Gets the current window in a tab page
+/// Gets the current window in a tabpage
///
-/// @param tabpage The tab page handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The Window handle
+/// @param tabpage Tabpage handle
+/// @param[out] err Error details, if any
+/// @return Window handle
Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
{
Window rv = 0;
@@ -159,11 +159,11 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
}
}
-/// Gets the tab page number
+/// Gets the tabpage number
///
-/// @param tabpage The tabpage handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The tabpage number
+/// @param tabpage Tabpage handle
+/// @param[out] err Error details, if any
+/// @return Tabpage number
Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err)
{
Integer rv = 0;
@@ -176,10 +176,10 @@ Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err)
return tabpage_index(tab);
}
-/// Checks if a tab page is valid
+/// Checks if a tabpage is valid
///
-/// @param tabpage The tab page handle
-/// @return true if the tab page is valid, false otherwise
+/// @param tabpage Tabpage handle
+/// @return true if the tabpage is valid, false otherwise
Boolean nvim_tabpage_is_valid(Tabpage tabpage)
{
Error stub = ERROR_INIT;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 9f3a84c88b..491375bd73 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -33,24 +33,26 @@
# include "api/vim.c.generated.h"
#endif
-/// Executes an ex-mode command str
+/// Executes an ex-command.
+/// On VimL error: Returns the VimL error; v:errmsg is not updated.
///
-/// @param str The command str
-/// @param[out] err Details of an error that may have occurred
-void nvim_command(String str, Error *err)
+/// @param command Ex-command string
+/// @param[out] err Error details (including actual VimL error), if any
+void nvim_command(String command, Error *err)
{
// Run the command
try_start();
- do_cmdline_cmd(str.data);
+ do_cmdline_cmd(command.data);
update_screen(VALID);
try_end(err);
}
-/// Passes input keys to Neovim
+/// Passes input keys to Nvim.
+/// On VimL error: Does not fail, but updates v:errmsg.
///
-/// @param keys to be typed
-/// @param mode specifies the mapping options
-/// @param escape_csi the string needs escaping for K_SPECIAL/CSI bytes
+/// @param keys to be typed
+/// @param mode mapping options
+/// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys`
/// @see feedkeys()
/// @see vim_strsave_escape_csi
void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
@@ -102,13 +104,15 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
}
}
-/// Passes input keys to Neovim. Unlike `nvim_feedkeys`, this will use a
-/// lower-level input buffer and the call is not deferred.
-/// This is the most reliable way to emulate real user input.
+/// Passes keys to Nvim as raw user-input.
+/// On VimL error: Does not fail, but updates v:errmsg.
+///
+/// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call
+/// is not deferred. This is the most reliable way to emulate real user input.
///
/// @param keys to be typed
-/// @return The number of bytes actually written, which can be lower than
-/// requested if the buffer becomes full.
+/// @return Number of bytes actually written (can be fewer than
+/// requested if the buffer becomes full).
Integer nvim_input(String keys)
FUNC_API_ASYNC
{
@@ -152,19 +156,19 @@ String nvim_command_output(String str, Error *err)
return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT));
}
-/// Evaluates the expression str using the Vim internal expression
-/// evaluator (see |expression|).
-/// Dictionaries and lists are recursively expanded.
+/// Evaluates a VimL expression (:help expression).
+/// Dictionaries and Lists are recursively expanded.
+/// On VimL error: Returns a generic error; v:errmsg is not updated.
///
-/// @param str The expression str
-/// @param[out] err Details of an error that may have occurred
-/// @return The expanded object
-Object nvim_eval(String str, Error *err)
+/// @param expr VimL expression string
+/// @param[out] err Error details, if any
+/// @return Evaluation result or expanded object
+Object nvim_eval(String expr, Error *err)
{
Object rv = OBJECT_INIT;
// Evaluate the expression
try_start();
- typval_T *expr_result = eval_expr((char_u *) str.data, NULL);
+ typval_T *expr_result = eval_expr((char_u *)expr.data, NULL);
if (!expr_result) {
api_set_error(err, Exception, _("Failed to evaluate expression"));
@@ -180,11 +184,12 @@ Object nvim_eval(String str, Error *err)
return rv;
}
-/// Call the given function with the given arguments stored in an array.
+/// Calls a VimL function with the given arguments.
+/// On VimL error: Returns a generic error; v:errmsg is not updated.
///
-/// @param fname Function to call
-/// @param args Functions arguments packed in an Array
-/// @param[out] err Details of an error that may have occurred
+/// @param fname Function to call
+/// @param args Function arguments packed in an Array
+/// @param[out] err Error details, if any
/// @return Result of the function call
Object nvim_call_function(String fname, Array args, Error *err)
{
@@ -229,12 +234,12 @@ free_vim_args:
return rv;
}
-/// Calculates the number of display cells `str` occupies, tab is counted as
-/// one cell.
+/// Calculates the number of display cells occupied by `text`.
+/// <Tab> counts as one cell.
///
-/// @param str Some text
-/// @param[out] err Details of an error that may have occurred
-/// @return The number of cells
+/// @param text Some text
+/// @param[out] err Error details, if any
+/// @return Number of cells
Integer nvim_strwidth(String str, Error *err)
{
if (str.size > INT_MAX) {
@@ -245,9 +250,9 @@ Integer nvim_strwidth(String str, Error *err)
return (Integer) mb_string2cells((char_u *) str.data);
}
-/// Gets a list of paths contained in 'runtimepath'
+/// Gets the paths contained in 'runtimepath'.
///
-/// @return The list of paths
+/// @return List of paths
ArrayOf(String) nvim_list_runtime_paths(void)
{
Array rv = ARRAY_DICT_INIT;
@@ -285,10 +290,10 @@ ArrayOf(String) nvim_list_runtime_paths(void)
return rv;
}
-/// Changes Vim working directory
+/// Changes the global working directory.
///
-/// @param dir The new working directory
-/// @param[out] err Details of an error that may have occurred
+/// @param dir Directory path
+/// @param[out] err Error details, if any
void nvim_set_current_dir(String dir, Error *err)
{
if (dir.size >= MAXPATHL) {
@@ -315,8 +320,8 @@ void nvim_set_current_dir(String dir, Error *err)
/// Gets the current line
///
-/// @param[out] err Details of an error that may have occurred
-/// @return The current line string
+/// @param[out] err Error details, if any
+/// @return Current line string
String nvim_get_current_line(Error *err)
{
return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
@@ -324,8 +329,8 @@ String nvim_get_current_line(Error *err)
/// Sets the current line
///
-/// @param line The line contents
-/// @param[out] err Details of an error that may have occurred
+/// @param line Line contents
+/// @param[out] err Error details, if any
void nvim_set_current_line(String line, Error *err)
{
buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
@@ -333,36 +338,36 @@ void nvim_set_current_line(String line, Error *err)
/// Deletes the current line
///
-/// @param[out] err Details of an error that may have occurred
+/// @param[out] err Error details, if any
void nvim_del_current_line(Error *err)
{
buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
-/// Gets a global variable
+/// Gets a global (g:) variable
///
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The variable value
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Variable value
Object nvim_get_var(String name, Error *err)
{
return dict_get_value(&globvardict, name, err);
}
-/// Sets a global variable
+/// Sets a global (g:) variable
///
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
void nvim_set_var(String name, Object value, Error *err)
{
dict_set_value(&globvardict, name, value, false, false, err);
}
-/// Removes a global variable
+/// Removes a global (g:) variable
///
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
+/// @param name Variable name
+/// @param[out] err Error details, if any
void nvim_del_var(String name, Error *err)
{
dict_set_value(&globvardict, name, NIL, true, false, err);
@@ -372,10 +377,10 @@ void nvim_del_var(String name, Error *err)
///
/// @deprecated
///
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value or nil if there was no previous value.
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
+/// @return Old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
@@ -388,19 +393,19 @@ Object vim_set_var(String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Old value
Object vim_del_var(String name, Error *err)
{
return dict_set_value(&globvardict, name, NIL, true, true, err);
}
-/// Gets a vim variable
+/// Gets a v: variable
///
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The variable value
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Variable value
Object nvim_get_vvar(String name, Error *err)
{
return dict_get_value(&vimvardict, name, err);
@@ -408,9 +413,9 @@ Object nvim_get_vvar(String name, Error *err)
/// Gets an option value string
///
-/// @param name The option name
-/// @param[out] err Details of an error that may have occurred
-/// @return The option value
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value
Object nvim_get_option(String name, Error *err)
{
return get_option_from(NULL, SREQ_GLOBAL, name, err);
@@ -418,9 +423,9 @@ Object nvim_get_option(String name, Error *err)
/// Sets an option value
///
-/// @param name The option name
-/// @param value The new option value
-/// @param[out] err Details of an error that may have occurred
+/// @param name Option name
+/// @param value New option value
+/// @param[out] err Error details, if any
void nvim_set_option(String name, Object value, Error *err)
{
set_option_to(NULL, SREQ_GLOBAL, name, value, err);
@@ -428,7 +433,7 @@ void nvim_set_option(String name, Object value, Error *err)
/// Writes a message to vim output buffer
///
-/// @param str The message
+/// @param str Message
void nvim_out_write(String str)
{
write_msg(str, false);
@@ -436,16 +441,17 @@ void nvim_out_write(String str)
/// Writes a message to vim error buffer
///
-/// @param str The message
+/// @param str Message
void nvim_err_write(String str)
{
write_msg(str, true);
}
-/// Higher level error reporting function that ensures all str contents
-/// are written by sending a trailing linefeed to `nvim_err_write`
+/// Writes a message to vim error buffer. Appends a linefeed to ensure all
+/// contents are written.
///
-/// @param str The message
+/// @param str Message
+/// @see nvim_err_write()
void nvim_err_writeln(String str)
{
nvim_err_write(str);
@@ -454,7 +460,7 @@ void nvim_err_writeln(String str)
/// Gets the current list of buffer handles
///
-/// @return The number of buffers
+/// @return List of buffer handles
ArrayOf(Buffer) nvim_list_bufs(void)
{
Array rv = ARRAY_DICT_INIT;
@@ -475,7 +481,7 @@ ArrayOf(Buffer) nvim_list_bufs(void)
/// Gets the current buffer
///
-/// @reqturn The buffer handle
+/// @return Buffer handle
Buffer nvim_get_current_buf(void)
{
return curbuf->handle;
@@ -483,8 +489,8 @@ Buffer nvim_get_current_buf(void)
/// Sets the current buffer
///
-/// @param id The buffer handle
-/// @param[out] err Details of an error that may have occurred
+/// @param id Buffer handle
+/// @param[out] err Error details, if any
void nvim_set_current_buf(Buffer buffer, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -505,7 +511,7 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
/// Gets the current list of window handles
///
-/// @return The number of windows
+/// @return List of window handles
ArrayOf(Window) nvim_list_wins(void)
{
Array rv = ARRAY_DICT_INIT;
@@ -526,7 +532,7 @@ ArrayOf(Window) nvim_list_wins(void)
/// Gets the current window
///
-/// @return The window handle
+/// @return Window handle
Window nvim_get_current_win(void)
{
return curwin->handle;
@@ -534,7 +540,7 @@ Window nvim_get_current_win(void)
/// Sets the current window
///
-/// @param handle The window handle
+/// @param handle Window handle
void nvim_set_current_win(Window window, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -555,7 +561,7 @@ void nvim_set_current_win(Window window, Error *err)
/// Gets the current list of tabpage handles
///
-/// @return The number of tab pages
+/// @return List of tabpage handles
ArrayOf(Tabpage) nvim_list_tabpages(void)
{
Array rv = ARRAY_DICT_INIT;
@@ -574,18 +580,18 @@ ArrayOf(Tabpage) nvim_list_tabpages(void)
return rv;
}
-/// Gets the current tab page
+/// Gets the current tabpage
///
-/// @return The tab page handle
+/// @return Tabpage handle
Tabpage nvim_get_current_tabpage(void)
{
return curtab->handle;
}
-/// Sets the current tab page
+/// Sets the current tabpage
///
-/// @param handle The tab page handle
-/// @param[out] err Details of an error that may have occurred
+/// @param handle Tabpage handle
+/// @param[out] err Error details, if any
void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
{
tabpage_T *tp = find_tab_by_handle(tabpage, err);
@@ -606,8 +612,8 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
/// Subscribes to event broadcasts
///
-/// @param channel_id The channel id (passed automatically by the dispatcher)
-/// @param event The event type string
+/// @param channel_id Channel id (passed automatically by the dispatcher)
+/// @param event Event type string
void nvim_subscribe(uint64_t channel_id, String event)
FUNC_API_NOEVAL
{
@@ -620,8 +626,8 @@ void nvim_subscribe(uint64_t channel_id, String event)
/// Unsubscribes to event broadcasts
///
-/// @param channel_id The channel id (passed automatically by the dispatcher)
-/// @param event The event type string
+/// @param channel_id Channel id (passed automatically by the dispatcher)
+/// @param event Event type string
void nvim_unsubscribe(uint64_t channel_id, String event)
FUNC_API_NOEVAL
{
@@ -756,9 +762,8 @@ validation_error:
/// and flushed after each newline. Incomplete lines are kept for writing
/// later.
///
-/// @param message The message to write
-/// @param to_err true if it should be treated as an error message (use
-/// `emsg` instead of `msg` to print each line)
+/// @param message Message to write
+/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
static void write_msg(String message, bool to_err)
{
static size_t out_pos = 0, err_pos = 0;
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 382f65e7d9..ef881fa0eb 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -15,9 +15,9 @@
/// Gets the current buffer in a window
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The buffer handle
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Buffer handle
Buffer nvim_win_get_buf(Window window, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -31,9 +31,9 @@ Buffer nvim_win_get_buf(Window window, Error *err)
/// Gets the cursor position in the window
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return the (row, col) tuple
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return (row, col) tuple
ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err)
{
Array rv = ARRAY_DICT_INIT;
@@ -49,9 +49,9 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err)
/// Sets the cursor position in the window
///
-/// @param window The window handle
-/// @param pos the (row, col) tuple representing the new position
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param pos (row, col) tuple representing the new position
+/// @param[out] err Error details, if any
void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -95,9 +95,9 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
/// Gets the window height
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return the height in rows
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Height as a count of rows
Integer nvim_win_get_height(Window window, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -112,9 +112,9 @@ Integer nvim_win_get_height(Window window, Error *err)
/// Sets the window height. This will only succeed if the screen is split
/// horizontally.
///
-/// @param window The window handle
-/// @param height the new height in rows
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param height Height as a count of rows
+/// @param[out] err Error details, if any
void nvim_win_set_height(Window window, Integer height, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -138,9 +138,9 @@ void nvim_win_set_height(Window window, Integer height, Error *err)
/// Gets the window width
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return the width in columns
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Width as a count of columns
Integer nvim_win_get_width(Window window, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -155,9 +155,9 @@ Integer nvim_win_get_width(Window window, Error *err)
/// Sets the window width. This will only succeed if the screen is split
/// vertically.
///
-/// @param window The window handle
-/// @param width the new width in columns
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param width Width as a count of columns
+/// @param[out] err Error details, if any
void nvim_win_set_width(Window window, Integer width, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -181,10 +181,10 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
/// Gets a window-scoped (w:) variable
///
-/// @param window The window handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The variable value
+/// @param window Window handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
+/// @return Variable value
Object nvim_win_get_var(Window window, String name, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -198,10 +198,10 @@ Object nvim_win_get_var(Window window, String name, Error *err)
/// Sets a window-scoped (w:) variable
///
-/// @param window The window handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
void nvim_win_set_var(Window window, String name, Object value, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -215,9 +215,9 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err)
/// Removes a window-scoped (w:) variable
///
-/// @param window The window handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param name Variable name
+/// @param[out] err Error details, if any
void nvim_win_del_var(Window window, String name, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -233,11 +233,11 @@ void nvim_win_del_var(Window window, String name, Error *err)
///
/// @deprecated
///
-/// @param window The window handle
-/// @param name The variable name
-/// @param value The variable value
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value or nil if there was no previous value.
+/// @param window Window handle
+/// @param name Variable name
+/// @param value Variable value
+/// @param[out] err Error details, if any
+/// @return Old value or nil if there was no previous value.
///
/// @warning It may return nil if there was no previous value
/// or if previous value was `v:null`.
@@ -256,10 +256,10 @@ Object window_set_var(Window window, String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param window The window handle
-/// @param name The variable name
-/// @param[out] err Details of an error that may have occurred
-/// @return The old value
+/// @param window Window handle
+/// @param name variable name
+/// @param[out] err Error details, if any
+/// @return Old value
Object window_del_var(Window window, String name, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -273,10 +273,10 @@ Object window_del_var(Window window, String name, Error *err)
/// Gets a window option value
///
-/// @param window The window handle
-/// @param name The option name
-/// @param[out] err Details of an error that may have occurred
-/// @return The option value
+/// @param window Window handle
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value
Object nvim_win_get_option(Window window, String name, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -291,10 +291,10 @@ Object nvim_win_get_option(Window window, String name, Error *err)
/// Sets a window option value. Passing 'nil' as value deletes the option(only
/// works if there's a global fallback)
///
-/// @param window The window handle
-/// @param name The option name
-/// @param value The option value
-/// @param[out] err Details of an error that may have occurred
+/// @param window Window handle
+/// @param name Option name
+/// @param value Option value
+/// @param[out] err Error details, if any
void nvim_win_set_option(Window window, String name, Object value, Error *err)
{
win_T *win = find_window_by_handle(window, err);
@@ -308,9 +308,9 @@ void nvim_win_set_option(Window window, String name, Object value, Error *err)
/// Gets the window position in display cells. First position is zero.
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The (row, col) tuple with the window position
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return (row, col) tuple with the window position
ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err)
{
Array rv = ARRAY_DICT_INIT;
@@ -324,11 +324,11 @@ ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err)
return rv;
}
-/// Gets the window tab page
+/// Gets the window tabpage
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The tab page that contains the window
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Tabpage that contains the window
Tabpage nvim_win_get_tabpage(Window window, Error *err)
{
Tabpage rv = 0;
@@ -343,9 +343,9 @@ Tabpage nvim_win_get_tabpage(Window window, Error *err)
/// Gets the window number
///
-/// @param window The window handle
-/// @param[out] err Details of an error that may have occurred
-/// @return The window number
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Window number
Integer nvim_win_get_number(Window window, Error *err)
{
Integer rv = 0;
@@ -363,7 +363,7 @@ Integer nvim_win_get_number(Window window, Error *err)
/// Checks if a window is valid
///
-/// @param window The window handle
+/// @param window Window handle
/// @return true if the window is valid, false otherwise
Boolean nvim_win_is_valid(Window window)
{
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 5fb011885e..a66fdc1304 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -323,13 +323,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
wipe_buf = true;
}
- if (win_valid(win)) {
- /* Set b_last_cursor when closing the last window for the buffer.
- * Remember the last cursor position and window options of the buffer.
- * This used to be only for the current window, but then options like
- * 'foldmethod' may be lost with a ":only" command. */
- if (buf->b_nwindows == 1)
+ if (win_valid_any_tab(win)) {
+ // Set b_last_cursor when closing the last window for the buffer.
+ // Remember the last cursor position and window options of the buffer.
+ // This used to be only for the current window, but then options like
+ // 'foldmethod' may be lost with a ":only" command.
+ if (buf->b_nwindows == 1) {
set_last_cursor(win);
+ }
buflist_setfpos(buf, win,
win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
win->w_cursor.col, TRUE);
@@ -402,7 +403,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
buf->b_nwindows = nwindows;
buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
- if (win_valid(win) && win->w_buffer == buf) {
+ if (win_valid_any_tab(win) && win->w_buffer == buf) {
win->w_buffer = NULL; // make sure we don't use the buffer now
}
@@ -478,17 +479,20 @@ void buf_clear_file(buf_T *buf)
buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
}
-/*
- * buf_freeall() - free all things allocated for a buffer that are related to
- * the file. flags:
- * BFA_DEL buffer is going to be deleted
- * BFA_WIPE buffer is going to be wiped out
- * BFA_KEEP_UNDO do not free undo information
- */
+/// buf_freeall() - free all things allocated for a buffer that are related to
+/// the file. Careful: get here with "curwin" NULL when exiting.
+///
+/// @param flags BFA_DEL buffer is going to be deleted
+/// BFA_WIPE buffer is going to be wiped out
+/// BFA_KEEP_UNDO do not free undo information
void buf_freeall(buf_T *buf, int flags)
{
bool is_curbuf = (buf == curbuf);
+ int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
+ win_T *the_curwin = curwin;
+ tabpage_T *the_curtab = curtab;
+ // Make sure the buffer isn't closed by autocommands.
buf->b_closing = true;
apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf);
if (!buf_valid(buf)) /* autocommands may delete the buffer */
@@ -505,8 +509,18 @@ void buf_freeall(buf_T *buf, int flags)
return;
}
buf->b_closing = false;
- if (aborting()) /* autocmds may abort script processing */
+
+ // If the buffer was in curwin and the window has changed, go back to that
+ // window, if it still exists. This avoids that ":edit x" triggering a
+ // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
+ if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
+ block_autocmds();
+ goto_tabpage_win(the_curtab, the_curwin);
+ unblock_autocmds();
+ }
+ if (aborting()) { // autocmds may abort script processing
return;
+ }
/*
* It's possible that autocommands change curbuf to the one being deleted.
@@ -4509,7 +4523,7 @@ chk_modeline (
char_u *e;
char_u *linecopy; /* local copy of any modeline found */
int prev;
- int vers;
+ intmax_t vers;
int end;
int retval = OK;
char_u *save_sourcing_name;
@@ -4528,7 +4542,10 @@ chk_modeline (
e = s + 4;
else
e = s + 3;
- vers = getdigits_int(&e);
+ if (getdigits_safe(&e, &vers) != OK) {
+ continue;
+ }
+
if (*e == ':'
&& (s[0] != 'V'
|| STRNCMP(skipwhite(e + 1), "set", 3) == 0)
@@ -4536,8 +4553,9 @@ chk_modeline (
|| (VIM_VERSION_100 >= vers && isdigit(s[3]))
|| (VIM_VERSION_100 < vers && s[3] == '<')
|| (VIM_VERSION_100 > vers && s[3] == '>')
- || (VIM_VERSION_100 == vers && s[3] == '=')))
+ || (VIM_VERSION_100 == vers && s[3] == '='))) {
break;
+ }
}
}
prev = *s;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 78f5d96fc7..61c5b10808 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1732,6 +1732,26 @@ char_u* skiptowhite_esc(char_u *p) {
return p;
}
+/// Get a number from a string and skip over it, signalling overflows
+///
+/// @param[out] pp A pointer to a pointer to char_u.
+/// It will be advanced past the read number.
+/// @param[out] nr Number read from the string.
+///
+/// @return OK on success, FAIL on error/overflow
+int getdigits_safe(char_u **pp, intmax_t *nr)
+{
+ errno = 0;
+ *nr = strtoimax((char *)(*pp), (char **)pp, 10);
+
+ if ((*nr == INTMAX_MIN || *nr == INTMAX_MAX)
+ && errno == ERANGE) {
+ return FAIL;
+ }
+
+ return OK;
+}
+
/// Get a number from a string and skip over it.
///
/// @param[out] pp A pointer to a pointer to char_u.
@@ -1740,17 +1760,16 @@ char_u* skiptowhite_esc(char_u *p) {
/// @return Number read from the string.
intmax_t getdigits(char_u **pp)
{
- errno = 0;
- intmax_t number = strtoimax((char *)*pp, (char **)pp, 10);
- if (number == INTMAX_MAX || number == INTMAX_MIN) {
- assert(errno != ERANGE);
- }
+ intmax_t number;
+ int ret = getdigits_safe(pp, &number);
+
+ (void)ret; // Avoid "unused variable" warning in Release build
+ assert(ret == OK);
+
return number;
}
-/// Get an int number from a string.
-///
-/// A getdigits wrapper restricted to int values.
+/// Get an int number from a string. Like getdigits(), but restricted to `int`.
int getdigits_int(char_u **pp)
{
intmax_t number = getdigits(pp);
@@ -1760,9 +1779,7 @@ int getdigits_int(char_u **pp)
return (int)number;
}
-/// Get a long number from a string.
-///
-/// A getdigits wrapper restricted to long values.
+/// Get a long number from a string. Like getdigits(), but restricted to `long`.
long getdigits_long(char_u **pp)
{
intmax_t number = getdigits(pp);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4eb3b36464..5d4241c8af 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -10524,16 +10524,10 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
: file_pat_to_reg_pat(pat, NULL, NULL, false);
}
-/*
- * "has()" function
- */
+/// "has()" function
static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int i;
- char_u *name;
- int n = FALSE;
- static char *(has_list[]) =
- {
+ static char *(has_list[]) = {
#ifdef UNIX
"unix",
#endif
@@ -10646,36 +10640,42 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
NULL
};
- name = get_tv_string(&argvars[0]);
- for (i = 0; has_list[i] != NULL; ++i)
+ bool n = false;
+ char *name = (char *)get_tv_string(&argvars[0]);
+
+ for (int i = 0; has_list[i] != NULL; i++) {
if (STRICMP(name, has_list[i]) == 0) {
- n = TRUE;
+ n = true;
break;
}
+ }
- if (n == FALSE) {
+ if (!n) {
if (STRNICMP(name, "patch", 5) == 0) {
if (name[5] == '-'
- && STRLEN(name) > 11
+ && strlen(name) > 11
&& ascii_isdigit(name[6])
&& ascii_isdigit(name[8])
&& ascii_isdigit(name[10])) {
- int major = atoi((char *)name + 6);
- int minor = atoi((char *)name + 8);
+ int major = atoi(name + 6);
+ int minor = atoi(name + 8);
// Expect "patch-9.9.01234".
n = (major < VIM_VERSION_MAJOR
|| (major == VIM_VERSION_MAJOR
&& (minor < VIM_VERSION_MINOR
|| (minor == VIM_VERSION_MINOR
- && has_patch(atoi((char *)name + 10))))));
+ && has_vim_patch(atoi(name + 10))))));
} else {
- n = has_patch(atoi((char *)name + 5));
+ n = has_vim_patch(atoi(name + 5));
}
+ } else if (STRNICMP(name, "nvim-", 5) == 0) {
+ // Expect "nvim-x.y.z"
+ n = has_nvim_version(name + 5);
} else if (STRICMP(name, "vim_starting") == 0) {
n = (starting != 0);
} else if (STRICMP(name, "multi_byte_encoding") == 0) {
- n = has_mbyte;
+ n = has_mbyte != 0;
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
} else if (STRICMP(name, "iconv") == 0) {
n = iconv_enabled(false);
@@ -10685,8 +10685,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- if (n == FALSE && eval_has_provider((char *)name)) {
- n = TRUE;
+ if (!n && eval_has_provider(name)) {
+ n = true;
}
rettv->vval.v_number = n;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 6205daf0cb..4674460a16 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2250,28 +2250,28 @@ do_ecmd (
xfree(new_name);
goto theend;
}
- if (buf == curbuf) /* already in new buffer */
- auto_buf = TRUE;
- else {
- if (curbuf == old_curbuf)
+ if (buf == curbuf) { // already in new buffer
+ auto_buf = true;
+ } else {
+ win_T *the_curwin = curwin;
+
+ // Set the w_closing flag to avoid that autocommands close the window.
+ the_curwin->w_closing = true;
+ if (curbuf == old_curbuf) {
buf_copy_options(buf, BCO_ENTER);
+ }
- /* close the link to the current buffer */
- u_sync(FALSE);
+ // Close the link to the current buffer. This will set
+ // curwin->w_buffer to NULL.
+ u_sync(false);
close_buffer(oldwin, curbuf,
- (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, FALSE);
-
- /* Autocommands may open a new window and leave oldwin open
- * which leads to crashes since the above call sets
- * oldwin->w_buffer to NULL. */
- if (curwin != oldwin && oldwin != aucmd_win && win_valid(oldwin)) {
- assert(oldwin);
- if (oldwin->w_buffer == NULL) {
- win_close(oldwin, FALSE);
- }
- }
+ (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+ false);
+
+ the_curwin->w_closing = false;
- if (aborting()) { /* autocmds may abort script processing */
+ // autocmds may abort script processing
+ if (aborting() && curwin->w_buffer != NULL) {
xfree(new_name);
goto theend;
}
@@ -2370,10 +2370,12 @@ do_ecmd (
if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) {
/* Save all the text, so that the reload can be undone.
* Sync first so that this is a separate undo-able action. */
- u_sync(FALSE);
- if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE)
- == FAIL)
+ u_sync(false);
+ if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, true)
+ == FAIL) {
+ xfree(new_name);
goto theend;
+ }
u_unchanged(curbuf);
buf_freeall(curbuf, BFA_KEEP_UNDO);
@@ -4072,61 +4074,66 @@ void ex_global(exarg_T *eap)
vim_regfree(regmatch.regprog);
}
-/*
- * Execute "cmd" on lines marked with ml_setmarked().
- */
+/// Execute `cmd` on lines marked with ml_setmarked().
void global_exe(char_u *cmd)
{
- linenr_T old_lcount; /* b_ml.ml_line_count before the command */
- buf_T *old_buf = curbuf; /* remember what buffer we started in */
- linenr_T lnum; /* line number according to old situation */
-
- /*
- * Set current position only once for a global command.
- * If global_busy is set, setpcmark() will not do anything.
- * If there is an error, global_busy will be incremented.
- */
+ linenr_T old_lcount; // b_ml.ml_line_count before the command
+ buf_T *old_buf = curbuf; // remember what buffer we started in
+ linenr_T lnum; // line number according to old situation
+ int save_mapped_ctrl_c = mapped_ctrl_c;
+
+ // Set current position only once for a global command.
+ // If global_busy is set, setpcmark() will not do anything.
+ // If there is an error, global_busy will be incremented.
setpcmark();
- /* When the command writes a message, don't overwrite the command. */
- msg_didout = TRUE;
+ // When the command writes a message, don't overwrite the command.
+ msg_didout = true;
+ // Disable CTRL-C mapping, let it interrupt (potentially long output).
+ mapped_ctrl_c = 0;
sub_nsubs = 0;
sub_nlines = 0;
- global_need_beginline = FALSE;
+ global_need_beginline = false;
global_busy = 1;
old_lcount = curbuf->b_ml.ml_line_count;
+
while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1) {
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
- if (*cmd == NUL || *cmd == '\n')
+ if (*cmd == NUL || *cmd == '\n') {
do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT);
- else
+ } else {
do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT);
+ }
os_breakcheck();
}
+ mapped_ctrl_c = save_mapped_ctrl_c;
global_busy = 0;
- if (global_need_beginline)
+ if (global_need_beginline) {
beginline(BL_WHITE | BL_FIX);
- else
- check_cursor(); /* cursor may be beyond the end of the line */
+ } else {
+ check_cursor(); // cursor may be beyond the end of the line
+ }
- /* the cursor may not have moved in the text but a change in a previous
- * line may move it on the screen */
+ // the cursor may not have moved in the text but a change in a previous
+ // line may move it on the screen
changed_line_abv_curs();
- /* If it looks like no message was written, allow overwriting the
- * command with the report for number of changes. */
- if (msg_col == 0 && msg_scrolled == 0)
- msg_didout = FALSE;
+ // If it looks like no message was written, allow overwriting the
+ // command with the report for number of changes.
+ if (msg_col == 0 && msg_scrolled == 0) {
+ msg_didout = false;
+ }
- /* If substitutes done, report number of substitutes, otherwise report
- * number of extra or deleted lines.
- * Don't report extra or deleted lines in the edge case where the buffer
- * we are in after execution is different from the buffer we started in. */
- if (!do_sub_msg(false) && curbuf == old_buf)
+ // If substitutes done, report number of substitutes, otherwise report
+ // number of extra or deleted lines.
+ // Don't report extra or deleted lines in the edge case where the buffer
+ // we are in after execution is different from the buffer we started in.
+ if (!do_sub_msg(false) && curbuf == old_buf) {
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
+ }
}
#if defined(EXITFREE)
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 9f83688e1e..5e418bf099 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1302,8 +1302,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
* 2. Handle command modifiers.
*/
p = ea.cmd;
- if (ascii_isdigit(*ea.cmd))
- p = skipwhite(skipdigits(ea.cmd));
+ p = skip_range(ea.cmd, NULL);
switch (*p) {
/* When adding an entry, also modify cmd_exists(). */
case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3))
@@ -1406,12 +1405,18 @@ static char_u * do_one_cmd(char_u **cmdlinep,
continue;
case 't': if (checkforcmd(&p, "tab", 3)) {
- if (ascii_isdigit(*ea.cmd))
- cmdmod.tab = atoi((char *)ea.cmd) + 1;
- else
- cmdmod.tab = tabpage_index(curtab) + 1;
- ea.cmd = p;
- continue;
+ long tabnr = get_address(&ea, &ea.cmd, ADDR_TABS, ea.skip, false);
+ if (tabnr == MAXLNUM) {
+ cmdmod.tab = tabpage_index(curtab) + 1;
+ } else {
+ if (tabnr < 0 || tabnr > LAST_TAB_NR) {
+ errormsg = (char_u *)_(e_invrange);
+ goto doend;
+ }
+ cmdmod.tab = tabnr + 1;
+ }
+ ea.cmd = p;
+ continue;
}
if (!checkforcmd(&ea.cmd, "topleft", 2))
break;
@@ -1766,11 +1771,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (text_locked() && !(ea.argt & CMDWIN)
&& !IS_USER_CMDIDX(ea.cmdidx)) {
- /* Command not allowed when editing the command line. */
- if (cmdwin_type != 0)
- errormsg = (char_u *)_(e_cmdwin);
- else
- errormsg = (char_u *)_(e_secure);
+ // Command not allowed when editing the command line.
+ errormsg = get_text_locked_msg();
goto doend;
}
/* Disallow editing another buffer when "curbuf_lock" is set.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7444eb8a38..e525c949bd 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1688,10 +1688,15 @@ int text_locked(void) {
*/
void text_locked_msg(void)
{
- if (cmdwin_type != 0)
- EMSG(_(e_cmdwin));
- else
- EMSG(_(e_secure));
+ EMSG(_(get_text_locked_msg()));
+}
+
+char_u * get_text_locked_msg(void) {
+ if (cmdwin_type != 0) {
+ return e_cmdwin;
+ } else {
+ return e_secure;
+ }
}
/*
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index e085b973ea..fdfcd1f428 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -397,10 +397,11 @@ EXTERN char_u *p_dir; /* 'directory' */
EXTERN char_u *p_dy; /* 'display' */
EXTERN unsigned dy_flags;
#ifdef IN_OPTION_C
-static char *(p_dy_values[]) = {"lastline", "uhex", NULL};
+static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", NULL };
#endif
#define DY_LASTLINE 0x001
-#define DY_UHEX 0x002
+#define DY_TRUNCATE 0x002
+#define DY_UHEX 0x004
EXTERN int p_ed; // 'edcompatible'
EXTERN bool p_emoji; // 'emoji'
EXTERN char_u *p_ead; // 'eadirection'
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 47dd640e59..3e4d016fe7 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1401,7 +1401,7 @@ static void win_update(win_T *wp)
&& wp->w_lines[idx].wl_valid
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
- && !(dy_flags & DY_LASTLINE)
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
&& srow + wp->w_lines[idx].wl_size > wp->w_height
&& diff_check_fill(wp, lnum) == 0
) {
@@ -1484,10 +1484,20 @@ static void win_update(win_T *wp)
/* Window ends in filler lines. */
wp->w_botline = lnum;
wp->w_filler_rows = wp->w_height - srow;
- } else if (dy_flags & DY_LASTLINE) { /* 'display' has "lastline" */
- /*
- * Last line isn't finished: Display "@@@" at the end.
- */
+ } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
+ int scr_row = wp->w_winrow + wp->w_height - 1;
+
+ // Last line isn't finished: Display "@@@" in the last screen line.
+ screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol,
+ hl_attr(HLF_AT));
+
+ screen_fill(scr_row, scr_row + 1,
+ (int)wp->w_wincol + 2, (int)W_ENDCOL(wp),
+ '@', ' ', hl_attr(HLF_AT));
+ set_empty_rows(wp, srow);
+ wp->w_botline = lnum;
+ } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ // Last line isn't finished: Display "@@@" at the end.
screen_fill(wp->w_winrow + wp->w_height - 1,
wp->w_winrow + wp->w_height,
W_ENDCOL(wp) - 3, W_ENDCOL(wp),
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 6fd7603629..b49ae9da21 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5537,7 +5537,7 @@ void ex_ownsyntax(exarg_T *eap)
}
}
-int syntax_present(win_T *win)
+bool syntax_present(win_T *win)
{
return win->w_s->b_syn_patterns.ga_len != 0
|| win->w_s->b_syn_clusters.ga_len != 0
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index e6b85d6e14..0bf7d056de 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -186,4 +186,62 @@ function Test_tabpage_with_autocmd()
bw!
endfunction
+function Test_tabpage_with_tab_modifier()
+ for n in range(4)
+ tabedit
+ endfor
+
+ function s:check_tab(pre_nr, cmd, post_nr)
+ exec 'tabnext ' . a:pre_nr
+ exec a:cmd
+ call assert_equal(a:post_nr, tabpagenr())
+ call assert_equal('help', &filetype)
+ helpclose
+ endfunc
+
+ call s:check_tab(1, 'tab help', 2)
+ call s:check_tab(1, '3tab help', 4)
+ call s:check_tab(1, '.tab help', 2)
+ call s:check_tab(1, '.+1tab help', 3)
+ call s:check_tab(1, '0tab help', 1)
+ call s:check_tab(2, '+tab help', 4)
+ call s:check_tab(2, '+2tab help', 5)
+ call s:check_tab(4, '-tab help', 4)
+ call s:check_tab(4, '-2tab help', 3)
+ call s:check_tab(3, '$tab help', 6)
+ call assert_fails('99tab help', 'E16:')
+ call assert_fails('+99tab help', 'E16:')
+ call assert_fails('-99tab help', 'E16:')
+
+ delfunction s:check_tab
+ tabonly!
+ bw!
+endfunction
+
+func Test_tabnext_on_buf_unload1()
+ " This once caused a crash
+ new
+ tabedit
+ tabfirst
+ au BufUnload <buffer> tabnext
+ q
+
+ while tabpagenr('$') > 1
+ bwipe!
+ endwhile
+endfunc
+
+func Test_tabnext_on_buf_unload2()
+ " This once caused a crash
+ tabedit
+ autocmd BufUnload <buffer> tabnext
+ file x
+ edit y
+
+ while tabpagenr('$') > 1
+ bwipe!
+ endwhile
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 153b7fb5fd..a215001194 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -7,6 +7,7 @@
#include <assert.h>
#include <limits.h>
+#include "nvim/api/private/helpers.h"
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/iconv.h"
@@ -129,10 +130,10 @@ static int included_patches[] = {
// 2315,
// 2314,
// 2313,
- // 2312,
+ 2312,
// 2311,
// 2310 NA
- // 2309,
+ 2309,
// 2308 NA
// 2307,
// 2306,
@@ -204,7 +205,7 @@ static int included_patches[] = {
// 2240,
// 2239,
// 2238 NA
- // 2237,
+ 2237,
// 2236,
// 2235,
// 2234 NA
@@ -229,7 +230,7 @@ static int included_patches[] = {
// 2215,
// 2214 NA
2213,
- // 2212,
+ 2212,
// 2211 NA
// 2210 NA
// 2209,
@@ -313,7 +314,7 @@ static int included_patches[] = {
// 2131 NA
// 2130 NA
// 2129 NA
- // 2128,
+ 2128,
// 2127,
// 2126,
// 2125,
@@ -332,7 +333,7 @@ static int included_patches[] = {
2112,
// 2111,
// 2110,
- // 2109,
+ 2109,
// 2108 NA
// 2107,
// 2106,
@@ -2457,20 +2458,72 @@ static char *(extra_patches[]) = {
NULL
};
-/// Checks whether patch `n` has been included.
+/// Compares a version string to the current Nvim version.
///
-/// @param n The patch number.
+/// @param version Version string like "1.3.42"
///
-/// @return TRUE if patch "n" has been included.
-int has_patch(int n)
+/// @return true if Nvim is at or above the version.
+bool has_nvim_version(char *version_str)
+ FUNC_ATTR_NONNULL_ALL
{
- int i;
- for (i = 0; included_patches[i] != 0; ++i) {
+ char *p = version_str;
+ int major = 0;
+ int minor = 0;
+ int patch = 0;
+
+ if (!ascii_isdigit(*p)) {
+ return false;
+ }
+ major = atoi(p);
+ p = strchr(p, '.'); // Find the next dot.
+
+ if (p) {
+ p++; // Advance past the dot.
+ if (!ascii_isdigit(*p)) {
+ return false;
+ }
+ minor = atoi(p);
+ p = strchr(p, '.');
+ if (p) {
+ p++;
+ if (!ascii_isdigit(*p)) {
+ return false;
+ }
+ patch = atoi(p);
+ }
+ }
+
+ return (major < NVIM_VERSION_MAJOR
+ || (major == NVIM_VERSION_MAJOR
+ && (minor < NVIM_VERSION_MINOR
+ || (minor == NVIM_VERSION_MINOR
+ && patch <= NVIM_VERSION_PATCH))));
+}
+
+/// Checks whether a Vim patch has been included.
+///
+/// @param n Patch number.
+///
+/// @return true if patch `n` has been included.
+bool has_vim_patch(int n)
+{
+ for (int i = 0; included_patches[i] != 0; i++) {
if (included_patches[i] == n) {
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
+}
+
+Dictionary version_dict(void) {
+ Dictionary d = ARRAY_DICT_INIT;
+ PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR));
+ PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR));
+ PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH));
+ PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL));
+ PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT));
+ PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE));
+ return d;
}
void ex_version(exarg_T *eap)
@@ -2529,7 +2582,11 @@ static void list_features(void)
}
} else {
while (msg_col % width) {
+ int old_msg_col = msg_col;
msg_putchar(' ');
+ if (old_msg_col == msg_col) {
+ break; // XXX: Avoid infinite loop.
+ }
}
}
} else {
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 03a2e9a842..9c6a2e26a6 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1065,6 +1065,23 @@ bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
return false;
}
+/// Check if "win" is a pointer to an existing window in any tabpage.
+///
+/// @param win window to check
+bool win_valid_any_tab(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (win == NULL) {
+ return false;
+ }
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp == win) {
+ return true;
+ }
+ }
+ return false;
+}
+
/*
* Return the number of windows.
*/
@@ -1918,7 +1935,7 @@ int win_close(win_T *win, int free_buf)
if (win->w_buffer != NULL) {
win->w_closing = true;
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
- if (win_valid(win)) {
+ if (win_valid_any_tab(win)) {
win->w_closing = false;
}
@@ -1938,11 +1955,19 @@ int win_close(win_T *win, int free_buf)
curwin->w_buffer = curbuf;
getout(0);
}
- /* Autocommands may have closed the window already, or closed the only
- * other window or moved to another tab page. */
- else if (!win_valid(win) || last_window() || curtab != prev_curtab
- || close_last_window_tabpage(win, free_buf, prev_curtab))
+ // Autocommands may have moved to another tab page.
+ if (curtab != prev_curtab && win_valid_any_tab(win)
+ && win->w_buffer == NULL) {
+ // Need to close the window anyway, since the buffer is NULL.
+ win_close_othertab(win, false, prev_curtab);
return FAIL;
+ }
+ // Autocommands may have closed the window already, or closed the only
+ // other window or moved to another tab page.
+ if (!win_valid(win) || last_window()
+ || close_last_window_tabpage(win, free_buf, prev_curtab)) {
+ return FAIL;
+ }
// let terminal buffers know that this window dimensions may be ignored
win->w_closing = true;
@@ -2019,12 +2044,16 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
tabpage_T *ptp = NULL;
int free_tp = FALSE;
- assert(win->w_buffer); // to avoid np dereference warning in next line
- if (win->w_closing || win->w_buffer->b_closing)
- return; /* window is already being closed */
+ // Get here with win->w_buffer == NULL when win_close() detects the tab page
+ // changed.
+ if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) {
+ return; // window is already being closed
+ }
- /* Close the link to the buffer. */
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE);
+ if (win->w_buffer != NULL) {
+ // Close the link to the buffer.
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
+ }
/* Careful: Autocommands may have closed the tab page or made it the
* current tab page. */
@@ -3213,11 +3242,8 @@ void goto_tabpage(int n)
int i;
if (text_locked()) {
- /* Not allowed when editing the command line. */
- if (cmdwin_type != 0)
- EMSG(_(e_cmdwin));
- else
- EMSG(_(e_secure));
+ // Not allowed when editing the command line.
+ text_locked_msg();
return;
}