diff options
Diffstat (limited to 'src')
121 files changed, 7741 insertions, 4902 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8ec087c626..db77931c16 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -242,7 +242,7 @@ if(use_git_version) add_custom_target(update_version_stamp ALL COMMAND ${LUA_PRG} scripts/update_version_stamp.lua ${relbuild}/config/auto/versiondef_git.h - "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}" + "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}" WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS ${CMAKE_BINARY_DIR}/config/auto/versiondef_git.h) else() diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 1011f050fd..8d82d22040 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1,7 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// Much of this code was adapted from 'if_py_both.h' from the original +// Some of this code was adapted from 'if_py_both.h' from the original // vim source #include <stdbool.h> #include <stdint.h> @@ -80,34 +80,6 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) return buf->b_ml.ml_line_count; } -/// Gets a buffer line -/// -/// @deprecated use nvim_buf_get_lines instead. -/// for positive indices (including 0) use -/// "nvim_buf_get_lines(buffer, index, index+1, true)" -/// for negative indices use -/// "nvim_buf_get_lines(buffer, index-1, index, true)" -/// -/// @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 }; - - index = convert_index(index); - Array slice = nvim_buf_get_lines(0, buffer, index, index+1, true, err); - - if (!ERROR_SET(err) && slice.size) { - rv = slice.items[0].data.string; - } - - xfree(slice.items); - - return rv; -} - /// Activates buffer-update events on a channel, or as Lua callbacks. /// /// Example (Lua): capture buffer updates in a global `events` variable @@ -149,6 +121,8 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// - buffer handle /// - utf_sizes: include UTF-32 and UTF-16 size of the replaced /// region, as args to `on_lines`. +/// - preview: also attach to command preview (i.e. 'inccommand') +/// events. /// @param[out] err Error details, if any /// @return False if attach failed (invalid parameter, or buffer isn't loaded); /// otherwise True. TODO: LUA_API_NO_EVAL @@ -204,6 +178,12 @@ Boolean nvim_buf_attach(uint64_t channel_id, goto error; } cb.utf_sizes = v->data.boolean; + } else if (is_lua && strequal("preview", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, "preview must be boolean"); + goto error; + } + cb.preview = v->data.boolean; } else { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); goto error; @@ -258,68 +238,6 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, redraw_buf_range_later(buf, (linenr_T)first+1, (linenr_T)last); } -/// Sets a buffer line -/// -/// @deprecated use nvim_buf_set_lines instead. -/// for positive indices use -/// "nvim_buf_set_lines(buffer, index, index+1, true, [line])" -/// for negative indices use -/// "nvim_buf_set_lines(buffer, index-1, index, true, [line])" -/// -/// @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); - Array array = { .items = &l, .size = 1 }; - index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); -} - -/// Deletes a buffer line -/// -/// @deprecated use nvim_buf_set_lines instead. -/// for positive indices use -/// "nvim_buf_set_lines(buffer, index, index+1, true, [])" -/// for negative indices use -/// "nvim_buf_set_lines(buffer, index-1, index, true, [])" -/// @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; - index = convert_index(index); - nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); -} - -/// Retrieves a line range from the buffer -/// -/// @deprecated use nvim_buf_get_lines(buffer, newstart, newend, false) -/// 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 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, - Boolean include_start, - Boolean include_end, - Error *err) -{ - start = convert_index(start) + !include_start; - end = convert_index(end) + include_end; - return nvim_buf_get_lines(0, buffer, start , end, false, err); -} - /// Gets a line-range from the buffer. /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -391,36 +309,28 @@ end: return rv; } - -/// Replaces a line range on the buffer -/// -/// @deprecated use nvim_buf_set_lines(buffer, newstart, newend, false, lines) -/// 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 Buffer handle, or 0 for current buffer -/// @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, - Boolean include_start, - Boolean include_end, - ArrayOf(String) replacement, - Error *err) +static bool check_string_array(Array arr, bool disallow_nl, Error *err) { - start = convert_index(start) + !include_start; - end = convert_index(end) + include_end; - nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); + for (size_t i = 0; i < arr.size; i++) { + if (arr.items[i].type != kObjectTypeString) { + api_set_error(err, + kErrorTypeValidation, + "All items in the replacement array must be strings"); + return false; + } + // Disallow newlines in the middle of the line. + if (disallow_nl) { + const String l = arr.items[i].data.string; + if (memchr(l.data, NL, l.size)) { + api_set_error(err, kErrorTypeValidation, + "String cannot contain newlines"); + return false; + } + } + } + return true; } - /// Sets (replaces) a line-range in the buffer. /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -448,6 +358,7 @@ void nvim_buf_set_lines(uint64_t channel_id, ArrayOf(String) replacement, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -472,22 +383,9 @@ void nvim_buf_set_lines(uint64_t channel_id, return; } - for (size_t i = 0; i < replacement.size; i++) { - if (replacement.items[i].type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "All items in the replacement array must be strings"); - return; - } - // Disallow newlines in the middle of the line. - if (channel_id != VIML_INTERNAL_CALL) { - const String l = replacement.items[i].data.string; - if (memchr(l.data, NL, l.size)) { - api_set_error(err, kErrorTypeValidation, - "String cannot contain newlines"); - return; - } - } + bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); + if (!check_string_array(replacement, disallow_nl, err)) { + return; } size_t new_len = replacement.size; @@ -597,6 +495,250 @@ end: try_end(err); } +/// Sets (replaces) a range in the buffer +/// +/// This is recommended over nvim_buf_set_lines when only modifying parts of a +/// line, as extmarks will be preserved on non-modified parts of the touched +/// lines. +/// +/// Indexing is zero-based and end-exclusive. +/// +/// To insert text at a given index, set `start` and `end` ranges to the same +/// index. To delete a range, set `replacement` to an array containing +/// an empty string, or simply an empty array. +/// +/// Prefer nvim_buf_set_lines when adding or deleting entire lines only. +/// +/// @param channel_id +/// @param buffer Buffer handle, or 0 for current buffer +/// @param start_row First line index +/// @param start_column Last column +/// @param end_row Last line index +/// @param end_column Last column +/// @param replacement Array of lines to use as replacement +/// @param[out] err Error details, if any +void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, + Integer start_row, Integer start_col, + Integer end_row, Integer end_col, + ArrayOf(String) replacement, Error *err) + FUNC_API_SINCE(7) +{ + FIXED_TEMP_ARRAY(scratch, 1); + if (replacement.size == 0) { + scratch.items[0] = STRING_OBJ(STATIC_CSTR_AS_STRING("")); + replacement = scratch; + } + + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return; + } + + bool oob = false; + + // check range is ordered and everything! + // start_row, end_row within buffer len (except add text past the end?) + start_row = normalize_index(buf, start_row, &oob); + if (oob || start_row == buf->b_ml.ml_line_count + 1) { + api_set_error(err, kErrorTypeValidation, "start_row out of bounds"); + return; + } + + end_row = normalize_index(buf, end_row, &oob); + if (oob || end_row == buf->b_ml.ml_line_count + 1) { + api_set_error(err, kErrorTypeValidation, "end_row out of bounds"); + return; + } + + char *str_at_start = (char *)ml_get_buf(buf, start_row, false); + if (start_col < 0 || (size_t)start_col > strlen(str_at_start)) { + api_set_error(err, kErrorTypeValidation, "start_col out of bounds"); + return; + } + + char *str_at_end = (char *)ml_get_buf(buf, end_row, false); + size_t len_at_end = strlen(str_at_end); + if (end_col < 0 || (size_t)end_col > len_at_end) { + api_set_error(err, kErrorTypeValidation, "end_col out of bounds"); + return; + } + + if (start_row > end_row || (end_row == start_row && start_col > end_col)) { + api_set_error(err, kErrorTypeValidation, "start is higher than end"); + return; + } + + bool disallow_nl = (channel_id != VIML_INTERNAL_CALL); + if (!check_string_array(replacement, disallow_nl, err)) { + return; + } + + size_t new_len = replacement.size; + + bcount_t new_byte = 0; + bcount_t old_byte = 0; + + // calculate byte size of old region before it gets modified/deleted + if (start_row == end_row) { + old_byte = (bcount_t)end_col - start_col; + } else { + const char *bufline; + old_byte += (bcount_t)strlen(str_at_start) - start_col; + for (int64_t i = 1; i < end_row - start_row; i++) { + int64_t lnum = start_row + i; + + bufline = (char *)ml_get_buf(buf, lnum, false); + old_byte += (bcount_t)(strlen(bufline))+1; + } + old_byte += (bcount_t)end_col+1; + } + + String first_item = replacement.items[0].data.string; + String last_item = replacement.items[replacement.size-1].data.string; + + size_t firstlen = (size_t)start_col+first_item.size; + size_t last_part_len = strlen(str_at_end) - (size_t)end_col; + if (replacement.size == 1) { + firstlen += last_part_len; + } + char *first = xmallocz(firstlen), *last = NULL; + memcpy(first, str_at_start, (size_t)start_col); + memcpy(first+start_col, first_item.data, first_item.size); + memchrsub(first+start_col, NUL, NL, first_item.size); + if (replacement.size == 1) { + memcpy(first+start_col+first_item.size, str_at_end+end_col, last_part_len); + } else { + last = xmallocz(last_item.size+last_part_len); + memcpy(last, last_item.data, last_item.size); + memchrsub(last, NUL, NL, last_item.size); + memcpy(last+last_item.size, str_at_end+end_col, last_part_len); + } + + char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; + lines[0] = first; + new_byte += (bcount_t)(first_item.size); + for (size_t i = 1; i < new_len-1; i++) { + const String l = replacement.items[i].data.string; + + // Fill lines[i] with l's contents. Convert NULs to newlines as required by + // NL-used-for-NUL. + lines[i] = xmemdupz(l.data, l.size); + memchrsub(lines[i], NUL, NL, l.size); + new_byte += (bcount_t)(l.size)+1; + } + if (replacement.size > 1) { + lines[replacement.size-1] = last; + new_byte += (bcount_t)(last_item.size)+1; + } + + try_start(); + aco_save_T aco; + aucmd_prepbuf(&aco, (buf_T *)buf); + + if (!MODIFIABLE(buf)) { + api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'"); + goto end; + } + + // Small note about undo states: unlike set_lines, we want to save the + // undo state of one past the end_row, since end_row is inclusive. + if (u_save((linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to save undo information"); + goto end; + } + + ptrdiff_t extra = 0; // lines added to text, can be negative + size_t old_len = (size_t)(end_row-start_row+1); + + // If the size of the range is reducing (ie, new_len < old_len) we + // need to delete some old_len. We do this at the start, by + // repeatedly deleting line "start". + size_t to_delete = (new_len < old_len) ? (size_t)(old_len - new_len) : 0; + for (size_t i = 0; i < to_delete; i++) { + if (ml_delete((linenr_T)start_row, false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to delete line"); + goto end; + } + } + + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; + } + + // For as long as possible, replace the existing old_len with the + // new old_len. This is a more efficient operation, as it requires + // less memory allocation and freeing. + size_t to_replace = old_len < new_len ? old_len : new_len; + for (size_t i = 0; i < to_replace; i++) { + int64_t lnum = start_row + (int64_t)i; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Index value is too high"); + goto end; + } + + if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to replace line"); + goto end; + } + // Mark lines that haven't been passed to the buffer as they need + // to be freed later + lines[i] = NULL; + } + + // Now we may need to insert the remaining new old_len + for (size_t i = to_replace; i < new_len; i++) { + int64_t lnum = start_row + (int64_t)i - 1; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Index value is too high"); + goto end; + } + + if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to insert line"); + goto end; + } + + // Same as with replacing, but we also need to free lines + xfree(lines[i]); + lines[i] = NULL; + extra++; + } + + // Adjust marks. Invalidate any which lie in the + // changed range, and move any in the remainder of the buffer. + mark_adjust((linenr_T)start_row, + (linenr_T)end_row, + MAXLNUM, + (long)extra, + kExtmarkNOOP); + + colnr_T col_extent = (colnr_T)(end_col + - ((end_row == start_row) ? start_col : 0)); + extmark_splice(buf, (int)start_row-1, (colnr_T)start_col, + (int)(end_row-start_row), col_extent, old_byte, + (int)new_len-1, (colnr_T)last_item.size, new_byte, + kExtmarkUndo); + + + changed_lines((linenr_T)start_row, 0, (linenr_T)end_row, (long)extra, true); + + // adjust cursor like an extmark ( i e it was inside last_part_len) + if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) { + curwin->w_cursor.col -= col_extent - (colnr_T)last_item.size; + } + fix_cursor((linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra); + +end: + for (size_t i = 0; i < new_len; i++) { + xfree(lines[i]); + } + xfree(lines); + aucmd_restbuf(&aco); + try_end(err); +} + /// Returns the byte offset of a line (0-indexed). |api-indexing| /// /// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte. @@ -787,48 +929,6 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) dict_set_var(buf->b_vars, name, NIL, true, false, err); } -/// Sets a buffer-scoped (b:) variable -/// -/// @deprecated -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @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`. -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(buf->b_vars, name, value, false, true, err); -} - -/// Removes a buffer-scoped (b:) variable -/// -/// @deprecated -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @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); - - if (!buf) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(buf->b_vars, name, NIL, true, true, err); -} - /// Gets a buffer option value /// @@ -869,28 +969,6 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, set_option_to(channel_id, buf, SREQ_BUF, name, value, err); } -/// Gets the buffer number -/// -/// @deprecated The buffer number now is equal to the object id, -/// so there is no need to use this function. -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param[out] err Error details, if any -/// @return Buffer number -Integer nvim_buf_get_number(Buffer buffer, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(2) -{ - Integer rv = 0; - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return rv; - } - - return buf->b_fnum; -} - /// Gets the full file name for the buffer /// /// @param buffer Buffer handle, or 0 for current buffer @@ -962,6 +1040,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer) /// - unload: Unloaded only, do not delete. See |:bunload| void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) FUNC_API_SINCE(7) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1017,25 +1096,6 @@ Boolean nvim_buf_is_valid(Buffer buffer) return ret; } -/// Inserts a sequence of lines to a buffer at a certain index -/// -/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) -/// -/// @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, - Error *err) -{ - // "lnum" will be the index of the line after inserting, - // no matter if it is negative or not - nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); -} - /// Return a tuple (row,col) representing the position of the named mark. /// /// Marks are (1,0)-indexed. |api-indexing| @@ -1667,27 +1727,6 @@ void nvim_buf_clear_namespace(Buffer buffer, (int)line_end-1, MAXCOL); } -/// Clears highlights and virtual text from namespace and range of lines -/// -/// @deprecated use |nvim_buf_clear_namespace()|. -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param ns_id Namespace 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 Error details, if any -void nvim_buf_clear_highlight(Buffer buffer, - Integer ns_id, - Integer line_start, - Integer line_end, - Error *err) - FUNC_API_SINCE(1) -{ - nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); -} - - /// Set the virtual text (annotation) for a buffer line. /// /// By default (and currently the only option) the text will be placed after @@ -1873,8 +1912,3 @@ static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) index++; return index; } - -static int64_t convert_index(int64_t index) -{ - return index < 0 ? index - 1 : index; -} diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c new file mode 100644 index 0000000000..3989386bb9 --- /dev/null +++ b/src/nvim/api/deprecated.c @@ -0,0 +1,367 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <limits.h> + +#include "nvim/api/deprecated.h" +#include "nvim/api/buffer.h" +#include "nvim/api/vim.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" +#include "nvim/lua/executor.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/deprecated.c.generated.h" +#endif + +/// @deprecated +/// @see nvim_exec +String nvim_command_output(String command, Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(7) +{ + return nvim_exec(command, true, err); +} + +/// @deprecated Use nvim_exec_lua() instead. +/// @see nvim_exec_lua +Object nvim_execute_lua(String code, Array args, Error *err) + FUNC_API_SINCE(3) + FUNC_API_DEPRECATED_SINCE(7) + FUNC_API_REMOTE_ONLY +{ + return nlua_exec(code, args, err); +} + +/// Gets the buffer number +/// +/// @deprecated The buffer number now is equal to the object id, +/// so there is no need to use this function. +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param[out] err Error details, if any +/// @return Buffer number +Integer nvim_buf_get_number(Buffer buffer, Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(2) +{ + Integer rv = 0; + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return rv; + } + + return buf->b_fnum; +} + +/// Clears highlights and virtual text from namespace and range of lines +/// +/// @deprecated use |nvim_buf_clear_namespace()|. +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param ns_id Namespace 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 Error details, if any +void nvim_buf_clear_highlight(Buffer buffer, + Integer ns_id, + Integer line_start, + Integer line_end, + Error *err) + FUNC_API_SINCE(1) + FUNC_API_DEPRECATED_SINCE(7) +{ + nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); +} + + +/// Inserts a sequence of lines to a buffer at a certain index +/// +/// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) +/// +/// @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, + Error *err) +{ + // "lnum" will be the index of the line after inserting, + // no matter if it is negative or not + nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err); +} + +/// Gets a buffer line +/// +/// @deprecated use nvim_buf_get_lines instead. +/// for positive indices (including 0) use +/// "nvim_buf_get_lines(buffer, index, index+1, true)" +/// for negative indices use +/// "nvim_buf_get_lines(buffer, index-1, index, true)" +/// +/// @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 }; + + index = convert_index(index); + Array slice = nvim_buf_get_lines(0, buffer, index, index+1, true, err); + + if (!ERROR_SET(err) && slice.size) { + rv = slice.items[0].data.string; + } + + xfree(slice.items); + + return rv; +} + +/// Sets a buffer line +/// +/// @deprecated use nvim_buf_set_lines instead. +/// for positive indices use +/// "nvim_buf_set_lines(buffer, index, index+1, true, [line])" +/// for negative indices use +/// "nvim_buf_set_lines(buffer, index-1, index, true, [line])" +/// +/// @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); + Array array = { .items = &l, .size = 1 }; + index = convert_index(index); + nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); +} + +/// Deletes a buffer line +/// +/// @deprecated use nvim_buf_set_lines instead. +/// for positive indices use +/// "nvim_buf_set_lines(buffer, index, index+1, true, [])" +/// for negative indices use +/// "nvim_buf_set_lines(buffer, index-1, index, true, [])" +/// @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; + index = convert_index(index); + nvim_buf_set_lines(0, buffer, index, index+1, true, array, err); +} + +/// Retrieves a line range from the buffer +/// +/// @deprecated use nvim_buf_get_lines(buffer, newstart, newend, false) +/// 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 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, + Boolean include_start, + Boolean include_end, + Error *err) +{ + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + return nvim_buf_get_lines(0, buffer, start , end, false, err); +} + +/// Replaces a line range on the buffer +/// +/// @deprecated use nvim_buf_set_lines(buffer, newstart, newend, false, lines) +/// 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 Buffer handle, or 0 for current buffer +/// @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, + Boolean include_start, + Boolean include_end, + ArrayOf(String) replacement, + Error *err) +{ + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + nvim_buf_set_lines(0, buffer, start, end, false, replacement, err); +} + + +/// Sets a buffer-scoped (b:) variable +/// +/// @deprecated +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @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`. +Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return NIL; + } + + return dict_set_var(buf->b_vars, name, value, false, true, err); +} + +/// Removes a buffer-scoped (b:) variable +/// +/// @deprecated +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @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); + + if (!buf) { + return NIL; + } + + return dict_set_var(buf->b_vars, name, NIL, true, true, err); +} + +/// Sets a window-scoped (w:) variable +/// +/// @deprecated +/// +/// @param window Window handle, or 0 for current window +/// @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`. +Object window_set_var(Window window, String name, Object value, Error *err) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return NIL; + } + + return dict_set_var(win->w_vars, name, value, false, true, err); +} + +/// Removes a window-scoped (w:) variable +/// +/// @deprecated +/// +/// @param window Window handle, or 0 for current window +/// @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); + + if (!win) { + return NIL; + } + + return dict_set_var(win->w_vars, name, NIL, true, true, err); +} + +/// Sets a tab-scoped (t:) variable +/// +/// @deprecated +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @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`. +Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) +{ + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return NIL; + } + + return dict_set_var(tab->tp_vars, name, value, false, true, err); +} + +/// Removes a tab-scoped (t:) variable +/// +/// @deprecated +/// +/// @param tabpage Tabpage handle, or 0 for current tabpage +/// @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); + + if (!tab) { + return NIL; + } + + return dict_set_var(tab->tp_vars, name, NIL, true, true, err); +} + +/// @deprecated +/// @see nvim_set_var +/// @warning May return nil if there was no previous value +/// OR if previous value was `v:null`. +/// @return Old value or nil if there was no previous value. +Object vim_set_var(String name, Object value, Error *err) +{ + return dict_set_var(&globvardict, name, value, false, true, err); +} + +/// @deprecated +/// @see nvim_del_var +Object vim_del_var(String name, Error *err) +{ + return dict_set_var(&globvardict, name, NIL, true, true, err); +} + +static int64_t convert_index(int64_t index) +{ + return index < 0 ? index - 1 : index; +} diff --git a/src/nvim/api/deprecated.h b/src/nvim/api/deprecated.h new file mode 100644 index 0000000000..79095167e1 --- /dev/null +++ b/src/nvim/api/deprecated.h @@ -0,0 +1,11 @@ +#ifndef NVIM_API_DEPRECATED_H +#define NVIM_API_DEPRECATED_H + +#include <stdint.h> + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/deprecated.h.generated.h" +#endif +#endif // NVIM_API_DEPRECATED_H diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 2975df3c68..eae4581f42 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -19,6 +19,7 @@ #include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/api/window.h" +#include "nvim/api/deprecated.h" static Map(String, MsgpackRpcRequestHandler) *methods = NULL; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2c99d3426c..8f224e8c78 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -820,6 +820,10 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, } buf_T *target_buf = find_buffer_by_handle(buffer, err); + if (!target_buf) { + return; + } + MapArguments parsed_args; memset(&parsed_args, 0, sizeof(parsed_args)); if (parse_keymap_opts(opts, &parsed_args, err)) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 271fd5b485..055abb797f 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -16,6 +16,7 @@ #define BOOLEAN_OBJ(b) ((Object) { \ .type = kObjectTypeBoolean, \ .data.boolean = b }) +#define BOOL(b) BOOLEAN_OBJ(b) #define INTEGER_OBJ(i) ((Object) { \ .type = kObjectTypeInteger, \ @@ -29,6 +30,8 @@ .type = kObjectTypeString, \ .data.string = s }) +#define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) + #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ .data.integer = s }) @@ -59,6 +62,8 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); + #define ADD(array, item) \ kv_push(array, item) diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index dd17bc03e5..5f727dbc38 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -97,48 +97,6 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) dict_set_var(tab->tp_vars, name, NIL, true, false, err); } -/// Sets a tab-scoped (t:) variable -/// -/// @deprecated -/// -/// @param tabpage Tabpage handle, or 0 for current tabpage -/// @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`. -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) -{ - tabpage_T *tab = find_tab_by_handle(tabpage, err); - - if (!tab) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(tab->tp_vars, name, value, false, true, err); -} - -/// Removes a tab-scoped (t:) variable -/// -/// @deprecated -/// -/// @param tabpage Tabpage handle, or 0 for current tabpage -/// @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); - - if (!tab) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(tab->tp_vars, name, NIL, true, true, err); -} - /// Gets the current window in a tabpage /// /// @param tabpage Tabpage handle, or 0 for current tabpage diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 77002697fe..1e972e01be 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -15,6 +15,7 @@ #include "nvim/api/private/dispatch.h" #include "nvim/api/buffer.h" #include "nvim/api/window.h" +#include "nvim/api/deprecated.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" @@ -479,15 +480,6 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, return cstr_as_string(ptr); } -/// @deprecated -/// @see nvim_exec -String nvim_command_output(String command, Error *err) - FUNC_API_SINCE(1) - FUNC_API_DEPRECATED_SINCE(7) -{ - return nvim_exec(command, true, err); -} - /// Evaluates a VimL |expression|. /// Dictionaries and Lists are recursively expanded. /// @@ -534,16 +526,6 @@ Object nvim_eval(String expr, Error *err) return rv; } -/// @deprecated Use nvim_exec_lua() instead. -/// @see nvim_exec_lua -Object nvim_execute_lua(String code, Array args, Error *err) - FUNC_API_SINCE(3) - FUNC_API_DEPRECATED_SINCE(7) - FUNC_API_REMOTE_ONLY -{ - return nlua_exec(code, args, err); -} - /// Execute Lua code. Parameters (if any) are available as `...` inside the /// chunk. The chunk can return a value. /// @@ -875,6 +857,7 @@ String nvim_get_current_line(Error *err) /// @param[out] err Error details, if any void nvim_set_current_line(String line, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); } @@ -884,6 +867,7 @@ void nvim_set_current_line(String line, Error *err) /// @param[out] err Error details, if any void nvim_del_current_line(Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } @@ -920,23 +904,6 @@ void nvim_del_var(String name, Error *err) dict_set_var(&globvardict, name, NIL, true, false, err); } -/// @deprecated -/// @see nvim_set_var -/// @warning May return nil if there was no previous value -/// OR if previous value was `v:null`. -/// @return Old value or nil if there was no previous value. -Object vim_set_var(String name, Object value, Error *err) -{ - return dict_set_var(&globvardict, name, value, false, true, err); -} - -/// @deprecated -/// @see nvim_del_var -Object vim_del_var(String name, Error *err) -{ - return dict_set_var(&globvardict, name, NIL, true, true, err); -} - /// Gets a v: variable. /// /// @param name Variable name @@ -970,6 +937,47 @@ Object nvim_get_option(String name, Error *err) return get_option_from(NULL, SREQ_GLOBAL, name, err); } +/// Gets the option information for all options. +/// +/// The dictionary has the full option names as keys and option metadata +/// dictionaries as detailed at |nvim_get_option_info|. +/// +/// @return dictionary of all options +Dictionary nvim_get_all_options_info(Error *err) + FUNC_API_SINCE(7) +{ + return get_all_vimoptions(); +} + +/// Gets the option information for one option +/// +/// Resulting dictionary has keys: +/// - name: Name of the option (like 'filetype') +/// - shortname: Shortened name of the option (like 'ft') +/// - type: type of option ("string", "integer" or "boolean") +/// - default: The default value for the option +/// - was_set: Whether the option was set. +/// +/// - last_set_sid: Last set script id (if any) +/// - last_set_linenr: line number where option was set +/// - last_set_chan: Channel where option was set (0 for local) +/// +/// - scope: one of "global", "win", or "buf" +/// - global_local: whether win or buf option has a global value +/// +/// - commalist: List of comma separated values +/// - flaglist: List of single char flags +/// +/// +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option Information +Dictionary nvim_get_option_info(String name, Error *err) + FUNC_API_SINCE(7) +{ + return get_vimoption(name, err); +} + /// Sets an option value. /// /// @param channel_id @@ -1054,6 +1062,7 @@ Buffer nvim_get_current_buf(void) /// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1108,6 +1117,7 @@ Window nvim_get_current_win(void) /// @param[out] err Error details, if any void nvim_set_current_win(Window window, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err); @@ -1257,6 +1267,7 @@ fail: Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { FloatConfig fconfig = FLOAT_CONFIG_INIT; if (!parse_float_config(config, &fconfig, false, err)) { @@ -1321,6 +1332,7 @@ Tabpage nvim_get_current_tabpage(void) /// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) + FUNC_API_CHECK_TEXTLOCK { tabpage_T *tp = find_tab_by_handle(tabpage, err); @@ -1405,6 +1417,7 @@ Dictionary nvim_get_namespaces(void) /// - false: Client must cancel the paste. Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { static bool draining = false; bool cancel = false; @@ -1477,6 +1490,7 @@ theend: void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); if (!prepare_yankreg_from_object(reg, type, lines.size)) { diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index f09a03f592..f4af1632ec 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -44,6 +44,7 @@ Buffer nvim_win_get_buf(Window window, Error *err) /// @param[out] err Error details, if any void nvim_win_set_buf(Window window, Buffer buffer, Error *err) FUNC_API_SINCE(5) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -288,48 +289,6 @@ void nvim_win_del_var(Window window, String name, Error *err) dict_set_var(win->w_vars, name, NIL, true, false, err); } -/// Sets a window-scoped (w:) variable -/// -/// @deprecated -/// -/// @param window Window handle, or 0 for current window -/// @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`. -Object window_set_var(Window window, String name, Object value, Error *err) -{ - win_T *win = find_window_by_handle(window, err); - - if (!win) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(win->w_vars, name, value, false, true, err); -} - -/// Removes a window-scoped (w:) variable -/// -/// @deprecated -/// -/// @param window Window handle, or 0 for current window -/// @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); - - if (!win) { - return (Object) OBJECT_INIT; - } - - return dict_set_var(win->w_vars, name, NIL, true, true, err); -} - /// Gets a window option value /// /// @param window Window handle, or 0 for current window @@ -542,6 +501,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) /// @param[out] err Error details, if any void nvim_win_close(Window window, Boolean force, Error *err) FUNC_API_SINCE(6) + FUNC_API_CHECK_TEXTLOCK { win_T *win = find_window_by_handle(window, err); if (!win) { diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 7f12c0c798..9fba38a49f 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -719,9 +719,7 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, // half-shape current and previous character int shape_c = half_shape(prev_c); - // Save away current character - int curr_c = c; - + int curr_c; int curr_laa = A_firstc_laa(c, *c1p); int prev_laa = A_firstc_laa(prev_c, prev_c1); diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 2397af27cc..f41068ea70 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -89,6 +89,10 @@ static inline bool ascii_iswhite(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +static inline bool ascii_iswhite_or_nul(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + static inline bool ascii_isdigit(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; @@ -117,6 +121,14 @@ static inline bool ascii_iswhite(int c) return c == ' ' || c == '\t'; } +/// Checks if `c` is a space or tab character or NUL. +/// +/// @see {ascii_isdigit} +static inline bool ascii_iswhite_or_nul(int c) +{ + return ascii_iswhite(c) || c == NUL; +} + /// Check whether character is a decimal digit. /// /// Library isdigit() function is officially locale-dependent and, for diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 53b11c250e..42224d0a4f 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1106,9 +1106,9 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) win = curwin; } - aco->save_curwin = curwin; - aco->save_prevwin = prevwin; + aco->save_curwin_handle = curwin->handle; aco->save_curbuf = curbuf; + aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle; if (win != NULL) { // There is a window for "buf" in the current tab page, make it the // curwin. This is preferred, it has the least side effects (esp. if @@ -1148,7 +1148,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) curwin = aucmd_win; } curbuf = buf; - aco->new_curwin = curwin; + aco->new_curwin_handle = curwin->handle; set_bufref(&aco->new_curbuf, curbuf); } @@ -1194,14 +1194,14 @@ void aucmd_restbuf(aco_save_T *aco) unblock_autocmds(); - if (win_valid(aco->save_curwin)) { - curwin = aco->save_curwin; + win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); + if (save_curwin != NULL) { + curwin = save_curwin; } else { // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } - prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin - : firstwin; // window disappeared? + prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab curbuf = curwin->w_buffer; @@ -1216,11 +1216,14 @@ void aucmd_restbuf(aco_save_T *aco) curwin->w_topfill = 0; } } else { - // restore curwin - if (win_valid(aco->save_curwin)) { + // Restore curwin. Use the window ID, a window may have been closed + // and the memory re-used for another one. + win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); + if (save_curwin != NULL) { // Restore the buffer which was previously edited by curwin, if it was // changed, we are still the same window and the buffer is valid. - if (curwin == aco->new_curwin && curbuf != aco->new_curbuf.br_buf + if (curwin->handle == aco->new_curwin_handle + && curbuf != aco->new_curbuf.br_buf && bufref_valid(&aco->new_curbuf) && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) { if (curwin->w_s == &curbuf->b_s) { @@ -1232,10 +1235,9 @@ void aucmd_restbuf(aco_save_T *aco) curbuf->b_nwindows++; } - curwin = aco->save_curwin; - prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin - : firstwin; // window disappeared? + curwin = save_curwin; curbuf = curwin->w_buffer; + prevwin = win_find_by_handle(aco->save_prevwin_handle); // In case the autocommand moves the cursor to a position that does not // exist in curbuf check_cursor(); @@ -1717,7 +1719,8 @@ void unblock_autocmds(void) } } -static inline bool is_autocmd_blocked(void) +bool is_autocmd_blocked(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return autocmd_blocked != 0; } diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index af1eeb0fc4..1c0f88f08f 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -7,13 +7,13 @@ // Struct to save values in before executing autocommands for a buffer that is // not the current buffer. typedef struct { - buf_T *save_curbuf; ///< saved curbuf - int use_aucmd_win; ///< using aucmd_win - win_T *save_curwin; ///< saved curwin - win_T *save_prevwin; ///< saved prevwin - win_T *new_curwin; ///< new curwin - bufref_T new_curbuf; ///< new curbuf - char_u *globaldir; ///< saved value of globaldir + buf_T *save_curbuf; ///< saved curbuf + bool use_aucmd_win; ///< using aucmd_win + handle_T save_curwin_handle; ///< ID of saved curwin + handle_T new_curwin_handle; ///< ID of new curwin + handle_T save_prevwin_handle; ///< ID of saved prevwin + bufref_T new_curbuf; ///< new curbuf + char_u *globaldir; ///< saved value of globaldir } aco_save_T; typedef struct AutoCmd { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 839d61cd2e..0134ee838d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -602,8 +602,12 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) * Remove the buffer from the list. */ if (wipe_buf) { - xfree(buf->b_ffname); - xfree(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } + XFREE_CLEAR(buf->b_ffname); if (buf->b_prev == NULL) { firstbuf = buf->b_next; } else { @@ -1583,7 +1587,7 @@ void enter_buffer(buf_T *buf) need_fileinfo = true; // display file info after redraw } // check if file changed - (void)buf_check_timestamp(curbuf, false); + (void)buf_check_timestamp(curbuf); curwin->w_topline = 1; curwin->w_topfill = 0; @@ -1693,15 +1697,18 @@ static inline void buf_init_changedtick(buf_T *const buf) /// if the buffer already exists. /// This is the ONLY way to create a new buffer. /// -/// @param ffname full path of fname or relative -/// @param sfname short fname or NULL +/// @param ffname_arg full path of fname or relative +/// @param sfname_arg short fname or NULL /// @param lnum preferred cursor line /// @param flags BLN_ defines /// @param bufnr /// /// @return pointer to the buffer -buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) +buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, + int flags) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; buf_T *buf; fname_expand(curbuf, &ffname, &sfname); // will allocate ffname @@ -1787,8 +1794,12 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) buf->b_wininfo = xcalloc(1, sizeof(wininfo_T)); if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) { + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } XFREE_CLEAR(buf->b_ffname); - XFREE_CLEAR(buf->b_sfname); if (buf != curbuf) { free_buffer(buf); } @@ -2148,10 +2159,11 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, int buflist_findpat( const char_u *pattern, const char_u *pattern_end, // pointer to first char after pattern - int unlisted, // find unlisted buffers - int diffmode, // find diff-mode buffers only - int curtab_only // find buffers in current tab only + bool unlisted, // find unlisted buffers + bool diffmode, // find diff-mode buffers only + bool curtab_only // find buffers in current tab only ) + FUNC_ATTR_NONNULL_ARG(1) { int match = -1; int find_listed; @@ -2778,28 +2790,32 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum) return OK; } -/* - * Set the file name for "buf"' to 'ffname', short file name to 'sfname'. - * The file name with the full path is also remembered, for when :cd is used. - * Returns FAIL for failure (file name already in use by other buffer) - * OK otherwise. - */ -int -setfname( +// Set the file name for "buf" to "ffname_arg", short file name to +// "sfname_arg". +// The file name with the full path is also remembered, for when :cd is used. +// Returns FAIL for failure (file name already in use by other buffer) +// OK otherwise. +int setfname( buf_T *buf, - char_u *ffname, - char_u *sfname, + char_u *ffname_arg, + char_u *sfname_arg, bool message // give message when buffer already exists ) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; buf_T *obuf = NULL; FileID file_id; bool file_id_valid = false; if (ffname == NULL || *ffname == NUL) { // Removing the name. + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } else { + buf->b_sfname = NULL; + } XFREE_CLEAR(buf->b_ffname); - XFREE_CLEAR(buf->b_sfname); } else { fname_expand(buf, &ffname, &sfname); // will allocate ffname if (ffname == NULL) { // out of memory @@ -2830,8 +2846,10 @@ setfname( #ifdef USE_FNAME_CASE path_fix_case(sfname); // set correct case for short file name #endif + if (buf->b_sfname != buf->b_ffname) { + xfree(buf->b_sfname); + } xfree(buf->b_ffname); - xfree(buf->b_sfname); buf->b_ffname = ffname; buf->b_sfname = sfname; } @@ -2857,7 +2875,9 @@ void buf_set_name(int fnum, char_u *name) buf = buflist_findnr(fnum); if (buf != NULL) { - xfree(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + xfree(buf->b_sfname); + } xfree(buf->b_ffname); buf->b_ffname = vim_strsave(name); buf->b_sfname = NULL; @@ -4633,16 +4653,18 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) return true; } -/* - * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL. - * "ffname" becomes a pointer to allocated memory (or NULL). - */ +// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. +// "*ffname" becomes a pointer to allocated memory (or NULL). +// When resolving a link both "*sfname" and "*ffname" will point to the same +// allocated memory. +// The "*ffname" and "*sfname" pointer values on call will not be freed. +// Note that the resulting "*ffname" pointer should be considered not allocaed. void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) { - if (*ffname == NULL) { // if no file name given, nothing to do + if (*ffname == NULL) { // no file name given, nothing to do return; } - if (*sfname == NULL) { // if no short file name given, use ffname + if (*sfname == NULL) { // no short file name given, use ffname *sfname = *ffname; } *ffname = (char_u *)fix_fname((char *)(*ffname)); // expand to full path @@ -4685,7 +4707,6 @@ do_arg_all( int keep_tabs // keep current tabs, for ":tab drop file" ) { - int i; char_u *opened; // Array of weight for which args are open: // 0: not opened // 1: opened in other tab @@ -4694,6 +4715,7 @@ do_arg_all( int opened_len; // length of opened[] int use_firstwin = false; // use first window for arglist + bool tab_drop_empty_window = false; int split_ret = OK; bool p_ea_save; alist_T *alist; // argument list to be used @@ -4741,6 +4763,7 @@ do_arg_all( win_T *wpnext = NULL; tpnext = curtab->tp_next; for (win_T *wp = firstwin; wp != NULL; wp = wpnext) { + int i; wpnext = wp->w_next; buf = wp->w_buffer; if (buf->b_ffname == NULL @@ -4846,14 +4869,15 @@ do_arg_all( last_curwin = curwin; last_curtab = curtab; win_enter(lastwin, false); - // ":drop all" should re-use an empty window to avoid "--remote-tab" + // ":tab drop file" should re-use an empty window to avoid "--remote-tab" // leaving an empty tab page when executed locally. if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1 && curbuf->b_ffname == NULL && !curbuf->b_changed) { use_firstwin = true; + tab_drop_empty_window = true; } - for (i = 0; i < count && i < opened_len && !got_int; i++) { + for (int i = 0; i < count && !got_int; i++) { if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) { arg_had_last = true; } @@ -4873,6 +4897,10 @@ do_arg_all( } } } else if (split_ret == OK) { + // trigger events for tab drop + if (tab_drop_empty_window && i == count - 1) { + autocmd_no_enter--; + } if (!use_firstwin) { // split current window p_ea_save = p_ea; p_ea = true; // use space from all windows @@ -4898,6 +4926,9 @@ do_arg_all( || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF, curwin); + if (tab_drop_empty_window && i == count - 1) { + autocmd_no_enter++; + } if (use_firstwin) { autocmd_no_leave++; } @@ -5449,7 +5480,9 @@ int buf_signcols(buf_T *buf) curline = sign->lnum; linesum = 0; } - linesum++; + if (sign->has_text_or_icon) { + linesum++; + } } if (linesum > buf->b_signcols_max) { buf->b_signcols_max = linesum; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index dba02a67e8..cc09b7e989 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -491,9 +491,10 @@ typedef struct { LuaRef on_changedtick; LuaRef on_detach; bool utf_sizes; + bool preview; } BufUpdateCallbacks; #define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, false } + LUA_NOREF, false, false } EXTERN int curbuf_splice_pending INIT(= 0); @@ -532,9 +533,11 @@ struct file_buffer { // b_fname is the same as b_sfname, unless ":cd" has been done, // then it is the same as b_ffname (NULL for no name). // - char_u *b_ffname; // full path file name - char_u *b_sfname; // short file name - char_u *b_fname; // current file name + char_u *b_ffname; // full path file name, allocated + char_u *b_sfname; // short file name, allocated, may be equal to + // b_ffname + char_u *b_fname; // current file name, points to b_ffname or + // b_sfname bool file_id_valid; FileID file_id; diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index fc671ad9e2..68e123896b 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -237,7 +237,7 @@ void buf_updates_send_changes(buf_T *buf, for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_lines != LUA_NOREF) { + if (cb.on_lines != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) { Array args = ARRAY_DICT_INIT; Object items[8]; args.size = 6; // may be increased to 8 below @@ -298,7 +298,7 @@ void buf_updates_send_splice( for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); bool keep = true; - if (cb.on_bytes != LUA_NOREF) { + if (cb.on_bytes != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) { FIXED_TEMP_ARRAY(args, 11); // the first argument is always the buffer handle diff --git a/src/nvim/charset.c b/src/nvim/charset.c index fb158f377a..3e52b3e3ce 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -45,8 +45,6 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) #define GET_CHARTAB_TAB(chartab, c) \ ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) -#define GET_CHARTAB(buf, c) \ - GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1cdf84f9d0..93bc34fa4b 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -805,7 +805,7 @@ static void diff_try_update(diffio_T *dio, for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { buf = curtab->tp_diffbuf[idx_new]; if (buf_valid(buf)) { - buf_check_timestamp(buf, false); + buf_check_timestamp(buf); } } } @@ -1225,8 +1225,7 @@ void ex_diffpatch(exarg_T *eap) EMSG(_("E816: Cannot read patch output")); } else { if (curbuf->b_fname != NULL) { - newname = vim_strnsave(curbuf->b_fname, - (int)(STRLEN(curbuf->b_fname) + 4)); + newname = vim_strnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4); STRCAT(newname, ".new"); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5d44c3274e..b2abb06075 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -196,8 +196,8 @@ static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; static char_u *compl_pattern = NULL; -static int compl_direction = FORWARD; -static int compl_shows_dir = FORWARD; +static Direction compl_direction = FORWARD; +static Direction compl_shows_dir = FORWARD; static int compl_pending = 0; // > 1 for postponed CTRL-N static pos_T compl_startpos; static colnr_T compl_col = 0; /* column where the text starts @@ -381,7 +381,7 @@ static void insert_enter(InsertState *s) // Need to recompute the cursor position, it might move when the cursor is // on a TAB or special character. - curs_columns(true); + curs_columns(curwin, true); // Enable langmap or IME, indicated by 'iminsert'. // Note that IME may enabled/disabled without us noticing here, thus the @@ -594,7 +594,7 @@ static int insert_check(VimState *state) if (curwin->w_wcol < s->mincol - curbuf->b_p_ts && curwin->w_wrow == curwin->w_winrow - + curwin->w_height_inner - 1 - get_scrolloff_value() + + curwin->w_height_inner - 1 - get_scrolloff_value(curwin) && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { @@ -608,7 +608,7 @@ static int insert_check(VimState *state) } // May need to adjust w_topline to show the cursor. - update_topline(); + update_topline(curwin); s->did_backspace = false; @@ -1561,7 +1561,7 @@ void edit_putchar(int c, bool highlight) int attr; if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) { - update_topline(); // just in case w_topline isn't valid + update_topline(curwin); // just in case w_topline isn't valid validate_cursor(); if (highlight) { attr = HL_ATTR(HLF_8); @@ -1677,7 +1677,7 @@ void display_dollar(colnr_T col) // If on the last byte of a multi-byte move to the first byte. char_u *p = get_cursor_line_ptr(); curwin->w_cursor.col -= utf_head_off(p, p + col); - curs_columns(false); // Recompute w_wrow and w_wcol + curs_columns(curwin, false); // Recompute w_wrow and w_wcol if (curwin->w_wcol < curwin->w_grid.Columns) { edit_putchar('$', false); dollar_vcol = curwin->w_virtcol; @@ -2156,7 +2156,7 @@ static bool ins_compl_accept_char(int c) /// /// @param[in] cont_s_ipos next ^X<> will set initial_pos int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, - int dir, bool cont_s_ipos) + Direction dir, bool cont_s_ipos) FUNC_ATTR_NONNULL_ARG(1) { char_u *str = str_arg; @@ -2308,7 +2308,7 @@ static int ins_compl_add(char_u *const str, int len, FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); + const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); int flags = flags_arg; os_breakcheck(); @@ -2511,7 +2511,7 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) FUNC_ATTR_NONNULL_ALL { int add_r = OK; - int dir = compl_direction; + Direction dir = compl_direction; for (int i = 0; i < num_matches && add_r != FAIL; i++) { if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, @@ -2864,7 +2864,7 @@ ins_compl_dictionaries ( char_u **files; int count; int save_p_scs; - int dir = compl_direction; + Direction dir = compl_direction; if (*dict == NUL) { /* When 'dictionary' is empty and spell checking is enabled use @@ -2945,7 +2945,10 @@ theend: xfree(buf); } -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir) +static void ins_compl_files(int count, char_u **files, int thesaurus, + int flags, regmatch_T *regmatch, char_u *buf, + Direction *dir) + FUNC_ATTR_NONNULL_ARG(2, 7) { char_u *ptr; int i; @@ -2955,6 +2958,7 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, for (i = 0; i < count && !got_int && !compl_interrupted; i++) { fp = os_fopen((char *)files[i], "r"); // open dictionary file if (flags != DICT_EXACT) { + msg_hist_off = true; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning dictionary: %s"), (char *)files[i]); (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); @@ -3137,6 +3141,56 @@ bool ins_compl_active(void) return compl_started; } +static void ins_compl_update_sequence_numbers(void) +{ + int number = 0; + compl_T *match; + + if (compl_direction == FORWARD) { + // search backwards for the first valid (!= -1) number. + // This should normally succeed already at the first loop + // cycle, so it's fast! + for (match = compl_curr_match->cp_prev; + match != NULL && match != compl_first_match; + match = match->cp_prev) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go up and assign all numbers which are not assigned yet + for (match = match->cp_next; + match != NULL && match->cp_number == -1; + match = match->cp_next) { + match->cp_number = ++number; + } + } + } else { // BACKWARD + assert(compl_direction == BACKWARD); + // search forwards (upwards) for the first valid (!= -1) + // number. This should normally succeed already at the + // first loop cycle, so it's fast! + for (match = compl_curr_match->cp_next; + match != NULL && match != compl_first_match; + match = match->cp_next) { + if (match->cp_number != -1) { + number = match->cp_number; + break; + } + } + if (match != NULL) { + // go down and assign all numbers which are not + // assigned yet + for (match = match->cp_prev; + match && match->cp_number == -1; + match = match->cp_prev) { + match->cp_number = ++number; + } + } + } +} + // Get complete information void get_complete_info(list_T *what_list, dict_T *retdict) { @@ -3214,6 +3268,9 @@ void get_complete_info(list_T *what_list, dict_T *retdict) } if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { + ins_compl_update_sequence_numbers(); + } ret = tv_dict_add_nr(retdict, S_LEN("selected"), (compl_curr_match != NULL) ? compl_curr_match->cp_number - 1 : -1); @@ -3366,7 +3423,7 @@ static void ins_compl_addleader(int c) xfree(compl_leader); compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col, - (int)(curwin->w_cursor.col - compl_col)); + curwin->w_cursor.col - compl_col); ins_compl_new_leader(); } @@ -3659,7 +3716,7 @@ static bool ins_compl_prep(int c) retval = true; } - auto_format(FALSE, TRUE); + auto_format(false, true); // Trigger the CompleteDonePre event to give scripts a chance to // act upon the completion before clearing the info, and restore @@ -3753,10 +3810,10 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) */ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) { - static win_T *wp; + static win_T *wp = NULL; if (flag == 'w') { // just windows - if (buf == curbuf) { // first call for this flag/expansion + if (buf == curbuf || wp == NULL) { // first call for this flag/expansion wp = curwin; } assert(wp); @@ -3865,7 +3922,7 @@ theend: */ static void ins_compl_add_list(list_T *const list) { - int dir = compl_direction; + Direction dir = compl_direction; // Go through the List with matches and add each of them. TV_LIST_ITER(list, li, { @@ -4057,6 +4114,7 @@ static int ins_compl_get_exp(pos_T *ini) dict = ins_buf->b_fname; dict_f = DICT_EXACT; } + msg_hist_off = true; // reset in msg_trunc_attr() vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), ins_buf->b_fname == NULL ? buf_spname(ins_buf) @@ -4078,11 +4136,12 @@ static int ins_compl_get_exp(pos_T *ini) dict = e_cpt; dict_f = DICT_FIRST; } - } else if (*e_cpt == 'i') + } else if (*e_cpt == 'i') { type = CTRL_X_PATH_PATTERNS; - else if (*e_cpt == 'd') + } else if (*e_cpt == 'd') { type = CTRL_X_PATH_DEFINES; - else if (*e_cpt == ']' || *e_cpt == 't') { + } else if (*e_cpt == ']' || *e_cpt == 't') { + msg_hist_off = true; // reset in msg_trunc_attr() type = CTRL_X_TAGS; vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags.")); (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); @@ -4665,9 +4724,11 @@ ins_compl_next ( MB_PTR_ADV(s); } } + msg_hist_off = true; vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead, s > compl_shown_match->cp_fname ? "<" : "", s); msg(IObuff); + msg_hist_off = false; redraw_cmdline = false; // don't overwrite! } } @@ -5242,52 +5303,11 @@ static int ins_complete(int c, bool enable_pum) } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { edit_submode_extra = (char_u *)_("The only match"); edit_submode_highl = HLF_COUNT; + compl_curr_match->cp_number = 1; } else { // Update completion sequence number when needed. if (compl_curr_match->cp_number == -1) { - int number = 0; - compl_T *match; - - if (compl_direction == FORWARD) { - /* search backwards for the first valid (!= -1) number. - * This should normally succeed already at the first loop - * cycle, so it's fast! */ - for (match = compl_curr_match->cp_prev; match != NULL - && match != compl_first_match; - match = match->cp_prev) - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - if (match != NULL) - /* go up and assign all numbers which are not assigned - * yet */ - for (match = match->cp_next; - match != NULL && match->cp_number == -1; - match = match->cp_next) - match->cp_number = ++number; - } else { // BACKWARD - // search forwards (upwards) for the first valid (!= -1) - // number. This should normally succeed already at the - // first loop cycle, so it's fast! - for (match = compl_curr_match->cp_next; - match != NULL && match != compl_first_match; - match = match->cp_next) { - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - } - if (match != NULL) { - // go down and assign all numbers which are not - // assigned yet - for (match = match->cp_prev; - match && match->cp_number == -1; - match = match->cp_prev) { - match->cp_number = ++number; - } - } - } + ins_compl_update_sequence_numbers(); } /* The match should always have a sequence number now, this is @@ -5307,8 +5327,9 @@ static int ins_complete(int c, bool enable_pum) compl_curr_match->cp_number); edit_submode_extra = match_ref; edit_submode_highl = HLF_R; - if (dollar_vcol >= 0) - curs_columns(FALSE); + if (dollar_vcol >= 0) { + curs_columns(curwin, false); + } } } } @@ -5318,9 +5339,11 @@ static int ins_complete(int c, bool enable_pum) if (!shortmess(SHM_COMPLETIONMENU)) { if (edit_submode_extra != NULL) { if (!p_smd) { + msg_hist_off = true; msg_attr((const char *)edit_submode_extra, (edit_submode_highl < HLF_COUNT ? HL_ATTR(edit_submode_highl) : 0)); + msg_hist_off = false; } } else { msg_clr_cmdline(); // necessary for "noshowmode" @@ -6136,7 +6159,7 @@ internal_format ( curwin->w_p_lbr = has_lbr; if (!format_only && haveto_redraw) { - update_topline(); + update_topline(curwin); redraw_curbuf_later(VALID); } } @@ -6481,7 +6504,7 @@ stop_insert ( curwin->w_cursor = tpos; } - auto_format(TRUE, FALSE); + auto_format(true, false); if (ascii_iswhite(cc)) { if (gchar_cursor() != NUL) @@ -6785,7 +6808,7 @@ cursor_up ( coladvance(curwin->w_curswant); if (upd_topline) { - update_topline(); // make sure curwin->w_topline is valid + update_topline(curwin); // make sure curwin->w_topline is valid } return OK; @@ -6836,7 +6859,7 @@ cursor_down ( coladvance(curwin->w_curswant); if (upd_topline) { - update_topline(); // make sure curwin->w_topline is valid + update_topline(curwin); // make sure curwin->w_topline is valid } return OK; @@ -8259,8 +8282,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) } } while (revins_on || (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); + && (can_bs(BS_NOSTOP) + || (curwin->w_cursor.lnum != Insstart_orig.lnum + || curwin->w_cursor.col != Insstart_orig.col)))); } did_backspace = true; } @@ -8306,9 +8330,6 @@ static void ins_mouse(int c) pos_T tpos; win_T *old_curwin = curwin; - if (!mouse_has(MOUSE_INSERT)) - return; - undisplay_dollar(); tpos = curwin->w_cursor; if (do_mouse(NULL, c, BACKWARD, 1, 0)) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fa037b59d7..8a1556320c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7,6 +7,8 @@ #include <math.h> +#include "auto/config.h" + #ifdef HAVE_LOCALE_H # include <locale.h> #endif @@ -74,9 +76,6 @@ static char_u * const namespace_char = (char_u *)"abglstvw"; /// Variable used for g: static ScopeDictDictItem globvars_var; -/// g: value -#define globvarht globvardict.dv_hashtab - /* * Old Vim variables such as "v:version" are also available without the "v:". * Also in functions. We need a special hashtable for them. @@ -2554,6 +2553,7 @@ void free_for_info(void *fi_void) void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) + FUNC_ATTR_NONNULL_ALL { int got_eq = FALSE; int c; @@ -2636,6 +2636,23 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) } } } + + // ":exe one two" completes "two" + if ((cmdidx == CMD_execute + || cmdidx == CMD_echo + || cmdidx == CMD_echon + || cmdidx == CMD_echomsg) + && xp->xp_context == EXPAND_EXPRESSION) { + for (;;) { + char_u *const n = skiptowhite(arg); + + if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) { + break; + } + arg = skipwhite(n); + } + } + xp->xp_pattern = arg; } @@ -5402,7 +5419,7 @@ static int get_literal_key(char_u **arg, typval_T *tv) for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) { } tv->v_type = VAR_STRING; - tv->vval.v_string = vim_strnsave(*arg, (int)(p - *arg)); + tv->vval.v_string = vim_strnsave(*arg, p - *arg); *arg = skipwhite(p); return OK; @@ -5931,6 +5948,19 @@ int assert_exception(typval_T *argvars) return 0; } +static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, + const char *cmd) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { + char *const tofree = encode_tv2echo(&argvars[2], NULL); + ga_concat(gap, (char_u *)tofree); + xfree(tofree); + } else { + ga_concat(gap, (char_u *)cmd); + } +} + int assert_fails(typval_T *argvars) FUNC_ATTR_NONNULL_ALL { @@ -5949,14 +5979,7 @@ int assert_fails(typval_T *argvars) if (!called_emsg) { prepare_assert_error(&ga); ga_concat(&ga, (const char_u *)"command did not fail: "); - if (argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) { - char *const tofree = encode_tv2echo(&argvars[2], NULL); - ga_concat(&ga, (char_u *)tofree); - xfree(tofree); - } else { - ga_concat(&ga, (const char_u *)cmd); - } + assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); ret = 1; @@ -5969,6 +5992,8 @@ int assert_fails(typval_T *argvars) prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); + ga_concat(&ga, (char_u *)": "); + assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); ga_clear(&ga); ret = 1; @@ -7068,7 +7093,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, } } check_cursor_col(); - update_topline(); + update_topline(curwin); } if (!is_curbuf) { @@ -7780,13 +7805,13 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, if (name[0] == 'w' && dollar_lnum) { pos.col = 0; if (name[1] == '0') { // "w0": first visible line - update_topline(); + update_topline(curwin); // In silent Ex mode topline is zero, but that's not a valid line // number; use one instead. pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1; return &pos; } else if (name[1] == '$') { // "w$": last visible line - validate_botline(); + validate_botline(curwin); // In silent Ex mode botline is zero, return zero then. pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0; return &pos; @@ -10244,9 +10269,6 @@ repeat: if (src[*usedlen] == ':' && (src[*usedlen + 1] == 's' || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { - char_u *str; - char_u *pat; - char_u *sub; int sep; char_u *flags; int didit = FALSE; @@ -10263,13 +10285,13 @@ repeat: // find end of pattern p = vim_strchr(s, sep); if (p != NULL) { - pat = vim_strnsave(s, (int)(p - s)); + char_u *const pat = vim_strnsave(s, p - s); s = p + 1; // find end of substitution p = vim_strchr(s, sep); if (p != NULL) { - sub = vim_strnsave(s, (int)(p - s)); - str = vim_strnsave(*fnamep, *fnamelen); + char_u *const sub = vim_strnsave(s, p - s); + char_u *const str = vim_strnsave(*fnamep, *fnamelen); *usedlen = (size_t)(p + 1 - src); s = do_string_sub(str, pat, sub, NULL, flags); *fnamep = s; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 9f1994e299..952fa35b83 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -150,6 +150,7 @@ return { getjumplist={args={0, 2}}, getline={args={1, 2}}, getloclist={args={1, 2}}, + getmarklist={args={0, 1}}, getmatches={args={0, 1}}, getpid={}, getpos={args=1}, @@ -392,6 +393,7 @@ return { win_screenpos={args=1}, winbufnr={args=1}, wincol={}, + windowsversion={}, winheight={args=1}, winlayout={args={0, 1}}, winline={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4df935469a..8235d74cbb 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -95,7 +95,6 @@ PRAGMA_DIAG_POP static char *e_listarg = N_("E686: Argument of %s must be a List"); -static char *e_stringreq = N_("E928: String required"); static char *e_invalwindow = N_("E957: Invalid window number"); /// Dummy va_list for passing to vim_snprintf @@ -727,7 +726,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only) p_cpo = (char_u *)""; buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name), - TRUE, FALSE, curtab_only)); + true, false, curtab_only)); p_magic = save_magic; p_cpo = save_cpo; @@ -1877,10 +1876,12 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *name = tv_get_string(&argvars[0]); + if (tv_check_for_string(&argvars[0]) == FAIL) { + return; + } // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true); + rettv->vval.v_number = os_can_exe(tv_get_string(&argvars[0]), NULL, true); } typedef struct { @@ -1984,10 +1985,13 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *arg = tv_get_string(&argvars[0]); + if (tv_check_for_string(&argvars[0]) == FAIL) { + return; + } + char *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe(tv_get_string(&argvars[0]), &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)path; @@ -2161,7 +2165,7 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) .nextcmd = NULL, .cmdidx = CMD_USER, }; - eap.argt |= NOSPC; + eap.argt |= EX_NOSPC; expand_filename(&eap, &cmdstr, &errormsg); if (errormsg != NULL && *errormsg != NUL) { @@ -2459,7 +2463,7 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *fbuf = NULL; - size_t len; + size_t len = 0; char buf[NUMBUFLEN]; const char *fname = tv_get_string_chk(&argvars[0]); const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); @@ -2468,8 +2472,10 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { len = strlen(fname); size_t usedlen = 0; - (void)modify_fname((char_u *)mods, false, &usedlen, - (char_u **)&fname, &fbuf, &len); + if (mods != NULL && *mods != NUL) { + (void)modify_fname((char_u *)mods, false, &usedlen, + (char_u **)&fname, &fbuf, &len); + } } rettv->v_type = VAR_STRING; @@ -3477,6 +3483,25 @@ static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_qf_loc_list(false, wp, &argvars[1], rettv); } + +/// "getmarklist()" function +static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (argvars[0].v_type == VAR_UNKNOWN) { + get_global_marks(rettv->vval.v_list); + return; + } + + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + return; + } + + get_buf_local_marks(buf, rettv->vval.v_list); +} + /* * "getmatches()" function */ @@ -4200,6 +4225,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); + } else if (STRICMP(name, "clipboard_working") == 0) { + n = eval_has_provider("clipboard"); #ifdef UNIX } else if (STRICMP(name, "unnamedplus") == 0) { n = eval_has_provider("clipboard"); @@ -6545,7 +6572,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prevlen == 0) { assert(len < INT_MAX); - s = vim_strnsave(start, (int)len); + s = vim_strnsave(start, len); } else { /* Change "prev" buffer to be the right size. This way * the bytes are only copied once, and very long lines are @@ -10847,7 +10874,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = vim_strnsave(head, (int)(tail - head)); + rettv->vval.v_string = vim_strnsave(head, tail - head); } /* @@ -11213,6 +11240,13 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "windowsversion()" function +static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)xstrdup(windowsVersion); +} + /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b62820fecc..02d32a4f86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2953,6 +2953,19 @@ float_T tv_get_float(const typval_T *const tv) return 0; } +// Give an error and return FAIL unless "tv" is a non-empty string. +int tv_check_for_string(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (tv->v_type != VAR_STRING + || tv->vval.v_string == NULL + || *tv->vval.v_string == NUL) { + EMSG(_(e_stringreq)); + return FAIL; + } + return OK; +} + /// Get the string value of a "stringish" VimL object. /// /// @param[in] tv Object to get value of. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 1e3e9bd366..d8ede1e3ba 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -492,7 +492,7 @@ static inline void tv_list_ref(list_T *const l) static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); -/// Set a list as the return value +/// Set a list as the return value. Increments the reference count. /// /// @param[out] tv Object to receive the list /// @param[in,out] l List to pass to the object diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 8daef00985..70c998ef39 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2297,9 +2297,9 @@ void ex_function(exarg_T *eap) // Ignore leading white space. p = skipwhite(p + 4); heredoc_trimmed = - vim_strnsave(theline, (int)(skipwhite(theline) - theline)); + vim_strnsave(theline, skipwhite(theline) - theline); } - skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p)); + skip_until = vim_strnsave(p, skiptowhite(p) - p); do_concat = false; is_heredoc = true; } @@ -2484,6 +2484,7 @@ errret_2: ga_clear_strings(&newlines); ret_free: xfree(skip_until); + xfree(heredoc_trimmed); xfree(line_to_free); xfree(fudi.fd_newkey); xfree(name); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b0a51eaefd..61e4d634c6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -629,6 +629,8 @@ void ex_sort(exarg_T *eap) if (sort_abort) goto sortend; + bcount_t old_count = 0, new_count = 0; + // Insert the lines in the sorted order below the last one. lnum = eap->line2; for (i = 0; i < count; i++) { @@ -641,6 +643,8 @@ void ex_sort(exarg_T *eap) } s = ml_get(get_lnum); + size_t bytelen = STRLEN(s) + 1; // include EOL in bytelen + old_count += bytelen; if (!unique || i == 0 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { // Copy the line into a buffer, it may become invalid in @@ -649,6 +653,7 @@ void ex_sort(exarg_T *eap) if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) { break; } + new_count += bytelen; } fast_breakcheck(); if (got_int) @@ -668,11 +673,16 @@ void ex_sort(exarg_T *eap) deleted = (long)(count - (lnum - eap->line2)); if (deleted > 0) { mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, - kExtmarkUndo); + kExtmarkNOOP); msgmore(-deleted); } else if (deleted < 0) { - mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkUndo); + mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkNOOP); } + + extmark_splice(curbuf, eap->line1-1, 0, + count, 0, old_count, + lnum - eap->line2, 0, new_count, kExtmarkUndo); + if (change_occurred || deleted != 0) { changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); } @@ -1032,14 +1042,15 @@ void free_prev_shellcmd(void) * Bangs in the argument are replaced with the previously entered command. * Remember the argument. */ -void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) +void do_bang(int addr_count, exarg_T *eap, bool forceit, + bool do_in, bool do_out) + FUNC_ATTR_NONNULL_ALL { - char_u *arg = eap->arg; /* command */ - linenr_T line1 = eap->line1; /* start of range */ - linenr_T line2 = eap->line2; /* end of range */ - char_u *newcmd = NULL; /* the new command */ - int free_newcmd = FALSE; /* need to free() newcmd */ - int ins_prevcmd; + char_u *arg = eap->arg; // command + linenr_T line1 = eap->line1; // start of range + linenr_T line2 = eap->line2; // end of range + char_u *newcmd = NULL; // the new command + bool free_newcmd = false; // need to free() newcmd char_u *t; char_u *p; char_u *trailarg; @@ -1064,7 +1075,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) * Try to find an embedded bang, like in :!<cmd> ! [args] * (:!! is indicated by the 'forceit' variable) */ - ins_prevcmd = forceit; + bool ins_prevcmd = forceit; trailarg = arg; do { len = (int)STRLEN(trailarg) + 1; @@ -1101,7 +1112,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) else { trailarg = p; *trailarg++ = NUL; - ins_prevcmd = TRUE; + ins_prevcmd = true; break; } } @@ -1131,7 +1142,7 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) STRCPY(newcmd, p_shq); STRCAT(newcmd, prevcmd); STRCAT(newcmd, p_shq); - free_newcmd = TRUE; + free_newcmd = true; } if (addr_count == 0) { /* :! */ /* echo the command */ @@ -1164,15 +1175,15 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) // do this. // Alternatively, if on Unix and redirecting input or output, but not both, // and the 'shelltemp' option isn't set, use pipes. -// We use input redirection if do_in is TRUE. -// We use output redirection if do_out is TRUE. +// We use input redirection if do_in is true. +// We use output redirection if do_out is true. static void do_filter( linenr_T line1, linenr_T line2, exarg_T *eap, /* for forced 'ff' and 'fenc' */ char_u *cmd, - int do_in, - int do_out) + bool do_in, + bool do_out) { char_u *itmp = NULL; char_u *otmp = NULL; @@ -1669,10 +1680,17 @@ void ex_update(exarg_T *eap) */ void ex_write(exarg_T *eap) { - if (eap->usefilter) /* input lines to shell command */ - do_bang(1, eap, FALSE, TRUE, FALSE); - else + if (eap->cmdidx == CMD_saveas) { + // :saveas does not take a range, uses all lines. + eap->line1 = 1; + eap->line2 = curbuf->b_ml.ml_line_count; + } + + if (eap->usefilter) { // input lines to shell command + do_bang(1, eap, false, true, false); + } else { (void)do_write(eap); + } } /* @@ -2066,19 +2084,20 @@ static int check_readonly(int *forceit, buf_T *buf) return FALSE; } -/* - * Try to abandon current file and edit a new or existing file. - * "fnum" is the number of the file, if zero use ffname/sfname. - * "lnum" is the line number for the cursor in the new file (if non-zero). - * - * Return: - * GETFILE_ERROR for "normal" error, - * GETFILE_NOT_WRITTEN for "not written" error, - * GETFILE_SAME_FILE for success - * GETFILE_OPEN_OTHER for successfully opening another file. - */ -int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, int forceit) +// Try to abandon the current file and edit a new or existing file. +// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg". +// "lnum" is the line number for the cursor in the new file (if non-zero). +// +// Return: +// GETFILE_ERROR for "normal" error, +// GETFILE_NOT_WRITTEN for "not written" error, +// GETFILE_SAME_FILE for success +// GETFILE_OPEN_OTHER for successfully opening another file. +int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, + linenr_T lnum, int forceit) { + char_u *ffname = ffname_arg; + char_u *sfname = sfname_arg; int other; int retval; char_u *free_me = NULL; @@ -2322,7 +2341,7 @@ int do_ecmd( // Existing memfile. oldbuf = true; set_bufref(&bufref, buf); - (void)buf_check_timestamp(buf, false); + (void)buf_check_timestamp(buf); // Check if autocommands made buffer invalid or changed the current // buffer. if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) { @@ -2705,7 +2724,7 @@ int do_ecmd( if (topline == 0 && command == NULL) { *so_ptr = 999; // force cursor to be vertically centered in the window } - update_topline(); + update_topline(curwin); curwin->w_scbind_pos = curwin->w_topline; *so_ptr = n; redraw_curbuf_later(NOT_VALID); // redraw this buffer later @@ -2795,9 +2814,10 @@ void ex_append(exarg_T *eap) p = vim_strchr(eap->nextcmd, NL); if (p == NULL) p = eap->nextcmd + STRLEN(eap->nextcmd); - theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd)); - if (*p != NUL) - ++p; + theline = vim_strnsave(eap->nextcmd, p - eap->nextcmd); + if (*p != NUL) { + p++; + } eap->nextcmd = p; } else { // Set State to avoid the cursor shape to be set to INSERT mode @@ -3704,7 +3724,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, + len_change; highlight_match = TRUE; - update_topline(); + update_topline(curwin); validate_cursor(); update_screen(SOME_VALID); highlight_match = false; @@ -3909,17 +3929,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, ADJUST_SUB_FIRSTLNUM(); - // TODO(bfredl): adjust also in preview, because decorations? - // this has some robustness issues, will look into later. - bool do_splice = !preview; + // TODO(bfredl): this has some robustness issues, look into later. bcount_t replaced_bytes = 0; lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0]; - if (do_splice) { - for (i = 0; i < nmatch-1; i++) { - replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1; - } - replaced_bytes += end.col - start.col; + for (i = 0; i < nmatch-1; i++) { + replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1; } + replaced_bytes += end.col - start.col; // Now the trick is to replace CTRL-M chars with a real line @@ -3964,14 +3980,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, current_match.end.col = new_endcol; current_match.end.lnum = lnum; - if (do_splice) { - int matchcols = end.col - ((end.lnum == start.lnum) - ? start.col : 0); - int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); - extmark_splice(curbuf, lnum_start-1, start_col, - end.lnum-start.lnum, matchcols, replaced_bytes, - lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); - } + int matchcols = end.col - ((end.lnum == start.lnum) + ? start.col : 0); + int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); + extmark_splice(curbuf, lnum_start-1, start_col, + end.lnum-start.lnum, matchcols, replaced_bytes, + lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); } @@ -5254,8 +5268,10 @@ void ex_viusage(exarg_T *eap) /// @param tagname Name of the tags file ("tags" for English, "tags-fr" for /// French) /// @param add_help_tags Whether to add the "help-tags" tag -static void helptags_one(char_u *const dir, const char_u *const ext, - const char_u *const tagfname, const bool add_help_tags) +/// @param ignore_writeerr ignore write error +static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, + bool add_help_tags, bool ignore_writeerr) + FUNC_ATTR_NONNULL_ALL { garray_T ga; int filecount; @@ -5299,7 +5315,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext, FILE *const fd_tags = os_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { - EMSG2(_("E152: Cannot open %s for writing"), NameBuff); + if (!ignore_writeerr) { + EMSG2(_("E152: Cannot open %s for writing"), NameBuff); + } FreeWild(filecount, files); return; } @@ -5447,7 +5465,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext, } /// Generate tags in one help directory, taking care of translations. -static void do_helptags(char_u *dirname, bool add_help_tags) +static void do_helptags(char_u *dirname, bool add_help_tags, + bool ignore_writeerr) + FUNC_ATTR_NONNULL_ALL { int len; garray_T ga; @@ -5529,17 +5549,17 @@ static void do_helptags(char_u *dirname, bool add_help_tags) ext[1] = fname[5]; ext[2] = fname[6]; } - helptags_one(dirname, ext, fname, add_help_tags); + helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr); } ga_clear(&ga); FreeWild(filecount, files); } - static void -helptags_cb(char_u *fname, void *cookie) +static void helptags_cb(char_u *fname, void *cookie) + FUNC_ATTR_NONNULL_ALL { - do_helptags(fname, *(bool *)cookie); + do_helptags(fname, *(bool *)cookie, true); } /* @@ -5568,7 +5588,7 @@ void ex_helptags(exarg_T *eap) if (dirname == NULL || !os_isdir(dirname)) { EMSG2(_("E150: Not a directory: %s"), eap->arg); } else { - do_helptags(dirname, add_help_tags); + do_helptags(dirname, add_help_tags, false); } xfree(dirname); } @@ -5739,7 +5759,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, redraw_later(curwin, SOME_VALID); win_enter(save_curwin, false); // Return to original window - update_topline(); + update_topline(curwin); // Update screen now. int save_rd = RedrawingDisabled; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index d62b00fee1..069d5d461b 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1,6 +1,9 @@ local bit = require 'bit' +local module = {} + -- Description of the values below is contained in ex_cmds_defs.h file. +-- "EX_" prefix is omitted. local RANGE = 0x001 local BANG = 0x002 local EXTRA = 0x004 @@ -14,3289 +17,3308 @@ local REGSTR = 0x200 local COUNT = 0x400 local NOTRLCOM = 0x800 local ZEROR = 0x1000 -local USECTRLV = 0x2000 -local NOTADR = 0x4000 -local EDITCMD = 0x8000 -local BUFNAME = 0x10000 -local BUFUNL = 0x20000 -local ARGOPT = 0x40000 -local SBOXOK = 0x80000 -local CMDWIN = 0x100000 -local MODIFY = 0x200000 -local EXFLAGS = 0x400000 -local RESTRICT = 0x800000 +local CTRLV = 0x2000 +local CMDARG = 0x4000 +local BUFNAME = 0x8000 +local BUFUNL = 0x10000 +local ARGOPT = 0x20000 +local SBOXOK = 0x40000 +local CMDWIN = 0x80000 +local MODIFY = 0x100000 +local FLAGS = 0x200000 local FILES = bit.bor(XFILE, EXTRA) local WORD1 = bit.bor(EXTRA, NOSPC) local FILE1 = bit.bor(FILES, NOSPC) -local ADDR_LINES = 0 -- buffer line numbers -local ADDR_WINDOWS = 1 -- window number -local ADDR_ARGUMENTS = 2 -- argument number -local ADDR_LOADED_BUFFERS = 3 -- buffer number of loaded buffer -local ADDR_BUFFERS = 4 -- buffer number -local ADDR_TABS = 5 -- tab page number -local ADDR_TABS_RELATIVE = 6 -- Tab page that only relative -local ADDR_QUICKFIX = 7 -- quickfix list entry number -local ADDR_OTHER = 99 -- something else +module.flags = { + RANGE = RANGE, + DFLALL = DFLALL, +} -- The following table is described in ex_cmds_defs.h file. -return { +module.cmds = { { command='append', flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_append', }, { command='abbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='abclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='aboveleft', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='all', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_all', }, { command='amenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='anoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='args', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='argadd', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, FILES, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, ZEROR, FILES, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argadd', }, { command='argdelete', - flags=bit.bor(BANG, RANGE, NOTADR, FILES, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, FILES, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argdelete', }, { command='argdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_ARGUMENTS', func='ex_listdo', }, { command='argedit', - flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argedit', }, { command='argglobal', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='arglocal', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_args', }, { command='argument', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argument', }, { command='ascii', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_ascii', }, { command='autocmd', - flags=bit.bor(BANG, EXTRA, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_autocmd', }, { command='augroup', flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_autocmd', }, { command='aunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='buffer', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, EDITCMD, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_buffer', }, { command='bNext', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='ball', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='badd', - flags=bit.bor(NEEDARG, FILE1, EDITCMD, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_edit', }, { command='bdelete', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_bunload', }, { command='behave', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_behave', }, { command='belowright', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='bfirst', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_brewind', }, { command='blast', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_blast', }, { command='bmodified', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bmodified', }, { command='bnext', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bnext', }, { command='botright', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='bprevious', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='brewind', - flags=bit.bor(BANG, RANGE, NOTADR, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_brewind', }, { command='break', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_break', }, { command='breakadd', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakadd', }, { command='breakdel', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakdel', }, { command='breaklist', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breaklist', }, { command='browse', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='buffers', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='bufdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_BUFFERS', func='ex_listdo', }, { command='bunload', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_LOADED_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_LOADED_BUFFERS', func='ex_bunload', }, { command='bwipeout', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_bunload', }, { command='change', flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_change', }, { command='cNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cNfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cabclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='cabove', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='caddbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='caddexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='caddfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { + command='cafter', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='call', flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_call', }, { command='catch', flags=bit.bor(EXTRA, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_catch', }, { command='cbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { + command='cbefore', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='cbelow', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='cbottom', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cbottom', }, { command='cc', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_QUICKFIX', func='ex_cc', }, { command='cclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(TRLBAR), + addr_type='ADDR_NONE', func='ex_cclose', }, { command='cd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='cdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='center', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='cexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='cfile', flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after -- 'cfile' in this cmd list to support the existing ":cf" abbreviation. { command='cfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='cfirst', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='cgetfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { command='cgetbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='cgetexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='chdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='changes', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_changes', }, { command='checkhealth', flags=bit.bor(EXTRA, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_checkhealth', }, { command='checkpath', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_checkpath', }, { command='checktime', - flags=bit.bor(RANGE, NOTADR, BUFNAME, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_checktime', }, { command='chistory', - flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_history', }, { command='clist', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='qf_list', }, { command='clast', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='close', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_WINDOWS', func='ex_close', }, { command='clearjumps', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_clearjumps', }, { command='cmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='cmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='cmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='cnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cnewer', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='cnfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='cnoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='copy', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='colder', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='colorscheme', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_colorscheme', }, { command='command', - flags=bit.bor(EXTRA, BANG, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_command', }, { command='comclear', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_comclear', }, { command='compiler', flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_compiler', }, { command='continue', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_continue', }, { command='confirm', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='const', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_const', }, { command='copen', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_copen', }, { command='cprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='cpfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_OTHER', func='ex_cnext', }, { command='cquit', - flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cquit', }, { command='crewind', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='cscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cscope', }, { command='cstag', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cstag', }, { command='cunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='cunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='cunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='cwindow', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cwindow', }, { command='delete', flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_operators', }, { command='delmarks', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delmarks', }, { command='debug', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_debug', }, { command='debuggreedy', - flags=bit.bor(RANGE, NOTADR, ZEROR, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_debuggreedy', }, { command='delcommand', flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delcommand', }, { command='delfunction', flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_delfunction', }, { command='display', flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_display', }, { command='diffupdate', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffupdate', }, { command='diffget', flags=bit.bor(RANGE, EXTRA, TRLBAR, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_diffgetput', }, { command='diffoff', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffoff', }, { command='diffpatch', flags=bit.bor(EXTRA, FILE1, TRLBAR, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffpatch', }, { command='diffput', flags=bit.bor(RANGE, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_diffgetput', }, { command='diffsplit', flags=bit.bor(EXTRA, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffsplit', }, { command='diffthis', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_diffthis', }, { command='digraphs', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_digraphs', }, { command='djump', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='dlist', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='doautocmd', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_doautocmd', }, { command='doautoall', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_doautoall', }, { command='drop', - flags=bit.bor(FILES, EDITCMD, NEEDARG, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_drop', }, { command='dsearch', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='dsplit', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='edit', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='earlier', flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_later', }, { command='echo', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echo', }, { command='echoerr', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='echohl', flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echohl', }, { command='echomsg', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='echon', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_echo', }, { command='else', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_else', }, { command='elseif', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_else', }, { command='emenu', - flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, NOTADR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN), + addr_type='ADDR_OTHER', func='ex_emenu', }, { command='endif', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endif', }, { command='endfunction', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endfunction', }, { command='endfor', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endwhile', }, { command='endtry', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endtry', }, { command='endwhile', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_endwhile', }, { command='enew', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_edit', }, { command='ex', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='execute', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_execute', }, { command='exit', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='exusage', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_exusage', }, { command='file', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, FILE1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_file', }, { command='files', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='filetype', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_filetype', }, { command='filter', flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='find', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type='ADDR_OTHER', func='ex_find', }, { command='finally', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_finally', }, { command='finish', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_finish', }, { command='first', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='fold', flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_fold', }, { command='foldclose', flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_foldopen', }, { command='folddoopen', flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_folddo', }, { command='folddoclosed', flags=bit.bor(RANGE, DFLALL, NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_folddo', }, { command='foldopen', flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_foldopen', }, { command='for', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_while', }, { command='function', flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_function', }, { command='global', flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_global', }, { command='goto', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN), + addr_type='ADDR_OTHER', func='ex_goto', }, { command='grep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='grepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='gui', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_nogui', }, { command='gvim', - flags=bit.bor(BANG, FILES, EDITCMD, ARGOPT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', func='ex_nogui', }, { command='help', flags=bit.bor(BANG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_help', }, { command='helpclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_helpclose', }, { command='helpgrep', flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helpgrep', }, { command='helptags', flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helptags', }, { command='hardcopy', flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, DFLALL, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_hardcopy', }, { command='highlight', flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_highlight', }, { command='hide', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, TRLBAR), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR), + addr_type='ADDR_WINDOWS', func='ex_hide', }, { command='history', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_history', }, { command='insert', flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_append', }, { command='iabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='iabclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_abclear', }, { command='if', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_if', }, { command='ijump', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='ilist', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='imap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='imapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='imenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='inoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='inoreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='inoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='intro', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_intro', }, { command='isearch', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='isplit', flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_findpat', }, { command='iunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='iunabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='iunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='join', - flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_join', }, { command='jumps', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_jumps', }, { command='k', flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_mark', }, { command='keepmarks', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keepjumps', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keeppatterns', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='keepalt', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='list', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='lNext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lNfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='last', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_last', }, { command='labove', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='language', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_language', }, { command='laddexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='laddbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='laddfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { + command='lafter', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='later', flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_later', }, { command='lbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { + command='lbefore', + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', + func='ex_cbelow', + }, + { command='lbelow', - flags=bit.bor(RANGE, TRLBAR), - addr_type=ADDR_OTHER , + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='ex_cbelow', }, { command='lbottom', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cbottom', }, { command='lcd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='lchdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='lclose', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cclose', }, { command='lcscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cscope', }, { command='ldo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='left', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='leftabove', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='let', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_let', }, { command='lexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='lfile', flags=bit.bor(TRLBAR, FILE1, BANG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after -- 'lfile' in this cmd list to support the existing ":lf" abbreviation. { command='lfdo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_QUICKFIX, + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_QUICKFIX_VALID', func='ex_listdo', }, { command='lfirst', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='lgetfile', flags=bit.bor(TRLBAR, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cfile', }, { command='lgetbuffer', - flags=bit.bor(RANGE, NOTADR, WORD1, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WORD1, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cbuffer', }, { command='lgetexpr', flags=bit.bor(NEEDARG, WORD1, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cexpr', }, { command='lgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='lgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_make', }, { command='lhelpgrep', flags=bit.bor(EXTRA, NOTRLCOM, NEEDARG), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_helpgrep', }, { command='lhistory', - flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_history', }, { command='ll', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_QUICKFIX', func='ex_cc', }, { command='llast', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='llist', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='qf_list', }, { command='lmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='lmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='lmake', flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_make', }, { command='lnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='lnext', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lnewer', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='lnfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='loadview', flags=bit.bor(FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_loadview', }, { command='loadkeymap', flags=bit.bor(CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_loadkeymap', }, { command='lockmarks', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='lockvar', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_lockvar', }, { command='lolder', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_UNSIGNED', func='qf_age', }, { command='lopen', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_copen', }, { command='lprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cnext', }, { command='lpfile', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_OTHER', func='ex_cnext', }, { command='lrewind', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR, BANG), + addr_type='ADDR_UNSIGNED', func='ex_cc', }, { command='ltag', - flags=bit.bor(NOTADR, TRLBAR, BANG, WORD1), - addr_type=ADDR_LINES, + flags=bit.bor(TRLBAR, BANG, WORD1), + addr_type='ADDR_NONE', func='ex_tag', }, { command='lunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='lua', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_lua', }, { command='luado', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_luado', }, { command='luafile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_luafile', }, { command='lvimgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='lvimgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='lwindow', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_cwindow', }, { command='ls', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='buflist_list', }, { command='move', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='mark', flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_mark', }, { command='make', flags=bit.bor(BANG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_make', }, { command='map', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='mapclear', flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='marks', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_marks', }, { command='match', - flags=bit.bor(RANGE, NOTADR, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, CMDWIN), + addr_type='ADDR_OTHER', func='ex_match', }, { command='menu', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='menutranslate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menutranslate', }, { command='messages', flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN), - addr_type=ADDR_OTHER, + addr_type='ADDR_OTHER', func='ex_messages', }, { command='mkexrc', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mksession', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mkspell', flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkspell', }, { command='mkvimrc', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mkview', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mkrc', }, { command='mode', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mode', }, { command='mzscheme', flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_script_ni', }, { command='mzfile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='next', - flags=bit.bor(RANGE, NOTADR, BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_next', }, { command='new', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='nmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='nmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='nmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='nnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='nnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='noremap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='noautocmd', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='nohlsearch', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_nohlsearch', }, { command='noreabbrev', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='noremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='noswapfile', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='normal', - flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, USECTRLV, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN), + addr_type='ADDR_LINES', func='ex_normal', }, { command='number', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='nunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='nunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='oldfiles', flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_oldfiles', }, { command='omap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='omapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='omenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='only', - flags=bit.bor(BANG, NOTADR, RANGE, COUNT, TRLBAR), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_WINDOWS', func='ex_only', }, { command='onoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='onoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='options', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_options', }, { command='ounmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='ounmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='ownsyntax', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ownsyntax', }, { command='print', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, SBOXOK), + addr_type='ADDR_LINES', func='ex_print', }, { command='packadd', flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_packadd', }, { command='packloadall', flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_packloadall', }, { command='pclose', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_pclose', }, { command='perl', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN), + addr_type='ADDR_LINES', func='ex_perl', }, { command='perldo', - flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_perldo', }, { command='perlfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_perlfile', }, { command='pedit', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_pedit', }, { command='pop', - flags=bit.bor(RANGE, NOTADR, BANG, COUNT, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='popup', flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='ppop', - flags=bit.bor(RANGE, NOTADR, BANG, COUNT, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, COUNT, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='preserve', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_preserve', }, { command='previous', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='profile', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_profile', }, { command='profdel', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_breakdel', }, { command='psearch', flags=bit.bor(BANG, RANGE, WHOLEFOLD, DFLALL, EXTRA), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_psearch', }, { command='ptag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptNext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptfirst', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='ptlast', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='ptnext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptprevious', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptrewind', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_ptag', }, { command='ptselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ptag', }, { command='put', flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_put', }, { command='pwd', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_pwd', }, { command='python', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python', }, { command='pydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pydo', }, { command='pyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyfile', }, { command='py3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python3', }, { command='py3do', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pydo3', }, { command='python3', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_python3', }, { command='py3file', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_py3file', }, { command='pyx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyx', }, { command='pyxdo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyxdo', }, { command='pythonx', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyx', }, { command='pyxfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_pyxfile', }, { command='quit', - flags=bit.bor(BANG, RANGE, COUNT, NOTADR, TRLBAR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_WINDOWS', func='ex_quit', }, { command='quitall', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_quit_all', }, { command='qall', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_quit_all', }, { command='read', flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_read', }, { command='recover', flags=bit.bor(BANG, FILE1, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_recover', }, { command='redo', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redo', }, { command='redir', flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redir', }, { command='redraw', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redraw', }, { command='redrawstatus', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redrawstatus', }, { command='redrawtabline', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_redrawtabline', }, { command='registers', flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_display', }, { command='resize', - flags=bit.bor(RANGE, NOTADR, TRLBAR, WORD1, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN), + addr_type='ADDR_OTHER', func='ex_resize', }, { command='retab', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_retab', }, { command='return', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_return', }, { command='rewind', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='right', flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_align', }, { command='rightbelow', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='rshada', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='runtime', flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_runtime', }, { command='rundo', flags=bit.bor(NEEDARG, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_rundo', }, { command='ruby', - flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_ruby', }, { command='rubydo', - flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_rubydo', }, { command='rubyfile', - flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, RESTRICT), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type='ADDR_LINES', func='ex_rubyfile', }, { command='rviminfo', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='substitute', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, { command='sNext', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='sargument', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_ARGUMENTS, + flags=bit.bor(BANG, RANGE, COUNT, EXTRA, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_ARGUMENTS', func='ex_argument', }, { command='sall', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_all', }, { command='sandbox', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='saveas', - flags=bit.bor(BANG, DFLALL, FILE1, ARGOPT, CMDWIN, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, TRLBAR), + addr_type='ADDR_NONE', func='ex_write', }, { command='sbuffer', - flags=bit.bor(BANG, RANGE, NOTADR, BUFNAME, BUFUNL, COUNT, EXTRA, EDITCMD, TRLBAR), - addr_type=ADDR_BUFFERS, + flags=bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR), + addr_type='ADDR_BUFFERS', func='ex_buffer', }, { command='sbNext', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='sball', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='sbfirst', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_brewind', }, { command='sblast', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_blast', }, { command='sbmodified', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bmodified', }, { command='sbnext', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bnext', }, { command='sbprevious', - flags=bit.bor(RANGE, NOTADR, COUNT, EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, CMDARG, TRLBAR), + addr_type='ADDR_OTHER', func='ex_bprevious', }, { command='sbrewind', - flags=bit.bor(EDITCMD, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(CMDARG, TRLBAR), + addr_type='ADDR_NONE', func='ex_brewind', }, { command='scriptnames', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_OTHER, + flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_scriptnames', }, { command='scriptencoding', flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_scriptencoding', }, { command='scscope', flags=bit.bor(EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_scscope', }, { command='set', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='setfiletype', flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_setfiletype', }, { command='setglobal', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='setlocal', flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_set', }, { command='sfind', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR, NEEDARG), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='sfirst', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='simalt', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='sign', - flags=bit.bor(NEEDARG, RANGE, NOTADR, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN), + addr_type='ADDR_OTHER', func='ex_sign', }, { command='silent', flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='sleep', - flags=bit.bor(RANGE, NOTADR, COUNT, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, EXTRA, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_sleep', }, { command='slast', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_last', }, { command='smagic', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_submagic', }, { command='smap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='smapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='smenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='snext', - flags=bit.bor(RANGE, NOTADR, BANG, FILES, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILES, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_next', }, { command='snomagic', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_submagic', }, { command='snoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='snoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='source', flags=bit.bor(BANG, FILE1, TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_source', }, { command='sort', flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, EXTRA, NOTRLCOM, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_sort', }, { command='split', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='spellgood', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='spelldump', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spelldump', }, { command='spellinfo', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spellinfo', }, { command='spellrepall', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_spellrepall', }, { command='spellundo', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='spellwrong', - flags=bit.bor(BANG, RANGE, NOTADR, NEEDARG, EXTRA, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', func='ex_spell', }, { command='sprevious', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='srewind', - flags=bit.bor(EXTRA, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_rewind', }, { command='stop', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stop', }, { command='stag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_stag', }, { command='startinsert', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='startgreplace', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='startreplace', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_startinsert', }, { command='stopinsert', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stopinsert', }, { command='stjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stag', }, { command='stselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stag', }, { command='sunhide', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='sunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='sunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='suspend', flags=bit.bor(TRLBAR, BANG, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_stop', }, { command='sview', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='swapname', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_swapname', }, { command='syntax', flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syntax', }, { command='syntime', flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syntime', }, { command='syncbind', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_syncbind', }, { command='t', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_copymove', }, { command='tcd', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='tchdir', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_cd', }, { command='tNext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tag', - flags=bit.bor(RANGE, NOTADR, BANG, WORD1, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, WORD1, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tags', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_tags', }, { command='tab', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='tabclose', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), + addr_type='ADDR_TABS', func='ex_tabclose', }, { command='tabdo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_TABS, + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_TABS', func='ex_listdo', }, { command='tabedit', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabfind', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, NEEDARG, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, NEEDARG, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabfirst', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabmove', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS', func='ex_tabmove', }, { command='tablast', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabnext', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS', func='ex_tabnext', }, { command='tabnew', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, ZEROR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, FILE1, RANGE, ZEROR, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_TABS', func='ex_splitview', }, { command='tabonly', - flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN), + addr_type='ADDR_TABS', func='ex_tabonly', }, { command='tabprevious', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS_RELATIVE, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS_RELATIVE', func='ex_tabnext', }, { command='tabNext', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, NOSPC, TRLBAR), - addr_type=ADDR_TABS_RELATIVE, + flags=bit.bor(RANGE, ZEROR, EXTRA, NOSPC, TRLBAR), + addr_type='ADDR_TABS_RELATIVE', func='ex_tabnext', }, { command='tabrewind', flags=bit.bor(TRLBAR), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabnext', }, { command='tabs', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_TABS, + addr_type='ADDR_NONE', func='ex_tabs', }, { command='tcl', flags=bit.bor(RANGE,EXTRA,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_script_ni', }, { command='tcldo', flags=bit.bor(RANGE,DFLALL,EXTRA,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='tclfile', flags=bit.bor(RANGE,FILE1,NEEDARG,CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_ni', }, { command='terminal', flags=bit.bor(BANG, FILES, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_terminal', }, { command='tfirst', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='throw', flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_throw', }, { command='tjump', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tlast', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='tmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='tmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='tnext', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='tnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='topleft', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='tprevious', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='trewind', - flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, TRLBAR, ZEROR), + addr_type='ADDR_OTHER', func='ex_tag', }, { command='try', flags=bit.bor(TRLBAR, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_try', }, { command='tselect', flags=bit.bor(BANG, TRLBAR, WORD1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_tag', }, { command='tunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='tunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='undo', - flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, CMDWIN), + addr_type='ADDR_OTHER', func='ex_undo', }, { command='undojoin', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_undojoin', }, { command='undolist', flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_undolist', }, { command='unabbreviate', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_abbreviate', }, { command='unhide', - flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, COUNT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_buffer_all', }, { command='unlet', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_unlet', }, { command='unlockvar', flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_lockvar', }, { command='unmap', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='unmenu', - flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='unsilent', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='update', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_update', }, { command='vglobal', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_global', }, { command='version', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_version', }, { command='verbose', - flags=bit.bor(NEEDARG, RANGE, NOTADR, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN), + addr_type='ADDR_OTHER', func='ex_wrongmodifier', }, { command='vertical', flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wrongmodifier', }, { command='visual', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='view', - flags=bit.bor(BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='ex_edit', }, { command='vimgrep', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='vimgrepadd', - flags=bit.bor(RANGE, NOTADR, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE), + addr_type='ADDR_OTHER', func='ex_vimgrep', }, { command='viusage', flags=bit.bor(TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_viusage', }, { command='vmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='vmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='vmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='vnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='vnew', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='vnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='vsplit', - flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, RANGE, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_splitview', }, { command='vunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='vunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='write', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_write', }, { command='wNext', - flags=bit.bor(RANGE, WHOLEFOLD, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wall', flags=bit.bor(BANG, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_wqall', }, { command='while', flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_while', }, { command='winsize', flags=bit.bor(EXTRA, NEEDARG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_winsize', }, { command='wincmd', - flags=bit.bor(NEEDARG, WORD1, RANGE, NOTADR, CMDWIN), - addr_type=ADDR_WINDOWS, + flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN), + addr_type='ADDR_OTHER', func='ex_wincmd', }, { command='windo', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), - addr_type=ADDR_WINDOWS, + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, DFLALL), + addr_type='ADDR_WINDOWS', func='ex_listdo', }, { command='winpos', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_ni', }, { command='wnext', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wprevious', - flags=bit.bor(RANGE, NOTADR, BANG, FILE1, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_wnext', }, { command='wq', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='wqall', - flags=bit.bor(BANG, FILE1, ARGOPT, DFLALL, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, FILE1, ARGOPT, TRLBAR), + addr_type='ADDR_NONE', func='do_wqall', }, { command='wshada', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='wundo', flags=bit.bor(BANG, NEEDARG, FILE1), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_wundo', }, { command='wviminfo', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_shada', }, { command='xit', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_exit', }, { command='xall', flags=bit.bor(BANG, TRLBAR), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='do_wqall', }, { command='xmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='xmapclear', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_NONE', func='ex_mapclear', }, { command='xmenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='xnoremap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_map', }, { command='xnoremenu', - flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_OTHER', func='ex_menu', }, { command='xunmap', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_unmap', }, { command='xunmenu', - flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), + addr_type='ADDR_NONE', func='ex_menu', }, { command='yank', flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_operators', }, { command='z', - flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_z', }, { command='!', enum='CMD_bang', flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_bang', }, { command='#', enum='CMD_pound', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN), + addr_type='ADDR_LINES', func='ex_print', }, { command='&', enum='CMD_and', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, { command='<', enum='CMD_lshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_operators', }, { command='=', enum='CMD_equal', - flags=bit.bor(RANGE, TRLBAR, DFLALL, EXFLAGS, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN), + addr_type='ADDR_LINES', func='ex_equal', }, { command='>', enum='CMD_rshift', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY), + addr_type='ADDR_LINES', func='ex_operators', }, { command='@', enum='CMD_at', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_at', }, { command='Next', - flags=bit.bor(EXTRA, RANGE, NOTADR, COUNT, BANG, EDITCMD, ARGOPT, TRLBAR), - addr_type=ADDR_LINES, + flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR), + addr_type='ADDR_OTHER', func='ex_previous', }, { command='~', enum='CMD_tilde', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), - addr_type=ADDR_LINES, + addr_type='ADDR_LINES', func='ex_substitute', }, } + +return module diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3b9c44c3cd..c400975108 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2113,7 +2113,7 @@ void ex_listdo(exarg_T *eap) } } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) { - qf_size = qf_get_size(eap); + qf_size = qf_get_valid_size(eap); assert(eap->line1 >= 0); if (qf_size == 0 || (size_t)eap->line1 > qf_size) { buf = NULL; @@ -3392,7 +3392,7 @@ void ex_checktime(exarg_T *eap) } else { buf = buflist_findnr((int)eap->line2); if (buf != NULL) { // cannot happen? - (void)buf_check_timestamp(buf, false); + (void)buf_check_timestamp(buf); } } no_check_timestamps = save_no_check_timestamps; @@ -3790,6 +3790,14 @@ void ex_drop(exarg_T *eap) if (wp->w_buffer == buf) { goto_tabpage_win(tp, wp); curwin->w_arg_idx = 0; + if (!bufIsChanged(curbuf)) { + const int save_ar = curbuf->b_p_ar; + + // reload the file if it is newer + curbuf->b_p_ar = 1; + buf_check_timestamp(curbuf); + curbuf->b_p_ar = save_ar; + } return; } } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 21db3936b8..ca84d375ce 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -36,46 +36,50 @@ // 4. Add documentation in ../doc/xxx.txt. Add a tag for both the short and // long name of the command. -#define RANGE 0x001 // allow a linespecs -#define BANG 0x002 // allow a ! after the command name -#define EXTRA 0x004 // allow extra args after command name -#define XFILE 0x008 // expand wildcards in extra part -#define NOSPC 0x010 // no spaces allowed in the extra part -#define DFLALL 0x020 // default file range is 1,$ -#define WHOLEFOLD 0x040 // extend range to include whole fold also - // when less than two numbers given -#define NEEDARG 0x080 // argument required -#define TRLBAR 0x100 // check for trailing vertical bar -#define REGSTR 0x200 // allow "x for register designation -#define COUNT 0x400 // allow count in argument, after command -#define NOTRLCOM 0x800 // no trailing comment allowed -#define ZEROR 0x1000 // zero line number allowed -#define USECTRLV 0x2000 // do not remove CTRL-V from argument -#define NOTADR 0x4000 // number before command is not an address -#define EDITCMD 0x8000 // allow "+command" argument -#define BUFNAME 0x10000 // accepts buffer name -#define BUFUNL 0x20000 // accepts unlisted buffer too -#define ARGOPT 0x40000 // allow "++opt=val" argument -#define SBOXOK 0x80000 // allowed in the sandbox -#define CMDWIN 0x100000 // allowed in cmdline window; when missing - // disallows editing another buffer when - // curbuf_lock is set -#define MODIFY 0x200000 // forbidden in non-'modifiable' buffer -#define EXFLAGS 0x400000 // allow flags after count in argument -#define FILES (XFILE | EXTRA) // multiple extra files allowed -#define WORD1 (EXTRA | NOSPC) // one extra word allowed -#define FILE1 (FILES | NOSPC) // 1 file allowed, defaults to current file +#define EX_RANGE 0x001 // allow a linespecs +#define EX_BANG 0x002 // allow a ! after the command name +#define EX_EXTRA 0x004 // allow extra args after command name +#define EX_XFILE 0x008 // expand wildcards in extra part +#define EX_NOSPC 0x010 // no spaces allowed in the extra part +#define EX_DFLALL 0x020 // default file range is 1,$ +#define EX_WHOLEFOLD 0x040 // extend range to include whole fold also + // when less than two numbers given +#define EX_NEEDARG 0x080 // argument required +#define EX_TRLBAR 0x100 // check for trailing vertical bar +#define EX_REGSTR 0x200 // allow "x for register designation +#define EX_COUNT 0x400 // allow count in argument, after command +#define EX_NOTRLCOM 0x800 // no trailing comment allowed +#define EX_ZEROR 0x1000 // zero line number allowed +#define EX_CTRLV 0x2000 // do not remove CTRL-V from argument +#define EX_CMDARG 0x4000 // allow "+command" argument +#define EX_BUFNAME 0x8000 // accepts buffer name +#define EX_BUFUNL 0x10000 // accepts unlisted buffer too +#define EX_ARGOPT 0x20000 // allow "++opt=val" argument +#define EX_SBOXOK 0x40000 // allowed in the sandbox +#define EX_CMDWIN 0x80000 // allowed in cmdline window; when missing + // disallows editing another buffer when + // curbuf_lock is set +#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer +#define EX_FLAGS 0x200000 // allow flags after count in argument +#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed +#define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file +#define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed // values for cmd_addr_type -#define ADDR_LINES 0 -#define ADDR_WINDOWS 1 -#define ADDR_ARGUMENTS 2 -#define ADDR_LOADED_BUFFERS 3 -#define ADDR_BUFFERS 4 -#define ADDR_TABS 5 -#define ADDR_TABS_RELATIVE 6 // Tab page that only relative -#define ADDR_QUICKFIX 7 -#define ADDR_OTHER 99 +typedef enum { + ADDR_LINES, // buffer line numbers + ADDR_WINDOWS, // window number + ADDR_ARGUMENTS, // argument number + ADDR_LOADED_BUFFERS, // buffer number of loaded buffer + ADDR_BUFFERS, // buffer number + ADDR_TABS, // tab page number + ADDR_TABS_RELATIVE, // Tab page that only relative + ADDR_QUICKFIX_VALID, // quickfix list valid entry number + ADDR_QUICKFIX, // quickfix list entry number + ADDR_UNSIGNED, // positive count or zero, defaults to 1 + ADDR_OTHER, // something else, use line number for '$', '%', etc. + ADDR_NONE // no range used +} cmd_addr_T; typedef struct exarg exarg_T; @@ -93,7 +97,7 @@ typedef struct cmdname { char_u *cmd_name; ///< Name of the command. ex_func_T cmd_func; ///< Function with implementation of this command. uint32_t cmd_argt; ///< Relevant flags from the declared above. - int cmd_addr_type; ///< Flag for address type + cmd_addr_T cmd_addr_type; ///< Flag for address type } CommandDefinition; // A list used for saving values of "emsg_silent". Used by ex_try() to save the @@ -150,7 +154,7 @@ struct exarg { int addr_count; ///< the number of addresses given linenr_T line1; ///< the first line number linenr_T line2; ///< the second line number or count - int addr_type; ///< type of the count/range + cmd_addr_T addr_type; ///< type of the count/range int flags; ///< extra flags after count: EXFLAG_ char_u *do_ecmd_cmd; ///< +command arg to be used in edited file linenr_T do_ecmd_lnum; ///< the line number in an edited file diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0ffe67a4db..ccf7dd0f68 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -85,7 +85,7 @@ typedef struct ucmd { char_u *uc_rep; // The command's replacement string long uc_def; // The default value for a range/count int uc_compl; // completion type - int uc_addr_type; // The command's address type + cmd_addr_T uc_addr_type; // The command's address type sctx_T uc_script_ctx; // SCTX where the command was defined char_u *uc_compl_arg; // completion argument if any } ucmd_T; @@ -1086,7 +1086,10 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) return buf->b_fnum; } -static int current_win_nr(win_T *win) +// Return the window number of "win". +// When "win" is NULL return the number of windows. +static int current_win_nr(const win_T *win) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { int nr = 0; @@ -1163,7 +1166,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case 'd': case Ctrl_D: // window size or any count - eap->addr_type = ADDR_LINES; // -V1037 + eap->addr_type = ADDR_OTHER; // -V1037 break; case Ctrl_HAT: @@ -1199,7 +1202,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) case '=': case CAR: // no count - eap->addr_type = 0; + eap->addr_type = ADDR_NONE; break; } } @@ -1380,6 +1383,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_wincmd && p != NULL) { get_wincmd_addr_type(skipwhite(p), &ea); } + // :.cc in quickfix window uses line number + if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf)) { + ea.addr_type = ADDR_OTHER; + } } ea.cmd = cmd; @@ -1411,7 +1418,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) { ea.cmdidx = CMD_print; - ea.argt = RANGE | COUNT | TRLBAR; + ea.argt = EX_RANGE | EX_COUNT | EX_TRLBAR; if ((errormsg = invalid_range(&ea)) == NULL) { correct_range(&ea); ex_print(&ea); @@ -1490,20 +1497,18 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.forceit = false; } - /* - * 6. Parse arguments. - */ + // 6. Parse arguments. Then check for errors. if (!IS_USER_CMDIDX(ea.cmdidx)) { ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } if (!ea.skip) { - if (sandbox != 0 && !(ea.argt & SBOXOK)) { + if (sandbox != 0 && !(ea.argt & EX_SBOXOK)) { // Command not allowed in sandbox. errormsg = (char_u *)_(e_sandbox); goto doend; } - if (!MODIFIABLE(curbuf) && (ea.argt & MODIFY) + if (!MODIFIABLE(curbuf) && (ea.argt & EX_MODIFY) // allow :put in terminals && (!curbuf->terminal || ea.cmdidx != CMD_put)) { /* Command not allowed in non-'modifiable' buffer */ @@ -1511,7 +1516,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if (text_locked() && !(ea.argt & CMDWIN) + if (text_locked() && !(ea.argt & EX_CMDWIN) && !IS_USER_CMDIDX(ea.cmdidx)) { // Command not allowed when editing the command line. errormsg = (char_u *)_(get_text_locked_msg()); @@ -1522,7 +1527,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, // Do allow ":checktime" (it is postponed). // Do allow ":edit" (check for an argument later). // Do allow ":file" with no arguments (check for an argument later). - if (!(ea.argt & CMDWIN) + if (!(ea.argt & EX_CMDWIN) && ea.cmdidx != CMD_checktime && ea.cmdidx != CMD_edit && ea.cmdidx != CMD_file @@ -1531,14 +1536,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if (!ni && !(ea.argt & RANGE) && ea.addr_count > 0) { - /* no range allowed */ + if (!ni && !(ea.argt & EX_RANGE) && ea.addr_count > 0) { + // no range allowed errormsg = (char_u *)_(e_norange); goto doend; } } - if (!ni && !(ea.argt & BANG) && ea.forceit) { /* no <!> allowed */ + if (!ni && !(ea.argt & EX_BANG) && ea.forceit) { // no <!> allowed errormsg = (char_u *)_(e_nobang); goto doend; } @@ -1547,12 +1552,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Don't complain about the range if it is not used * (could happen if line_count is accidentally set to 0). */ - if (!ea.skip && !ni) { - /* - * If the range is backwards, ask for confirmation and, if given, swap - * ea.line1 & ea.line2 so it's forwards again. - * When global command is busy, don't ask, will fail below. - */ + if (!ea.skip && !ni && (ea.argt & EX_RANGE)) { + // If the range is backwards, ask for confirmation and, if given, swap + // ea.line1 & ea.line2 so it's forwards again. + // When global command is busy, don't ask, will fail below. if (!global_busy && ea.line1 > ea.line2) { if (msg_silent == 0) { if ((flags & DOCMD_VERBOSE) || exmode_active) { @@ -1571,12 +1574,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - if ((ea.argt & NOTADR) && ea.addr_count == 0) /* default is 1, not cursor */ + if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0) { + // default is 1, not cursor ea.line2 = 1; + } correct_range(&ea); - if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy + if (((ea.argt & EX_WHOLEFOLD) || ea.addr_count >= 2) && !global_busy && ea.addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. @@ -1610,12 +1615,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Check for "++opt=val" argument. * Must be first, allow ":w ++enc=utf8 !cmd" */ - if (ea.argt & ARGOPT) - while (ea.arg[0] == '+' && ea.arg[1] == '+') + if (ea.argt & EX_ARGOPT) { + while (ea.arg[0] == '+' && ea.arg[1] == '+') { if (getargopt(&ea) == FAIL && !ni) { errormsg = (char_u *)_(e_invarg); goto doend; } + } + } if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { if (*ea.arg == '>') { /* append */ @@ -1654,14 +1661,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, * Check for "+command" argument, before checking for next command. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & EDITCMD) && !ea.usefilter) + if ((ea.argt & EX_CMDARG) && !ea.usefilter) { ea.do_ecmd_cmd = getargcmd(&ea.arg); + } /* * Check for '|' to separate commands and '"' to start comments. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & TRLBAR) && !ea.usefilter) { + if ((ea.argt & EX_TRLBAR) && !ea.usefilter) { separate_nextcmd(&ea); } else if (ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal @@ -1688,12 +1696,13 @@ static char_u * do_one_cmd(char_u **cmdlinep, } } - if ((ea.argt & DFLALL) && ea.addr_count == 0) { + if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) { buf_T *buf; ea.line1 = 1; switch (ea.addr_type) { case ADDR_LINES: + case ADDR_OTHER: ea.line2 = curbuf->b_ml.ml_line_count; break; case ADDR_LOADED_BUFFERS: @@ -1728,21 +1737,27 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = ARGCOUNT; } break; - case ADDR_QUICKFIX: - ea.line2 = qf_get_size(&ea); + case ADDR_QUICKFIX_VALID: + ea.line2 = qf_get_valid_size(&ea); if (ea.line2 == 0) { ea.line2 = 1; } break; + case ADDR_NONE: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: + IEMSG(_("INTERNAL: Cannot use EX_DFLALL " + "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); + break; } } - /* accept numbered register only when no count allowed (:put) */ - if ((ea.argt & REGSTR) + // accept numbered register only when no count allowed (:put) + if ((ea.argt & EX_REGSTR) && *ea.arg != NUL /* Do not allow register = for user commands */ && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') - && !((ea.argt & COUNT) && ascii_isdigit(*ea.arg))) { + && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) { if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx)))) { ea.regname = *ea.arg++; @@ -1756,19 +1771,19 @@ static char_u * do_one_cmd(char_u **cmdlinep, } // - // Check for a count. When accepting a BUFNAME, don't use "123foo" as a + // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a // count, it's a buffer name. /// - if ((ea.argt & COUNT) && ascii_isdigit(*ea.arg) - && (!(ea.argt & BUFNAME) || *(p = skipdigits(ea.arg)) == NUL + if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg) + && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg)) == NUL || ascii_iswhite(*p))) { n = getdigits_long(&ea.arg, false, -1); ea.arg = skipwhite(ea.arg); - if (n <= 0 && !ni && (ea.argt & ZEROR) == 0) { + if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0) { errormsg = (char_u *)_(e_zerocount); goto doend; } - if (ea.argt & NOTADR) { /* e.g. :buffer 2, :sleep 3 */ + if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 ea.line2 = n; if (ea.addr_count == 0) ea.addr_count = 1; @@ -1777,8 +1792,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 += n - 1; ++ea.addr_count; // Be vi compatible: no error message for out of range. - if (ea.addr_type == ADDR_LINES - && ea.line2 > curbuf->b_ml.ml_line_count) { + if (ea.line2 > curbuf->b_ml.ml_line_count) { ea.line2 = curbuf->b_ml.ml_line_count; } } @@ -1787,16 +1801,17 @@ static char_u * do_one_cmd(char_u **cmdlinep, /* * Check for flags: 'l', 'p' and '#'. */ - if (ea.argt & EXFLAGS) + if (ea.argt & EX_FLAGS) { get_flags(&ea); - /* no arguments allowed */ - if (!ni && !(ea.argt & EXTRA) && *ea.arg != NUL - && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & TRLBAR) == 0)) { + } + if (!ni && !(ea.argt & EX_EXTRA) && *ea.arg != NUL + && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & EX_TRLBAR) == 0)) { + // no arguments allowed but there is something errormsg = (char_u *)_(e_trailing); goto doend; } - if (!ni && (ea.argt & NEEDARG) && *ea.arg == NUL) { + if (!ni && (ea.argt & EX_NEEDARG) && *ea.arg == NUL) { errormsg = (char_u *)_(e_argreq); goto doend; } @@ -1825,9 +1840,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_function: break; - /* Commands that handle '|' themselves. Check: A command should - * either have the TRLBAR flag, appear in this list or appear in - * the list at ":help :bar". */ + // Commands that handle '|' themselves. Check: A command should + // either have the EX_TRLBAR flag, appear in this list or appear in + // the list at ":help :bar". case CMD_aboveleft: case CMD_and: case CMD_belowright: @@ -1898,16 +1913,17 @@ static char_u * do_one_cmd(char_u **cmdlinep, } } - if (ea.argt & XFILE) { - if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) + if (ea.argt & EX_XFILE) { + if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { goto doend; + } } /* * Accept buffer name. Cannot be used at the same time with a buffer * number. Don't do this for a user command. */ - if ((ea.argt & BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 + if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 && !IS_USER_CMDIDX(ea.cmdidx) ) { /* @@ -1923,10 +1939,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, while (p > ea.arg && ascii_iswhite(p[-1])) --p; } - ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & BUFUNL) != 0, - FALSE, FALSE); - if (ea.line2 < 0) /* failed */ + ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0, + false, false); + if (ea.line2 < 0) { // failed goto doend; + } ea.addr_count = 1; ea.arg = skipwhite(p); } @@ -2322,6 +2339,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line1 = eap->line2; switch (eap->addr_type) { case ADDR_LINES: + case ADDR_OTHER: // default is current line number eap->line2 = curwin->w_cursor.lnum; break; @@ -2342,11 +2360,18 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line2 = CURRENT_TAB_NR; break; case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: eap->line2 = 1; break; case ADDR_QUICKFIX: + eap->line2 = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: eap->line2 = qf_get_cur_valid_idx(eap); break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->cmd = skipwhite(eap->cmd); lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, @@ -2359,6 +2384,7 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->cmd++; switch (eap->addr_type) { case ADDR_LINES: + case ADDR_OTHER: eap->line1 = 1; eap->line2 = curbuf->b_ml.ml_line_count; break; @@ -2394,7 +2420,8 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) } break; case ADDR_TABS_RELATIVE: - case ADDR_OTHER: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: *errormsg = (char_u *)_(e_invrange); return FAIL; case ADDR_ARGUMENTS: @@ -2405,13 +2432,16 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line2 = ARGCOUNT; } break; - case ADDR_QUICKFIX: + case ADDR_QUICKFIX_VALID: eap->line1 = 1; - eap->line2 = qf_get_size(eap); + eap->line2 = qf_get_valid_size(eap); if (eap->line2 == 0) { eap->line2 = 1; } break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->addr_count++; } else if (*eap->cmd == '*') { @@ -2513,14 +2543,13 @@ static void append_command(char_u *cmd) *d = NUL; } -/* - * Find an Ex command by its name, either built-in or user. - * Start of the name can be found at eap->cmd. - * Returns pointer to char after the command name. - * "full" is set to TRUE if the whole command name matched. - * Returns NULL for an ambiguous user command. - */ +// Find an Ex command by its name, either built-in or user. +// Start of the name can be found at eap->cmd. +// Sets eap->cmdidx and returns a pointer to char after the command name. +// "full" is set to TRUE if the whole command name matched. +// Returns NULL for an ambiguous user command. static char_u *find_command(exarg_T *eap, int *full) + FUNC_ATTR_NONNULL_ARG(1) { int len; char_u *p; @@ -2970,6 +2999,15 @@ const char * set_one_cmd_context( const char *arg = (const char *)skipwhite((const char_u *)p); + // Skip over ++argopt argument + if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) { + p = arg; + while (*p && !ascii_isspace(*p)) { + MB_PTR_ADV(p); + } + arg = (const char *)skipwhite((const char_u *)p); + } + if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { if (*arg == '>') { // Append. if (*++arg == '>') { @@ -2997,9 +3035,9 @@ const char * set_one_cmd_context( arg = (const char *)skipwhite((const char_u *)arg); } - /* Does command allow "+command"? */ - if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { - /* Check if we're in the +command */ + // Does command allow "+command"? + if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') { + // Check if we're in the +command p = arg + 1; arg = (const char *)skip_cmd_arg((char_u *)arg, false); @@ -3015,17 +3053,19 @@ const char * set_one_cmd_context( * Check for '|' to separate commands and '"' to start comments. * Don't do this for ":read !cmd" and ":write !cmd". */ - if ((ea.argt & TRLBAR) && !usefilter) { + if ((ea.argt & EX_TRLBAR) && !usefilter) { p = arg; /* ":redir @" is not the start of a comment */ if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') p += 2; while (*p) { if (*p == Ctrl_V) { - if (p[1] != NUL) - ++p; - } else if ( (*p == '"' && !(ea.argt & NOTRLCOM)) - || *p == '|' || *p == '\n') { + if (p[1] != NUL) { + p++; + } + } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM)) + || *p == '|' + || *p == '\n') { if (*(p - 1) != '\\') { if (*p == '|' || *p == '\n') return p + 1; @@ -3036,8 +3076,8 @@ const char * set_one_cmd_context( } } - // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { + if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { + // no arguments allowed but there is something return NULL; } @@ -3057,7 +3097,7 @@ const char * set_one_cmd_context( } } - if (ea.argt & XFILE) { + if (ea.argt & EX_XFILE) { int c; int in_quote = false; const char *bow = NULL; // Beginning of word. @@ -3478,8 +3518,8 @@ const char * set_one_cmd_context( case CMD_USER: case CMD_USER_BUF: if (context != EXPAND_NOTHING) { - // XFILE: file names are handled above. - if (!(ea.argt & XFILE)) { + // EX_XFILE: file names are handled above. + if (!(ea.argt & EX_XFILE)) { if (context == EXPAND_MENUS) { return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit); @@ -3689,6 +3729,15 @@ char_u *skip_range( return (char_u *)cmd; } +static void addr_error(cmd_addr_T addr_type) +{ + if (addr_type == ADDR_NONE) { + EMSG(_(e_norange)); + } else { + EMSG(_(e_invrange)); + } +} + // Get a single EX address // // Set ptr to the next character after the part that was interpreted. @@ -3698,11 +3747,12 @@ char_u *skip_range( // Return MAXLNUM when no Ex address was found. static linenr_T get_address(exarg_T *eap, char_u **ptr, - int addr_type, // flag: one of ADDR_LINES, ... + cmd_addr_T addr_type, int skip, // only skip the address, don't use it bool silent, // no errors or side effects int to_other_file, // flag: may jump to other file int address_count) // 1 for first, >1 after comma + FUNC_ATTR_NONNULL_ALL { int c; int i; @@ -3721,6 +3771,7 @@ static linenr_T get_address(exarg_T *eap, ++cmd; switch (addr_type) { case ADDR_LINES: + case ADDR_OTHER: lnum = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: @@ -3736,12 +3787,17 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = CURRENT_TAB_NR; break; + case ADDR_NONE: case ADDR_TABS_RELATIVE: - EMSG(_(e_invrange)); + case ADDR_UNSIGNED: + addr_error(addr_type); cmd = NULL; goto error; break; case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); break; } @@ -3751,6 +3807,7 @@ static linenr_T get_address(exarg_T *eap, ++cmd; switch (addr_type) { case ADDR_LINES: + case ADDR_OTHER: lnum = curbuf->b_ml.ml_line_count; break; case ADDR_WINDOWS: @@ -3775,8 +3832,10 @@ static linenr_T get_address(exarg_T *eap, case ADDR_TABS: lnum = LAST_TAB_NR; break; + case ADDR_NONE: case ADDR_TABS_RELATIVE: - EMSG(_(e_invrange)); + case ADDR_UNSIGNED: + addr_error(addr_type); cmd = NULL; goto error; break; @@ -3786,6 +3845,12 @@ static linenr_T get_address(exarg_T *eap, lnum = 1; } break; + case ADDR_QUICKFIX_VALID: + lnum = qf_get_valid_size(eap); + if (lnum == 0) { + lnum = 1; + } + break; } break; @@ -3795,7 +3860,7 @@ static linenr_T get_address(exarg_T *eap, goto error; } if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3823,7 +3888,7 @@ static linenr_T get_address(exarg_T *eap, case '?': /* '/' or '?' - search */ c = *cmd++; if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3870,7 +3935,7 @@ static linenr_T get_address(exarg_T *eap, case '\\': /* "\?", "\/" or "\&", repeat search */ ++cmd; if (addr_type != ADDR_LINES) { - EMSG(_(e_invaddr)); + addr_error(addr_type); cmd = NULL; goto error; } @@ -3917,7 +3982,9 @@ static linenr_T get_address(exarg_T *eap, if (lnum == MAXLNUM) { switch (addr_type) { case ADDR_LINES: - lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ + case ADDR_OTHER: + // "+1" is same as ".+1" + lnum = curwin->w_cursor.lnum; break; case ADDR_WINDOWS: lnum = CURRENT_WIN_NR; @@ -3936,8 +4003,15 @@ static linenr_T get_address(exarg_T *eap, lnum = 1; break; case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); break; + case ADDR_NONE: + case ADDR_UNSIGNED: + lnum = 0; + break; } } @@ -4027,12 +4101,11 @@ static char_u *invalid_range(exarg_T *eap) return (char_u *)_(e_invrange); } - if (eap->argt & RANGE) { - switch(eap->addr_type) { + if (eap->argt & EX_RANGE) { + switch (eap->addr_type) { case ADDR_LINES: - if (!(eap->argt & NOTADR) - && eap->line2 > curbuf->b_ml.ml_line_count - + (eap->cmdidx == CMD_diffget)) { + if (eap->line2 > (curbuf->b_ml.ml_line_count + + (eap->cmdidx == CMD_diffget))) { return (char_u *)_(e_invrange); } break; @@ -4081,14 +4154,30 @@ static char_u *invalid_range(exarg_T *eap) } break; case ADDR_TABS_RELATIVE: - // Do nothing + case ADDR_OTHER: + // Any range is OK. break; case ADDR_QUICKFIX: assert(eap->line2 >= 0); - if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) { + // No error for value that is too big, will use the last entry. + if (eap->line2 <= 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_QUICKFIX_VALID: + if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap)) + || eap->line2 < 0) { return (char_u *)_(e_invrange); } break; + case ADDR_UNSIGNED: + if (eap->line2 < 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_NONE: + // Will give an error elsewhere. + break; } } return NULL; @@ -4099,11 +4188,13 @@ static char_u *invalid_range(exarg_T *eap) */ static void correct_range(exarg_T *eap) { - if (!(eap->argt & ZEROR)) { /* zero in range not allowed */ - if (eap->line1 == 0) + if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed + if (eap->line1 == 0) { eap->line1 = 1; - if (eap->line2 == 0) + } + if (eap->line2 == 0) { eap->line2 = 1; + } } } @@ -4270,7 +4361,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) && eap->cmdidx != CMD_lmake && eap->cmdidx != CMD_make && eap->cmdidx != CMD_terminal - && !(eap->argt & NOSPC) + && !(eap->argt & EX_NOSPC) ) { char_u *l; #ifdef BACKSLASH_IN_FILENAME @@ -4311,7 +4402,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) * One file argument: Expand wildcards. * Don't do this with ":r !command" or ":w !command". */ - if ((eap->argt & NOSPC) && !eap->usefilter) { + if ((eap->argt & EX_NOSPC) && !eap->usefilter) { // Replace environment variables. if (has_wildcards) { /* @@ -4424,38 +4515,39 @@ void separate_nextcmd(exarg_T *eap) for (; *p; MB_PTR_ADV(p)) { if (*p == Ctrl_V) { - if (eap->argt & (USECTRLV | XFILE)) - ++p; /* skip CTRL-V and next char */ - else - /* remove CTRL-V and skip next char */ + if (eap->argt & (EX_CTRLV | EX_XFILE)) { + p++; // skip CTRL-V and next char + } else { + // remove CTRL-V and skip next char STRMOVE(p, p + 1); - if (*p == NUL) /* stop at NUL after CTRL-V */ + } + if (*p == NUL) { // stop at NUL after CTRL-V break; - } - /* Skip over `=expr` when wildcards are expanded. */ - else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) { + } + } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) { + // Skip over `=expr` when wildcards are expanded. p += 2; (void)skip_expr(&p); if (*p == NUL) { // stop at NUL after CTRL-V break; } - } - /* Check for '"': start of comment or '|': next command */ - /* :@" does not start a comment! - * :redir @" doesn't either. */ - else if ((*p == '"' && !(eap->argt & NOTRLCOM) - && (eap->cmdidx != CMD_at || p != eap->arg) - && (eap->cmdidx != CMD_redir - || p != eap->arg + 1 || p[-1] != '@')) - || *p == '|' || *p == '\n') { - /* - * We remove the '\' before the '|', unless USECTRLV is used - * AND 'b' is present in 'cpoptions'. - */ + } else if ( + // Check for '"': start of comment or '|': next command */ + // :@" does not start a comment! + // :redir @" doesn't either. + (*p == '"' + && !(eap->argt & EX_NOTRLCOM) + && (eap->cmdidx != CMD_at || p != eap->arg) + && (eap->cmdidx != CMD_redir + || p != eap->arg + 1 || p[-1] != '@')) + || *p == '|' + || *p == '\n') { + // We remove the '\' before the '|', unless EX_CTRLV is used + // AND 'b' is present in 'cpoptions'. if ((vim_strchr(p_cpo, CPO_BAR) == NULL - || !(eap->argt & USECTRLV)) && *(p - 1) == '\\') { - STRMOVE(p - 1, p); /* remove the '\' */ - --p; + || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\') { + STRMOVE(p - 1, p); // remove the '\' + p--; } else { eap->nextcmd = check_nextcmd(p); *p = NUL; @@ -4464,8 +4556,9 @@ void separate_nextcmd(exarg_T *eap) } } - if (!(eap->argt & NOTRLCOM)) /* remove trailing spaces */ + if (!(eap->argt & EX_NOTRLCOM)) { // remove trailing spaces del_trailing_spaces(eap->arg); + } } /* @@ -4742,10 +4835,8 @@ static void ex_abclear(exarg_T *eap) static void ex_autocmd(exarg_T *eap) { - /* - * Disallow auto commands from .exrc and .vimrc in current - * directory for security reasons. - */ + // Disallow autocommands from .exrc and .vimrc in current + // directory for security reasons. if (secure) { secure = 2; eap->errmsg = e_curdir; @@ -4957,10 +5048,10 @@ char_u *get_command_name(expand_T *xp, int idx) static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, int compl, - char_u *compl_arg, int addr_type, int force) + char_u *compl_arg, cmd_addr_T addr_type, bool force) + FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; - char_u *p; int i; int cmp = 1; char_u *rep_buf = NULL; @@ -5020,7 +5111,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, if (cmp != 0) { ga_grow(gap, 1); - p = vim_strnsave(name, (int)name_len); + char_u *const p = vim_strnsave(name, name_len); cmd = USER_CMD_GA(gap, i); memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); @@ -5049,7 +5140,7 @@ fail: static struct { - int expand; + cmd_addr_T expand; char *name; char *shortname; } addr_type_complete[] = @@ -5062,7 +5153,7 @@ static struct { { ADDR_WINDOWS, "windows", "win" }, { ADDR_QUICKFIX, "quickfix", "qf" }, { ADDR_OTHER, "other", "?" }, - { -1, NULL, NULL } + { ADDR_NONE, NULL, NULL } }; /* @@ -5156,11 +5247,11 @@ static void uc_list(char_u *name, size_t name_len) // Special cases int len = 4; - if (a & BANG) { + if (a & EX_BANG) { msg_putchar('!'); len--; } - if (a & REGSTR) { + if (a & EX_REGSTR) { msg_putchar('"'); len--; } @@ -5168,7 +5259,7 @@ static void uc_list(char_u *name, size_t name_len) msg_putchar('b'); len--; } - if (a & TRLBAR) { + if (a & EX_TRLBAR) { msg_putchar('|'); len--; } @@ -5190,20 +5281,20 @@ static void uc_list(char_u *name, size_t name_len) len = 0; // Arguments - switch (a & (EXTRA|NOSPC|NEEDARG)) { + switch (a & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: IObuff[len++] = '0'; break; - case (EXTRA): + case (EX_EXTRA): IObuff[len++] = '*'; break; - case (EXTRA|NOSPC): + case (EX_EXTRA | EX_NOSPC): IObuff[len++] = '?'; break; - case (EXTRA|NEEDARG): + case (EX_EXTRA | EX_NEEDARG): IObuff[len++] = '+'; break; - case (EXTRA|NOSPC|NEEDARG): + case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): IObuff[len++] = '1'; break; } @@ -5213,13 +5304,13 @@ static void uc_list(char_u *name, size_t name_len) } while (len < 5 - over); // Address / Range - if (a & (RANGE|COUNT)) { - if (a & COUNT) { + if (a & (EX_RANGE | EX_COUNT)) { + if (a & EX_COUNT) { // -count=N snprintf((char *)IObuff + len, IOSIZE, "%" PRId64 "c", (int64_t)cmd->uc_def); len += (int)STRLEN(IObuff + len); - } else if (a & DFLALL) { + } else if (a & EX_DFLALL) { IObuff[len++] = '%'; } else if (cmd->uc_def >= 0) { // -range=N @@ -5236,7 +5327,7 @@ static void uc_list(char_u *name, size_t name_len) } while (len < 8 - over); // Address Type - for (j = 0; addr_type_complete[j].expand != -1; j++) { + for (j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { STRCPY(IObuff + len, addr_type_complete[j].shortname); @@ -5284,7 +5375,8 @@ static void uc_list(char_u *name, size_t name_len) static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags, int *complp, char_u **compl_arg, - int *addr_type_arg) + cmd_addr_T *addr_type_arg) + FUNC_ATTR_NONNULL_ALL { char_u *p; @@ -5293,16 +5385,16 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, return FAIL; } - /* First, try the simple attributes (no arguments) */ - if (STRNICMP(attr, "bang", len) == 0) - *argt |= BANG; - else if (STRNICMP(attr, "buffer", len) == 0) + // First, try the simple attributes (no arguments) + if (STRNICMP(attr, "bang", len) == 0) { + *argt |= EX_BANG; + } else if (STRNICMP(attr, "buffer", len) == 0) { *flags |= UC_BUFFER; - else if (STRNICMP(attr, "register", len) == 0) - *argt |= REGSTR; - else if (STRNICMP(attr, "bar", len) == 0) - *argt |= TRLBAR; - else { + } else if (STRNICMP(attr, "register", len) == 0) { + *argt |= EX_REGSTR; + } else if (STRNICMP(attr, "bar", len) == 0) { + *argt |= EX_TRLBAR; + } else { int i; char_u *val = NULL; size_t vallen = 0; @@ -5320,28 +5412,29 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, if (STRNICMP(attr, "nargs", attrlen) == 0) { if (vallen == 1) { - if (*val == '0') - /* Do nothing - this is the default */; - else if (*val == '1') - *argt |= (EXTRA | NOSPC | NEEDARG); - else if (*val == '*') - *argt |= EXTRA; - else if (*val == '?') - *argt |= (EXTRA | NOSPC); - else if (*val == '+') - *argt |= (EXTRA | NEEDARG); - else + if (*val == '0') { + // Do nothing - this is the default; + } else if (*val == '1') { + *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG); + } else if (*val == '*') { + *argt |= EX_EXTRA; + } else if (*val == '?') { + *argt |= (EX_EXTRA | EX_NOSPC); + } else if (*val == '+') { + *argt |= (EX_EXTRA | EX_NEEDARG); + } else { goto wrong_nargs; + } } else { wrong_nargs: EMSG(_("E176: Invalid number of arguments")); return FAIL; } } else if (STRNICMP(attr, "range", attrlen) == 0) { - *argt |= RANGE; - if (vallen == 1 && *val == '%') - *argt |= DFLALL; - else if (val != NULL) { + *argt |= EX_RANGE; + if (vallen == 1 && *val == '%') { + *argt |= EX_DFLALL; + } else if (val != NULL) { p = val; if (*def >= 0) { two_count: @@ -5350,7 +5443,7 @@ two_count: } *def = getdigits_long(&p, true, 0); - *argt |= (ZEROR | NOTADR); + *argt |= EX_ZEROR; if (p != val + vallen || vallen == 0) { invalid_count: @@ -5358,8 +5451,16 @@ invalid_count: return FAIL; } } + // default for -range is using buffer lines + if (*addr_type_arg == ADDR_NONE) { + *addr_type_arg = ADDR_LINES; + } } else if (STRNICMP(attr, "count", attrlen) == 0) { - *argt |= (COUNT | ZEROR | RANGE | NOTADR); + *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE); + // default for -count is using any number + if (*addr_type_arg == ADDR_NONE) { + *addr_type_arg = ADDR_OTHER; + } if (val != NULL) { p = val; @@ -5385,16 +5486,16 @@ invalid_count: return FAIL; } } else if (STRNICMP(attr, "addr", attrlen) == 0) { - *argt |= RANGE; + *argt |= EX_RANGE; if (val == NULL) { EMSG(_("E179: argument required for -addr")); return FAIL; } - if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) { + if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL) { return FAIL; } if (*addr_type_arg != ADDR_LINES) { - *argt |= (ZEROR | NOTADR); + *argt |= EX_ZEROR; } } else { char_u ch = attr[len]; @@ -5421,7 +5522,7 @@ static void ex_command(exarg_T *eap) int flags = 0; int compl = EXPAND_NOTHING; char_u *compl_arg = NULL; - int addr_type_arg = ADDR_LINES; + cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); int name_len; @@ -5698,8 +5799,9 @@ uc_check_code( /* When specified there is a single argument don't split it. * Works for ":Cmd %" when % is "a b c". */ - if ((eap->argt & NOSPC) && quote == 2) + if ((eap->argt & EX_NOSPC) && quote == 2) { quote = 1; + } switch (quote) { case 0: /* No quoting, no splitting */ @@ -6079,12 +6181,12 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx) /* * Parse address type argument */ -int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, - int *addr_type_arg) +int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) + FUNC_ATTR_NONNULL_ALL { int i, a, b; - for (i = 0; addr_type_complete[i].expand != -1; i++) { + for (i = 0; addr_type_complete[i].expand != ADDR_NONE; i++) { a = (int)STRLEN(addr_type_complete[i].name) == vallen; b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; if (a && b) { @@ -6093,7 +6195,7 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, } } - if (addr_type_complete[i].expand == -1) { + if (addr_type_complete[i].expand == ADDR_NONE) { char_u *err = value; for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} @@ -6102,9 +6204,6 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, return FAIL; } - if (*addr_type_arg != ADDR_LINES) - *argt |= NOTADR; - return OK; } @@ -6117,6 +6216,7 @@ int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, */ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, char_u **compl_arg) + FUNC_ATTR_NONNULL_ALL { const char_u *arg = NULL; size_t arglen = 0; @@ -6141,9 +6241,9 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, && STRNCMP(value, command_complete[i], valend) == 0) { *complp = i; if (i == EXPAND_BUFFERS) { - *argt |= BUFNAME; + *argt |= EX_BUFNAME; } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES) { - *argt |= XFILE; + *argt |= EX_XFILE; } break; } @@ -6166,8 +6266,9 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, return FAIL; } - if (arg != NULL) - *compl_arg = vim_strnsave(arg, (int)arglen); + if (arg != NULL) { + *compl_arg = vim_strnsave(arg, arglen); + } return OK; } @@ -7340,7 +7441,7 @@ static void ex_syncbind(exarg_T *eap) topline = curwin->w_topline; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_scb && wp->w_buffer) { - y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(); + y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(curwin); if (topline > y) { topline = y; } @@ -7394,11 +7495,12 @@ static void ex_read(exarg_T *eap) int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); linenr_T lnum; - if (eap->usefilter) /* :r!cmd */ - do_bang(1, eap, FALSE, FALSE, TRUE); - else { - if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) + if (eap->usefilter) { // :r!cmd + do_bang(1, eap, false, false, true); + } else { + if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) { return; + } if (*eap->arg == NUL) { if (check_fname() == FAIL) /* check for no file name */ @@ -7770,7 +7872,7 @@ static void ex_copymove(exarg_T *eap) * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n' */ if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count) { - EMSG(_(e_invaddr)); + EMSG(_(e_invrange)); return; } @@ -7868,7 +7970,7 @@ static void ex_at(exarg_T *eap) */ static void ex_bang(exarg_T *eap) { - do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE); + do_bang(eap->addr_count, eap, eap->forceit, true, true); } /* @@ -8028,7 +8130,7 @@ static void ex_redraw(exarg_T *eap) RedrawingDisabled = 0; p_lz = FALSE; validate_cursor(); - update_topline(); + update_topline(curwin); if (eap->forceit) { redraw_all_later(NOT_VALID); } @@ -8177,10 +8279,11 @@ static void ex_mark(exarg_T *eap) */ void update_topline_cursor(void) { - check_cursor(); /* put cursor on valid line */ - update_topline(); - if (!curwin->w_p_wrap) + check_cursor(); // put cursor on valid line + update_topline(curwin); + if (!curwin->w_p_wrap) { validate_cursor(); + } update_curswant(); } @@ -8617,6 +8720,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) * '#' to curwin->w_altfile * '<cword>' to word under the cursor * '<cWORD>' to WORD under the cursor + * '<cexpr>' to C-expression under the cursor * '<cfile>' to path name under the cursor * '<sfile>' to sourced file name * '<slnum>' to sourced file line number @@ -9260,7 +9364,7 @@ static void ex_match(exarg_T *eap) } else { p = skiptowhite(eap->arg); if (!eap->skip) { - g = vim_strnsave(eap->arg, (int)(p - eap->arg)); + g = vim_strnsave(eap->arg, p - eap->arg); } p = skipwhite(p); if (*p == NUL) { @@ -9445,16 +9549,16 @@ Dictionary commands_array(buf_T *buf) PUT(d, "name", STRING_OBJ(cstr_to_string((char *)cmd->uc_name))); PUT(d, "definition", STRING_OBJ(cstr_to_string((char *)cmd->uc_rep))); PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); - PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & BANG))); - PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & TRLBAR))); - PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & REGSTR))); + PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); + PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); + PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); - switch (cmd->uc_argt & (EXTRA|NOSPC|NEEDARG)) { - case 0: arg[0] = '0'; break; - case(EXTRA): arg[0] = '*'; break; - case(EXTRA|NOSPC): arg[0] = '?'; break; - case(EXTRA|NEEDARG): arg[0] = '+'; break; - case(EXTRA|NOSPC|NEEDARG): arg[0] = '1'; break; + switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { + case 0: arg[0] = '0'; break; + case(EX_EXTRA): arg[0] = '*'; break; + case(EX_EXTRA | EX_NOSPC): arg[0] = '?'; break; + case(EX_EXTRA | EX_NEEDARG): arg[0] = '+'; break; + case(EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; } PUT(d, "nargs", STRING_OBJ(cstr_to_string(arg))); @@ -9465,7 +9569,7 @@ Dictionary commands_array(buf_T *buf) ? NIL : STRING_OBJ(cstr_to_string((char *)cmd->uc_compl_arg))); Object obj = NIL; - if (cmd->uc_argt & COUNT) { + if (cmd->uc_argt & EX_COUNT) { if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, (int64_t)cmd->uc_def); obj = STRING_OBJ(cstr_to_string(str)); // -count=N @@ -9476,8 +9580,8 @@ Dictionary commands_array(buf_T *buf) PUT(d, "count", obj); obj = NIL; - if (cmd->uc_argt & RANGE) { - if (cmd->uc_argt & DFLALL) { + if (cmd->uc_argt & EX_RANGE) { + if (cmd->uc_argt & EX_DFLALL) { obj = STRING_OBJ(cstr_to_string("%")); // -range=% } else if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, (int64_t)cmd->uc_def); @@ -9489,7 +9593,7 @@ Dictionary commands_array(buf_T *buf) PUT(d, "range", obj); obj = NIL; - for (int j = 0; addr_type_complete[j].expand != -1; j++) { + for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { obj = STRING_OBJ(cstr_to_string(addr_type_complete[j].name)); diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 0c7562980a..0917c6dd02 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -518,10 +518,13 @@ fail: * Discard an exception. "was_finished" is set when the exception has been * caught and the catch clause has been ended normally. */ -static void discard_exception(except_T *excp, int was_finished) +static void discard_exception(except_T *excp, bool was_finished) { char_u *saved_IObuff; + if (current_exception == excp) { + current_exception = NULL; + } if (excp == NULL) { internal_error("discard_exception()"); return; @@ -569,7 +572,6 @@ void discard_current_exception(void) { if (current_exception != NULL) { discard_exception(current_exception, false); - current_exception = NULL; } // Note: all globals manipulated here should be saved/restored in // try_enter/try_leave. @@ -652,8 +654,8 @@ static void finish_exception(except_T *excp) set_vim_var_string(VV_THROWPOINT, NULL, -1); } - /* Discard the exception, but use the finish message for 'verbose'. */ - discard_exception(excp, TRUE); + // Discard the exception, but use the finish message for 'verbose'. + discard_exception(excp, true); } /* @@ -1812,11 +1814,12 @@ void leave_cleanup(cleanup_T *csp) * made pending by it. Report this to the user if required by the * 'verbose' option or when debugging. */ if (aborting() || need_rethrow) { - if (pending & CSTP_THROW) - /* Cancel the pending exception (includes report). */ - discard_exception(csp->exception, FALSE); - else + if (pending & CSTP_THROW) { + // Cancel the pending exception (includes report). + discard_exception(csp->exception, false); + } else { report_discard_pending(pending, NULL); + } /* If an error was about to be converted to an exception when * enter_cleanup() was called, free the message list. */ @@ -1916,15 +1919,13 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) default: if (cstack->cs_flags[idx] & CSF_FINALLY) { if (cstack->cs_pending[idx] & CSTP_THROW) { - /* Cancel the pending exception. This is in the - * finally clause, so that the stack of the - * caught exceptions is not involved. */ - discard_exception((except_T *) - cstack->cs_exception[idx], - FALSE); - } else - report_discard_pending(cstack->cs_pending[idx], - NULL); + // Cancel the pending exception. This is in the + // finally clause, so that the stack of the + // caught exceptions is not involved. + discard_exception((except_T *)cstack->cs_exception[idx], false); + } else { + report_discard_pending(cstack->cs_pending[idx], NULL); + } cstack->cs_pending[idx] = CSTP_NONE; } break; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ed408c28e5..626b840798 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -523,7 +523,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, // positioned in the same way as the actual search command restore_viewstate(&s->old_viewstate); changed_cline_bef_curs(); - update_topline(); + update_topline(curwin); if (found != 0) { pos_T save_pos = curwin->w_cursor; @@ -1082,6 +1082,7 @@ static int command_line_execute(VimState *state, int key) if (s->c == K_DOWN && ccline.cmdpos > 0 && ccline.cmdbuff[ccline.cmdpos - 1] == '.') { s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } else if (s->c == K_UP) { // Hitting <Up>: Remove one submenu name in front of the // cursor @@ -1112,6 +1113,7 @@ static int command_line_execute(VimState *state, int key) cmdline_del(i); } s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped s->xpc.xp_context = EXPAND_NOTHING; } } @@ -1134,6 +1136,7 @@ static int command_line_execute(VimState *state, int key) || ccline.cmdbuff[ccline.cmdpos - 3] != '.')) { // go down a directory s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } else if (STRNCMP(s->xpc.xp_pattern, upseg + 1, 3) == 0 && s->c == K_DOWN) { // If in a direct ancestor, strip off one ../ to go down @@ -1154,6 +1157,7 @@ static int command_line_execute(VimState *state, int key) && (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) { cmdline_del(j - 2); s->c = (int)p_wc; + KeyTyped = true; // in case the key was mapped } } else if (s->c == K_UP) { // go up a directory @@ -1546,7 +1550,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, set_search_match(&s->match_end); curwin->w_cursor = s->match_start; changed_cline_bef_curs(); - update_topline(); + update_topline(curwin); validate_cursor(); highlight_match = true; save_viewstate(&s->old_viewstate); @@ -1880,9 +1884,6 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); // Ignore mouse case K_MIDDLEMOUSE: - if (!mouse_has(MOUSE_COMMAND)) { - return command_line_not_changed(s); // Ignore mouse - } cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -1906,10 +1907,6 @@ static int command_line_handle_key(CommandLineState *s) s->ignore_drag_release = false; } - if (!mouse_has(MOUSE_COMMAND)) { - return command_line_not_changed(s); // Ignore mouse - } - ccline.cmdspos = cmd_startcol(); for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; ccline.cmdpos++) { @@ -2242,7 +2239,7 @@ static int command_line_changed(CommandLineState *s) // Restore the window "view". curwin->w_cursor = s->is_state.save_cursor; restore_viewstate(&s->is_state.old_viewstate); - update_topline(); + update_topline(curwin); redrawcmdline(); @@ -2251,7 +2248,9 @@ static int command_line_changed(CommandLineState *s) close_preview_windows(); update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { - may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); + if (s->xpc.xp_context == EXPAND_NOTHING) { + may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); + } } if (cmdmsg_rl || (p_arshape && !p_tbidi)) { @@ -4229,17 +4228,11 @@ ExpandOne ( * Prepare an expand structure for use. */ void ExpandInit(expand_T *xp) + FUNC_ATTR_NONNULL_ALL { - xp->xp_pattern = NULL; - xp->xp_pattern_len = 0; + CLEAR_POINTER(xp); xp->xp_backslash = XP_BS_NONE; -#ifndef BACKSLASH_IN_FILENAME - xp->xp_shell = FALSE; -#endif xp->xp_numfiles = -1; - xp->xp_files = NULL; - xp->xp_arg = NULL; - xp->xp_line = NULL; } /* @@ -4327,7 +4320,8 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o /// if true then it escapes for a shell command. /// /// @return [allocated] escaped file name. -char *vim_strsave_fnameescape(const char *const fname, const bool shell) +char *vim_strsave_fnameescape(const char *const fname, + const bool shell FUNC_ATTR_UNUSED) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { #ifdef BACKSLASH_IN_FILENAME @@ -4786,7 +4780,7 @@ char_u *addstar(char_u *fname, size_t len, int context) * EXPAND_COMMANDS Cursor is still touching the command, so complete * it. * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands. - * EXPAND_FILES After command with XFILE set, or after setting + * EXPAND_FILES After command with EX_XFILE set, or after setting * with P_EXPAND set. eg :e ^I, :w>>^I * EXPAND_DIRECTORIES In some cases this is used instead of the latter * when we know only directories are of interest. eg @@ -5398,7 +5392,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, } /// Call "user_expand_func()" to invoke a user defined Vim script function and -/// return the result (either a string or a List). +/// return the result (either a string, a List or NULL). static void * call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file, char_u ***file) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 42a9ef08f9..14dac9a126 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -526,8 +526,12 @@ static int makeopens(FILE *fd, char_u *dirnow) } } - // Close all windows but one. + // Close all windows and tabs but one. PUTLINE_FAIL("silent only"); + if ((ssop_flags & SSOP_TABPAGES) + && put_line(fd, "silent tabonly") == FAIL) { + return FAIL; + } // // Now a :cd command to the session directory or the current directory @@ -606,13 +610,26 @@ static int makeopens(FILE *fd, char_u *dirnow) // tab_firstwin = firstwin; // First window in tab page "tabnr". tab_topframe = topframe; + if ((ssop_flags & SSOP_TABPAGES)) { + // Similar to ses_win_rec() below, populate the tab pages first so + // later local options won't be copied to the new tabs. + FOR_ALL_TABS(tp) { + if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) { + return FAIL; + } + } + + if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) { + return FAIL; + } + } for (tabnr = 1;; tabnr++) { tabpage_T *tp = find_tabpage(tabnr); if (tp == NULL) { break; // done all tab pages } - int need_tabnew = false; + bool need_tabnext = false; int cnr = 1; if ((ssop_flags & SSOP_TABPAGES)) { @@ -624,7 +641,7 @@ static int makeopens(FILE *fd, char_u *dirnow) tab_topframe = tp->tp_topframe; } if (tabnr > 1) { - need_tabnew = true; + need_tabnext = true; } } @@ -639,11 +656,15 @@ static int makeopens(FILE *fd, char_u *dirnow) && !bt_help(wp->w_buffer) && !bt_nofile(wp->w_buffer) ) { - if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 + if (need_tabnext && put_line(fd, "tabnext") == FAIL) { + return FAIL; + } + need_tabnext = false; + + if (fputs("edit ", fd) < 0 || ses_fname(fd, wp->w_buffer, &ssop_flags, true) == FAIL) { return FAIL; } - need_tabnew = false; if (!wp->w_arg_idx_invalid) { edited_win = wp; } @@ -652,7 +673,7 @@ static int makeopens(FILE *fd, char_u *dirnow) } // If no file got edited create an empty tab page. - if (need_tabnew && put_line(fd, "tabnew") == FAIL) { + if (need_tabnext && put_line(fd, "tabnext") == FAIL) { return FAIL; } @@ -775,6 +796,7 @@ static int makeopens(FILE *fd, char_u *dirnow) // if (fprintf(fd, "%s", "if exists('s:wipebuf') " + "&& len(win_findbuf(s:wipebuf)) == 0" "&& getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'\n" " silent exe 'bwipe ' . s:wipebuf\n" "endif\n" @@ -893,6 +915,9 @@ void ex_mkrc(exarg_T *eap) && (*flagp & SSOP_OPTIONS))) { failed |= (makemap(fd, NULL) == FAIL || makeset(fd, OPT_GLOBAL, false) == FAIL); + if (p_hls && fprintf(fd, "%s", "set hlsearch\n") < 0) { + failed = true; + } } if (!failed && view_session) { @@ -949,11 +974,16 @@ void ex_mkrc(exarg_T *eap) } if (fprintf(fd, "%s", - "let &g:so = s:so_save | let &g:siso = s:siso_save\n" - "doautoall SessionLoadPost\n") + "let &g:so = s:so_save | let &g:siso = s:siso_save\n") < 0) { failed = true; } + if (no_hlsearch && fprintf(fd, "%s", "nohlsearch\n") < 0) { + failed = true; + } + if (fprintf(fd, "%s", "doautoall SessionLoadPost\n") < 0) { + failed = true; + } if (eap->cmdidx == CMD_mksession) { if (fprintf(fd, "unlet SessionLoad\n") < 0) { failed = true; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index ba685b158e..b2d8532cd7 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -560,6 +560,23 @@ void extmark_adjust(buf_T *buf, new_row, 0, new_byte, undo); } +// Adjust extmarks following a text edit. +// +// @param buf +// @param start_row Start row of the region to be changed +// @param start_col Start col of the region to be changed +// @param old_row End row of the region to be changed. +// Encoded as an offset to start_row. +// @param old_col End col of the region to be changed. Encodes +// an offset from start_col if old_row = 0; otherwise, +// encodes the end column of the old region. +// @param old_byte Byte extent of the region to be changed. +// @param new_row Row offset of the new region. +// @param new_col Col offset of the new region. Encodes an offset from +// start_col if new_row = 0; otherwise, encodes +// the end column of the new region. +// @param new_byte Byte extent of the new region. +// @param undo void extmark_splice(buf_T *buf, int start_row, colnr_T start_col, int old_row, colnr_T old_col, bcount_t old_byte, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index ab8072ae1d..a542bb19dd 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -15,6 +15,7 @@ #include "nvim/ascii.h" #include "nvim/fileio.h" #include "nvim/buffer.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -1651,9 +1652,6 @@ failed: # ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { iconv_close(iconv_fd); -# ifndef __clang_analyzer__ - iconv_fd = (iconv_t)-1; -# endif } # endif @@ -2031,7 +2029,7 @@ static char_u *next_fenc(char_u **pp, bool *alloced) r = enc_canonize(*pp); *pp += STRLEN(*pp); } else { - r = vim_strnsave(*pp, (int)(p - *pp)); + r = vim_strnsave(*pp, p - *pp); *pp = p + 1; p = enc_canonize(r); xfree(r); @@ -4216,7 +4214,9 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force) && (force || buf->b_sfname == NULL || path_is_absolute(buf->b_sfname))) { - XFREE_CLEAR(buf->b_sfname); + if (buf->b_sfname != buf->b_ffname) { + XFREE_CLEAR(buf->b_sfname); + } p = path_shorten_fname(buf->b_ffname, dirname); if (p != NULL) { buf->b_sfname = vim_strsave(p); @@ -4672,7 +4672,6 @@ check_timestamps( ) { int didit = 0; - int n; /* Don't check timestamps while system() or another low-level function may * cause us to lose and gain focus. */ @@ -4700,7 +4699,7 @@ check_timestamps( if (buf->b_nwindows > 0) { bufref_T bufref; set_bufref(&bufref, buf); - n = buf_check_timestamp(buf, focus); + const int n = buf_check_timestamp(buf); if (didit < n) { didit = n; } @@ -4770,11 +4769,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) * return 2 if a message has been displayed. * return 0 otherwise. */ -int -buf_check_timestamp( - buf_T *buf, - int focus /* called for GUI focus event */ -) +int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { int retval = 0; @@ -5082,6 +5077,7 @@ void buf_reload(buf_T *buf, int orig_mode) // Mark all undo states as changed. u_unchanged(curbuf); } + buf_updates_unregister_all(curbuf); } } xfree(ea.cmd); @@ -5101,8 +5097,8 @@ void buf_reload(buf_T *buf, int orig_mode) curwin->w_topline = old_topline; curwin->w_cursor = old_cursor; check_cursor(); - update_topline(); - keep_filetype = FALSE; + update_topline(curwin); + keep_filetype = false; /* Update folds unless they are defined manually. */ FOR_ALL_TAB_WINDOWS(tp, wp) { diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 654aa6d5ba..0593c16999 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -902,8 +902,9 @@ int foldMoveTo( bool last = false; for (;; ) { if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) { - if (!updown) + if (!updown || gap->ga_len == 0) { break; + } /* When moving up, consider a fold above the cursor; when * moving down consider a fold below the cursor. */ diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 38b51b5564..e0b0610646 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -213,6 +213,8 @@ # define FUNC_API_REMOTE_ONLY /// API function not exposed in VimL/remote. # define FUNC_API_LUA_ONLY +/// API function checked textlock. +# define FUNC_API_CHECK_TEXTLOCK /// API function introduced at the given API level. # define FUNC_API_SINCE(X) /// API function deprecated since the given API level. diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index de098b7a7b..c9ab0cf709 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -43,6 +43,7 @@ local c_proto = Ct( (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) * (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) * + (fill * Cg((P('FUNC_API_CHECK_TEXTLOCK') * Cc(true)), 'check_textlock') ^ -1) * (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index b31209e8ff..7e78b9e9d6 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -260,6 +260,13 @@ for i = 1, #functions do args[#args + 1] = converted end + if fn.check_textlock then + output:write('\n if (textlock != 0) {') + output:write('\n api_set_error(error, kErrorTypeException, "%s", e_secure);') + output:write('\n goto cleanup;') + output:write('\n }\n') + end + -- function call local call_args = table.concat(args, ', ') output:write('\n ') @@ -393,6 +400,16 @@ local function process_function(fn) } ]], fn.name)) end + + if fn.check_textlock then + write_shifted_output(output, [[ + if (textlock != 0) { + api_set_error(&err, kErrorTypeException, "%s", e_secure); + goto exit_0; + } + ]]) + end + local cparams = '' local free_code = {} for j = #fn.parameters,1,-1 do diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 849c82f50e..844661adc3 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -22,7 +22,10 @@ local defsfname = autodir .. '/ex_cmds_defs.generated.h' local enumfile = io.open(enumfname, 'w') local defsfile = io.open(defsfname, 'w') -local defs = require('ex_cmds') +local bit = require 'bit' +local ex_cmds = require('ex_cmds') +local defs = ex_cmds.cmds +local flags = ex_cmds.flags local byte_a = string.byte('a') local byte_z = string.byte('z') @@ -51,6 +54,17 @@ static CommandDefinition cmdnames[%u] = { ]], #defs, #defs)) local cmds, cmdidxs1, cmdidxs2 = {}, {}, {} for _, cmd in ipairs(defs) do + if bit.band(cmd.flags, flags.RANGE) == flags.RANGE then + assert(cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Using RANGE with ADDR_NONE\n', cmd.command)) + else + assert(cmd.addr_type == 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing ADDR_NONE\n', cmd.command)) + end + if bit.band(cmd.flags, flags.DFLALL) == flags.DFLALL then + assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE', + string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command)) + end local enumname = cmd.enum or ('CMD_' .. cmd.command) local byte_cmd = cmd.command:sub(1, 1):byte() if byte_a <= byte_cmd and byte_cmd <= byte_z then @@ -62,7 +76,7 @@ for _, cmd in ipairs(defs) do .cmd_name = (char_u *) "%s", .cmd_func = (ex_func_T)&%s, .cmd_argt = %uL, - .cmd_addr_type = %i + .cmd_addr_type = %s }, ]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) end diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 566a5ae9d6..3b8f4116b7 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -215,6 +215,8 @@ EXTERN bool emsg_severe INIT(= false); // use message of next of several EXTERN int did_endif INIT(= false); // just had ":endif" EXTERN dict_T vimvardict; // Dictionary with v: variables EXTERN dict_T globvardict; // Dictionary with g: variables +/// g: value +#define globvarht globvardict.dv_hashtab EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown EXTERN bool called_vim_beep; // set if vim_beep() is called @@ -864,7 +866,6 @@ EXTERN char_u e_failed[] INIT(= N_("E472: Command failed")); EXTERN char_u e_internal[] INIT(= N_("E473: Internal error")); EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); EXTERN char_u e_interr[] INIT(= N_("Interrupted")); -EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address")); EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument")); EXTERN char_u e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); EXTERN char_u e_invargval[] INIT(= N_("E475: Invalid value for argument %s")); @@ -935,6 +936,7 @@ EXTERN char_u e_readonly[] INIT(= N_( "E45: 'readonly' option is set (add ! to override)")); EXTERN char_u e_readonlyvar[] INIT(= N_( "E46: Cannot change read-only variable \"%.*s\"")); +EXTERN char_u e_stringreq[] INIT(= N_("E928: String required")); EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); EXTERN char_u e_toomanyarg[] INIT(= N_( "E118: Too many arguments for function: %s")); @@ -1052,4 +1054,7 @@ typedef enum { #define MIN_CD_SCOPE kCdScopeWindow #define MAX_CD_SCOPE kCdScopeGlobal +// Only filled for Win32. +EXTERN char windowsVersion[20] INIT(= { 0 }); + #endif // NVIM_GLOBALS_H diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 2c954008b3..4ec949759c 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -964,7 +964,6 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T #define PRT_PS_DEFAULT_DPI (72) // Default user space resolution #define PRT_PS_DEFAULT_FONTSIZE (10) -#define PRT_PS_DEFAULT_BUFFER_SIZE (80) #define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / \ sizeof(struct prt_mediasize_S)) diff --git a/src/nvim/log.c b/src/nvim/log.c index 225e40cdb4..19203a3c2a 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -13,9 +13,6 @@ #include <stdarg.h> #include <stdbool.h> #include <stdio.h> -#if !defined(WIN32) -# include <sys/time.h> // for gettimeofday() -#endif #include <uv.h> #include "auto/config.h" @@ -296,12 +293,10 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, } int millis = 0; -#if !defined(WIN32) - struct timeval curtime; - if (gettimeofday(&curtime, NULL) == 0) { + uv_timeval64_t curtime; + if (uv_gettimeofday(&curtime) == 0) { millis = (int)curtime.tv_usec / 1000; } -#endif // Print the log message. int64_t pid = os_get_pid(); diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 2c7ab46ffe..b20fbbf038 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -115,7 +115,8 @@ function vim._load_package(name) for _,path in ipairs(paths) do local found = vim.api.nvim_get_runtime_file(path, false) if #found > 0 then - return loadfile(found[1]) + local f, err = loadfile(found[1]) + return f or error(err) end end @@ -123,7 +124,8 @@ function vim._load_package(name) local path = "lua/"..trail:gsub('?',basename) local found = vim.api.nvim_get_runtime_file(path, false) if #found > 0 then - return package.loadlib(found[1]) + local f, err = package.loadlib(found[1]) + return f or error(err) end end return nil diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 07dcb4a8e8..303722b4a8 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -98,9 +98,6 @@ # define UTF_COMPOSINGLIKE(p1, p2) utf_composinglike((p1), (p2)) -// Whether to draw the vertical bar on the right side of the cell. -# define CURSOR_BAR_RIGHT (curwin->w_p_rl && (!(State & CMDLINE) || cmdmsg_rl)) - // MB_PTR_ADV(): advance a pointer to the next character, taking care of // multi-byte characters if needed. // MB_PTR_BACK(): backup a pointer to the previous character, taking care of diff --git a/src/nvim/main.c b/src/nvim/main.c index 79c165419e..8bf745966e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -169,6 +169,14 @@ void early_init(mparm_T *paramp) init_normal_cmds(); // Init the table of Normal mode commands. highlight_init(); +#ifdef WIN32 + OSVERSIONINFO ovi; + ovi.dwOSVersionInfoSize = sizeof(ovi); + GetVersionEx(&ovi); + snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d", + (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion); +#endif + #if defined(HAVE_LOCALE_H) // Setup to use the current locale (for ctype() and many other things). // NOTE: Translated messages with encodings other than latin1 will not @@ -531,7 +539,7 @@ int main(int argc, char **argv) // When a startup script or session file setup for diff'ing and // scrollbind, sync the scrollbind now. if (curwin->w_p_diff && curwin->w_p_scb) { - update_topline(); + update_topline(curwin); check_scrollbind((linenr_T)0, 0L); TIME_MSG("diff scrollbinding"); } @@ -645,7 +653,18 @@ void getout(int exitval) } } } - apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf); + + int unblock = 0; + // deathtrap() blocks autocommands, but we do want to trigger + // VimLeavePre. + if (is_autocmd_blocked()) { + unblock_autocmds(); + unblock++; + } + apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, false, curbuf); + if (unblock) { + block_autocmds(); + } } if (p_shada && *p_shada != NUL) { @@ -654,7 +673,17 @@ void getout(int exitval) } if (v_dying <= 1) { + int unblock = 0; + + // deathtrap() blocks autocommands, but we do want to trigger VimLeave. + if (is_autocmd_blocked()) { + unblock_autocmds(); + unblock++; + } apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, false, curbuf); + if (unblock) { + block_autocmds(); + } } profile_dump(); @@ -1414,7 +1443,10 @@ static void read_stdin(void) no_wait_return = true; int save_msg_didany = msg_didany; set_buflisted(true); - (void)open_buffer(true, NULL, 0); // create memfile and read file + + // Create memfile and read from stdin. + (void)open_buffer(true, NULL, 0); + if (BUFEMPTY() && curbuf->b_next != NULL) { // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561 do_cmdline_cmd("silent! bnext"); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 1ecfae57ed..73a9c1d1d7 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -777,6 +777,7 @@ void ex_delmarks(exarg_T *eap) n = i - 'A'; } namedfm[n].fmark.mark.lnum = 0; + namedfm[n].fmark.fnum = 0; XFREE_CLEAR(namedfm[n].fname); } } @@ -1552,3 +1553,87 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) } } } + + +// Add information about mark 'mname' to list 'l' +static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, + const char *fname) + FUNC_ATTR_NONNULL_ARG(1, 2, 3) +{ + if (pos->lnum <= 0) { + return OK; + } + + dict_T *d = tv_dict_alloc(); + tv_list_append_dict(l, d); + + list_T *lpos = tv_list_alloc(kListLenMayKnow); + + tv_list_append_number(lpos, bufnr); + tv_list_append_number(lpos, pos->lnum); + tv_list_append_number(lpos, pos->col + 1); + tv_list_append_number(lpos, pos->coladd); + + if (tv_dict_add_str(d, S_LEN("mark"), mname) == FAIL + || tv_dict_add_list(d, S_LEN("pos"), lpos) == FAIL + || (fname != NULL && tv_dict_add_str(d, S_LEN("file"), fname) == FAIL)) { + return FAIL; + } + + return OK; +} + + +/// Get information about marks local to a buffer. +/// +/// @param[in] buf Buffer to get the marks from +/// @param[out] l List to store marks +void get_buf_local_marks(const buf_T *buf, list_T *l) + FUNC_ATTR_NONNULL_ALL +{ + char mname[3] = "' "; + + // Marks 'a' to 'z' + for (int i = 0; i < NMARKS; i++) { + mname[1] = (char)('a' + i); + add_mark(l, mname, &buf->b_namedm[i].mark, buf->b_fnum, NULL); + } + + // Mark '' is a window local mark and not a buffer local mark + add_mark(l, "''", &curwin->w_pcmark, curbuf->b_fnum, NULL); + + add_mark(l, "'\"", &buf->b_last_cursor.mark, buf->b_fnum, NULL); + add_mark(l, "'[", &buf->b_op_start, buf->b_fnum, NULL); + add_mark(l, "']", &buf->b_op_end, buf->b_fnum, NULL); + add_mark(l, "'^", &buf->b_last_insert.mark, buf->b_fnum, NULL); + add_mark(l, "'.", &buf->b_last_change.mark, buf->b_fnum, NULL); + add_mark(l, "'<", &buf->b_visual.vi_start, buf->b_fnum, NULL); + add_mark(l, "'>", &buf->b_visual.vi_end, buf->b_fnum, NULL); +} + +/// Get information about global marks ('A' to 'Z' and '0' to '9') +/// +/// @param[out] l List to store global marks +void get_global_marks(list_T *l) + FUNC_ATTR_NONNULL_ALL +{ + char mname[3] = "' "; + char *name; + + // Marks 'A' to 'Z' and '0' to '9' + for (int i = 0; i < NMARKS + EXTRA_MARKS; i++) { + if (namedfm[i].fmark.fnum != 0) { + name = (char *)buflist_nr2name(namedfm[i].fmark.fnum, true, true); + } else { + name = (char *)namedfm[i].fname; + } + if (name != NULL) { + mname[1] = i >= NMARKS ? (char)(i - NMARKS + '0') : (char)(i + 'A'); + + add_mark(l, mname, &namedfm[i].fmark.mark, namedfm[i].fmark.fnum, name); + if (namedfm[i].fmark.fnum != 0) { + xfree(name); + } + } + } +} diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index e9ea2cbba9..b3afdefac1 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -55,7 +55,6 @@ #define T MT_BRANCH_FACTOR #define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *)) -#define key_t SKRAPET #define RIGHT_GRAVITY (((uint64_t)1) << 63) #define ANTIGRAVITY(id) ((id)&(RIGHT_GRAVITY-1)) @@ -65,8 +64,6 @@ #define END_FLAG MARKTREE_END_FLAG #define ID_INCR (((uint64_t)1) << 2) -#define PROP_MASK (RIGHT_GRAVITY|PAIRED|END_FLAG) - #define rawkey(itr) (itr->node->key[itr->i]) static bool pos_leq(mtpos_t a, mtpos_t b) diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 70225484ec..31dc6b3649 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -975,9 +975,9 @@ void ml_recover(bool checkext) if (b0p->b0_flags & B0_HAS_FENC) { int fnsize = B0_FNAME_SIZE_NOCRYPT; - for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p) - ; - b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p)); + for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) { + } + b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p); } mf_put(mfp, hp, false, false); /* release block 0 */ diff --git a/src/nvim/message.c b/src/nvim/message.c index f76a408481..f94529c687 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1157,15 +1157,7 @@ void wait_return(int redraw) || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE || c == K_RIGHTDRAG || c == K_RIGHTRELEASE || c == K_MOUSELEFT || c == K_MOUSERIGHT - || c == K_MOUSEDOWN || c == K_MOUSEUP - || (!mouse_has(MOUSE_RETURN) - && mouse_row < msg_row - && (c == K_LEFTMOUSE - || c == K_MIDDLEMOUSE - || c == K_RIGHTMOUSE - || c == K_X1MOUSE - || c == K_X2MOUSE)) - ); + || c == K_MOUSEDOWN || c == K_MOUSEUP); os_breakcheck(); /* * Avoid that the mouse-up event causes visual mode to start. @@ -1711,8 +1703,11 @@ void msg_prt_line(char_u *s, int list) } else if ((l = utfc_ptr2len(s)) > 1) { col += utf_ptr2cells(s); char buf[MB_MAXBYTES + 1]; - if (curwin->w_p_lcs_chars.nbsp != NUL && list - && (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) { + if (l >= MB_MAXBYTES) { + xstrlcpy(buf, "?", sizeof(buf)); + } else if (curwin->w_p_lcs_chars.nbsp != NUL && list + && (utf_ptr2char(s) == 160 + || utf_ptr2char(s) == 0x202f)) { utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf); buf[utfc_ptr2len((char_u *)buf)] = NUL; } else { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index f05dade73f..ff471ea978 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -526,53 +526,9 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) void setmouse(void) { ui_cursor_shape(); - - // Be quick when mouse is off. - if (*p_mouse == NUL) { - return; - } - - int checkfor = MOUSE_NORMAL; // assume normal mode - if (VIsual_active) { - checkfor = MOUSE_VISUAL; - } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) { - checkfor = MOUSE_RETURN; - } else if (State & INSERT) { - checkfor = MOUSE_INSERT; - } else if (State & CMDLINE) { - checkfor = MOUSE_COMMAND; - } else if (State == CONFIRM || State == EXTERNCMD) { - checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd" - } - - if (mouse_has(checkfor)) { - ui_call_mouse_on(); - } else { - ui_call_mouse_off(); - } + ui_check_mouse(); } -/* - * Return true if - * - "c" is in 'mouse', or - * - 'a' is in 'mouse' and "c" is in MOUSE_A, or - * - the current buffer is a help file and 'h' is in 'mouse' and we are in a - * normal editing mode (not at hit-return message). - */ -int mouse_has(int c) -{ - for (char_u *p = p_mouse; *p; ++p) - switch (*p) { - case 'a': if (vim_strchr((char_u *)MOUSE_A, c) != NULL) - return true; - break; - case MOUSE_HELP: if (c != MOUSE_RETURN && curbuf->b_help) - return true; - break; - default: if (c == *p) return true; break; - } - return false; -} // Set orig_topline. Used when jumping to another window, so that a double // click still works. diff --git a/src/nvim/move.c b/src/nvim/move.c index fdcf6bb189..a6afdc27d9 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -129,98 +129,99 @@ void redraw_for_cursorline(win_T *wp) */ void update_topline_redraw(void) { - update_topline(); - if (must_redraw) + update_topline(curwin); + if (must_redraw) { update_screen(0); + } } /* * Update curwin->w_topline to move the cursor onto the screen. */ -void update_topline(void) +void update_topline(win_T *wp) { linenr_T old_topline; int old_topfill; bool check_topline = false; bool check_botline = false; - long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; + long *so_ptr = wp->w_p_so >= 0 ? &wp->w_p_so : &p_so; long save_so = *so_ptr; // If there is no valid screen and when the window height is zero just use // the cursor line. - if (!default_grid.chars || curwin->w_height_inner == 0) { - curwin->w_topline = curwin->w_cursor.lnum; - curwin->w_botline = curwin->w_topline; - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - curwin->w_viewport_invalid = true; - curwin->w_scbind_pos = 1; + if (!default_grid.chars || wp->w_height_inner == 0) { + wp->w_topline = wp->w_cursor.lnum; + wp->w_botline = wp->w_topline; + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + wp->w_viewport_invalid = true; + wp->w_scbind_pos = 1; return; } - check_cursor_moved(curwin); - if (curwin->w_valid & VALID_TOPLINE) + check_cursor_moved(wp); + if (wp->w_valid & VALID_TOPLINE) { return; + } // When dragging with the mouse, don't scroll that quickly if (mouse_dragging > 0) { *so_ptr = mouse_dragging - 1; } - old_topline = curwin->w_topline; - old_topfill = curwin->w_topfill; + old_topline = wp->w_topline; + old_topfill = wp->w_topfill; // If the buffer is empty, always set topline to 1. if (BUFEMPTY()) { // special case - file is empty - if (curwin->w_topline != 1) { - redraw_later(curwin, NOT_VALID); + if (wp->w_topline != 1) { + redraw_later(wp, NOT_VALID); } - curwin->w_topline = 1; - curwin->w_botline = 2; - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - curwin->w_viewport_invalid = true; - curwin->w_scbind_pos = 1; - } - /* - * If the cursor is above or near the top of the window, scroll the window - * to show the line the cursor is in, with 'scrolloff' context. - */ - else { - if (curwin->w_topline > 1) { - /* If the cursor is above topline, scrolling is always needed. - * If the cursor is far below topline and there is no folding, - * scrolling down is never needed. */ - if (curwin->w_cursor.lnum < curwin->w_topline) + wp->w_topline = 1; + wp->w_botline = 2; + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + wp->w_viewport_invalid = true; + wp->w_scbind_pos = 1; + } else { + // If the cursor is above or near the top of the window, scroll the window + // to show the line the cursor is in, with 'scrolloff' context. + if (wp->w_topline > 1) { + // If the cursor is above topline, scrolling is always needed. + // If the cursor is far below topline and there is no folding, + // scrolling down is never needed. + if (wp->w_cursor.lnum < wp->w_topline) { check_topline = true; - else if (check_top_offset()) + } else if (check_top_offset()) { check_topline = true; + } } - /* Check if there are more filler lines than allowed. */ - if (!check_topline && curwin->w_topfill > diff_check_fill(curwin, - curwin->w_topline)) + // Check if there are more filler lines than allowed. + if (!check_topline && wp->w_topfill > diff_check_fill(wp, wp->w_topline)) { check_topline = true; + } if (check_topline) { - int halfheight = curwin->w_height_inner / 2 - 1; + int halfheight = wp->w_height_inner / 2 - 1; if (halfheight < 2) { halfheight = 2; } long n; - if (hasAnyFolding(curwin)) { - /* Count the number of logical lines between the cursor and - * topline + p_so (approximation of how much will be - * scrolled). */ + if (hasAnyFolding(wp)) { + // Count the number of logical lines between the cursor and + // topline + p_so (approximation of how much will be + // scrolled). n = 0; - for (linenr_T lnum = curwin->w_cursor.lnum; - lnum < curwin->w_topline + *so_ptr; lnum++) { + for (linenr_T lnum = wp->w_cursor.lnum; + lnum < wp->w_topline + *so_ptr; lnum++) { n++; // stop at end of file or when we know we are far off - if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight) { + assert(wp->w_buffer != 0); + if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) { break; } - (void)hasFolding(lnum, NULL, &lnum); + (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); } } else { - n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum; + n = wp->w_topline + *so_ptr - wp->w_cursor.lnum; } /* If we weren't very close to begin with, we scroll to put the @@ -233,8 +234,8 @@ void update_topline(void) check_botline = true; } } else { - /* Make sure topline is the first line of a fold. */ - (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + // Make sure topline is the first line of a fold. + (void)hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); check_botline = true; } } @@ -248,35 +249,36 @@ void update_topline(void) * for every small change. */ if (check_botline) { - if (!(curwin->w_valid & VALID_BOTLINE_AP)) - validate_botline(); - - if (curwin->w_botline <= curbuf->b_ml.ml_line_count) { - if (curwin->w_cursor.lnum < curwin->w_botline) { - if (((long)curwin->w_cursor.lnum - >= (long)curwin->w_botline - *so_ptr - || hasAnyFolding(curwin) - )) { + if (!(wp->w_valid & VALID_BOTLINE_AP)) { + validate_botline(wp); + } + + assert(wp->w_buffer != 0); + if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { + if (wp->w_cursor.lnum < wp->w_botline) { + if (((long)wp->w_cursor.lnum + >= (long)wp->w_botline - *so_ptr + || hasAnyFolding(wp))) { lineoff_T loff; /* Cursor is (a few lines) above botline, check if there are * 'scrolloff' window lines below the cursor. If not, need to * scroll. */ - int n = curwin->w_empty_rows; - loff.lnum = curwin->w_cursor.lnum; - /* In a fold go to its last line. */ - (void)hasFolding(loff.lnum, NULL, &loff.lnum); + int n = wp->w_empty_rows; + loff.lnum = wp->w_cursor.lnum; + // In a fold go to its last line. + (void)hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); loff.fill = 0; - n += curwin->w_filler_rows; + n += wp->w_filler_rows; loff.height = 0; - while (loff.lnum < curwin->w_botline - && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0) + while (loff.lnum < wp->w_botline + && (loff.lnum + 1 < wp->w_botline || loff.fill == 0) ) { n += loff.height; if (n >= *so_ptr) { break; } - botline_forw(&loff); + botline_forw(wp, &loff); } if (n >= *so_ptr) { // sufficient context, no need to scroll @@ -289,23 +291,23 @@ void update_topline(void) } if (check_botline) { long line_count = 0; - if (hasAnyFolding(curwin)) { - /* Count the number of logical lines between the cursor and - * botline - p_so (approximation of how much will be - * scrolled). */ - for (linenr_T lnum = curwin->w_cursor.lnum; - lnum >= curwin->w_botline - *so_ptr; lnum--) { + if (hasAnyFolding(wp)) { + // Count the number of logical lines between the cursor and + // botline - p_so (approximation of how much will be + // scrolled). + for (linenr_T lnum = wp->w_cursor.lnum; + lnum >= wp->w_botline - *so_ptr; lnum--) { line_count++; // stop at end of file or when we know we are far off - if (lnum <= 0 || line_count > curwin->w_height_inner + 1) { + if (lnum <= 0 || line_count > wp->w_height_inner + 1) { break; } (void)hasFolding(lnum, &lnum, NULL); } - } else - line_count = curwin->w_cursor.lnum - curwin->w_botline - + 1 + *so_ptr; - if (line_count <= curwin->w_height_inner + 1) { + } else { + line_count = wp->w_cursor.lnum - wp->w_botline + 1 + *so_ptr; + } + if (line_count <= wp->w_height_inner + 1) { scroll_cursor_bot(scrolljump_value(), false); } else { scroll_cursor_halfway(false); @@ -313,25 +315,25 @@ void update_topline(void) } } } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; - win_check_anchored_floats(curwin); + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; + win_check_anchored_floats(wp); /* * Need to redraw when topline changed. */ - if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill + if (wp->w_topline != old_topline + || wp->w_topfill != old_topfill ) { dollar_vcol = -1; - if (curwin->w_skipcol != 0) { - curwin->w_skipcol = 0; - redraw_later(curwin, NOT_VALID); + if (wp->w_skipcol != 0) { + wp->w_skipcol = 0; + redraw_later(wp, NOT_VALID); } else { - redraw_later(curwin, VALID); + redraw_later(wp, VALID); } // May need to set w_skipcol when cursor in w_topline. - if (curwin->w_cursor.lnum == curwin->w_topline) { + if (wp->w_cursor.lnum == wp->w_topline) { validate_cursor(); } } @@ -346,7 +348,7 @@ void update_topline_win(win_T* win) { win_T *save_curwin; switch_win(&save_curwin, NULL, win, NULL, true); - update_topline(); + update_topline(curwin); restore_win(save_curwin, NULL, true); } @@ -368,7 +370,7 @@ static int scrolljump_value(void) */ static bool check_top_offset(void) { - long so = get_scrolloff_value(); + long so = get_scrolloff_value(curwin); if (curwin->w_cursor.lnum < curwin->w_topline + so || hasAnyFolding(curwin) ) { @@ -378,7 +380,7 @@ static bool check_top_offset(void) int n = curwin->w_topfill; // always have this context // Count the visible screen lines above the cursor line. while (n < so) { - topline_back(&loff); + topline_back(curwin, &loff); // Stop when included a line above the window. if (loff.lnum < curwin->w_topline || (loff.lnum == curwin->w_topline && loff.fill > 0) @@ -498,10 +500,11 @@ void changed_line_abv_curs_win(win_T *wp) /* * Make sure the value of curwin->w_botline is valid. */ -void validate_botline(void) +void validate_botline(win_T *wp) { - if (!(curwin->w_valid & VALID_BOTLINE)) - comp_botline(curwin); + if (!(wp->w_valid & VALID_BOTLINE)) { + comp_botline(wp); + } } /* @@ -539,8 +542,9 @@ int cursor_valid(void) void validate_cursor(void) { check_cursor_moved(curwin); - if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) - curs_columns(true); + if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { + curs_columns(curwin, true); + } } /* @@ -720,8 +724,10 @@ int curwin_col_off2(void) // Compute curwin->w_wcol and curwin->w_virtcol. // Also updates curwin->w_wrow and curwin->w_cline_row. // Also updates curwin->w_leftcol. +// @param may_scroll when true, may scroll horizontally void curs_columns( - int may_scroll /* when true, may scroll horizontally */ + win_T *wp, + int may_scroll ) { int n; @@ -729,136 +735,136 @@ void curs_columns( colnr_T startcol; colnr_T endcol; colnr_T prev_skipcol; - long so = get_scrolloff_value(); - long siso = get_sidescrolloff_value(); + long so = get_scrolloff_value(wp); + long siso = get_sidescrolloff_value(wp); /* * First make sure that w_topline is valid (after moving the cursor). */ - update_topline(); + update_topline(wp); // Next make sure that w_cline_row is valid. - if (!(curwin->w_valid & VALID_CROW)) { - curs_rows(curwin); + if (!(wp->w_valid & VALID_CROW)) { + curs_rows(wp); } /* * Compute the number of virtual columns. */ - if (curwin->w_cline_folded) - /* In a folded line the cursor is always in the first column */ - startcol = curwin->w_virtcol = endcol = curwin->w_leftcol; - else - getvvcol(curwin, &curwin->w_cursor, - &startcol, &(curwin->w_virtcol), &endcol); + if (wp->w_cline_folded) { + // In a folded line the cursor is always in the first column + startcol = wp->w_virtcol = endcol = wp->w_leftcol; + } else { + getvvcol(wp, &wp->w_cursor, &startcol, &(wp->w_virtcol), &endcol); + } /* remove '$' from change command when cursor moves onto it */ if (startcol > dollar_vcol) dollar_vcol = -1; - int extra = curwin_col_off(); - curwin->w_wcol = curwin->w_virtcol + extra; + int extra = win_col_off(wp); + wp->w_wcol = wp->w_virtcol + extra; endcol += extra; - /* - * Now compute w_wrow, counting screen lines from w_cline_row. - */ - curwin->w_wrow = curwin->w_cline_row; + // Now compute w_wrow, counting screen lines from w_cline_row. + wp->w_wrow = wp->w_cline_row; - int textwidth = curwin->w_width_inner - extra; + int textwidth = wp->w_width_inner - extra; if (textwidth <= 0) { // No room for text, put cursor in last char of window. - curwin->w_wcol = curwin->w_width_inner - 1; - curwin->w_wrow = curwin->w_height_inner - 1; - } else if (curwin->w_p_wrap - && curwin->w_width_inner != 0 + wp->w_wcol = wp->w_width_inner - 1; + wp->w_wrow = wp->w_height_inner - 1; + } else if (wp->w_p_wrap + && wp->w_width_inner != 0 ) { - width = textwidth + curwin_col_off2(); + width = textwidth + win_col_off2(wp); - // long line wrapping, adjust curwin->w_wrow - if (curwin->w_wcol >= curwin->w_width_inner) { + // long line wrapping, adjust wp->w_wrow + if (wp->w_wcol >= wp->w_width_inner) { // this same formula is used in validate_cursor_col() - n = (curwin->w_wcol - curwin->w_width_inner) / width + 1; - curwin->w_wcol -= n * width; - curwin->w_wrow += n; + n = (wp->w_wcol - wp->w_width_inner) / width + 1; + wp->w_wcol -= n * width; + wp->w_wrow += n; /* When cursor wraps to first char of next line in Insert * mode, the 'showbreak' string isn't shown, backup to first * column */ if (*p_sbr && *get_cursor_pos_ptr() == NUL - && curwin->w_wcol == (int)vim_strsize(p_sbr)) - curwin->w_wcol = 0; + && wp->w_wcol == (int)vim_strsize(p_sbr)) { + wp->w_wcol = 0; + } } - } - /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line - * is not folded. - * If scrolling is off, curwin->w_leftcol is assumed to be 0 */ - else if (may_scroll - && !curwin->w_cline_folded - ) { - /* - * If Cursor is left of the screen, scroll rightwards. - * If Cursor is right of the screen, scroll leftwards - * If we get closer to the edge than 'sidescrolloff', scroll a little - * extra - */ + } else if (may_scroll + && !wp->w_cline_folded + ) { + // No line wrapping: compute wp->w_leftcol if scrolling is on and line + // is not folded. + // If scrolling is off, wp->w_leftcol is assumed to be 0 + + // If Cursor is left of the screen, scroll rightwards. + // If Cursor is right of the screen, scroll leftwards + // If we get closer to the edge than 'sidescrolloff', scroll a little + // extra assert(siso <= INT_MAX); - int off_left = startcol - curwin->w_leftcol - (int)siso; + int off_left = startcol - wp->w_leftcol - (int)siso; int off_right = - endcol - curwin->w_leftcol - curwin->w_width_inner + (int)siso + 1; + endcol - wp->w_leftcol - wp->w_width_inner + (int)siso + 1; if (off_left < 0 || off_right > 0) { int diff = (off_left < 0) ? -off_left: off_right; /* When far off or not enough room on either side, put cursor in * middle of window. */ int new_leftcol; - if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) - new_leftcol = curwin->w_wcol - extra - textwidth / 2; - else { + if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) { + new_leftcol = wp->w_wcol - extra - textwidth / 2; + } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); diff = (int)p_ss; } - if (off_left < 0) - new_leftcol = curwin->w_leftcol - diff; - else - new_leftcol = curwin->w_leftcol + diff; + if (off_left < 0) { + new_leftcol = wp->w_leftcol - diff; + } else { + new_leftcol = wp->w_leftcol + diff; + } } if (new_leftcol < 0) new_leftcol = 0; - if (new_leftcol != (int)curwin->w_leftcol) { - curwin->w_leftcol = new_leftcol; - win_check_anchored_floats(curwin); - // screen has to be redrawn with new curwin->w_leftcol - redraw_later(curwin, NOT_VALID); + if (new_leftcol != (int)wp->w_leftcol) { + wp->w_leftcol = new_leftcol; + win_check_anchored_floats(wp); + // screen has to be redrawn with new wp->w_leftcol + redraw_later(wp, NOT_VALID); } } - curwin->w_wcol -= curwin->w_leftcol; - } else if (curwin->w_wcol > (int)curwin->w_leftcol) - curwin->w_wcol -= curwin->w_leftcol; - else - curwin->w_wcol = 0; + wp->w_wcol -= wp->w_leftcol; + } else if (wp->w_wcol > (int)wp->w_leftcol) { + wp->w_wcol -= wp->w_leftcol; + } else { + wp->w_wcol = 0; + } /* Skip over filler lines. At the top use w_topfill, there * may be some filler lines above the window. */ - if (curwin->w_cursor.lnum == curwin->w_topline) - curwin->w_wrow += curwin->w_topfill; - else - curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum); + if (wp->w_cursor.lnum == wp->w_topline) { + wp->w_wrow += wp->w_topfill; + } else { + wp->w_wrow += diff_check_fill(wp, wp->w_cursor.lnum); + } - prev_skipcol = curwin->w_skipcol; + prev_skipcol = wp->w_skipcol; int plines = 0; - if ((curwin->w_wrow >= curwin->w_height_inner + if ((wp->w_wrow >= wp->w_height_inner || ((prev_skipcol > 0 - || curwin->w_wrow + so >= curwin->w_height_inner) + || wp->w_wrow + so >= wp->w_height_inner) && (plines = - plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1 - >= curwin->w_height_inner)) - && curwin->w_height_inner != 0 - && curwin->w_cursor.lnum == curwin->w_topline + plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1 + >= wp->w_height_inner)) + && wp->w_height_inner != 0 + && wp->w_cursor.lnum == wp->w_topline && width > 0 - && curwin->w_width_inner != 0 + && wp->w_width_inner != 0 ) { /* Cursor past end of screen. Happens with a single line that does * not fit on screen. Find a skipcol to show the text around the @@ -867,87 +873,88 @@ void curs_columns( * 2: Less than "p_so" lines below * 3: both of them */ extra = 0; - if (curwin->w_skipcol + so * width > curwin->w_virtcol) { + if (wp->w_skipcol + so * width > wp->w_virtcol) { extra = 1; } // Compute last display line of the buffer line that we want at the // bottom of the window. if (plines == 0) { - plines = plines_win(curwin, curwin->w_cursor.lnum, false); + plines = plines_win(wp, wp->w_cursor.lnum, false); } plines--; - if (plines > curwin->w_wrow + so) { + if (plines > wp->w_wrow + so) { assert(so <= INT_MAX); - n = curwin->w_wrow + (int)so; + n = wp->w_wrow + (int)so; } else { n = plines; } - if ((colnr_T)n >= curwin->w_height_inner + curwin->w_skipcol / width) { + if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width) { extra += 2; } if (extra == 3 || plines < so * 2) { // not enough room for 'scrolloff', put cursor in the middle - n = curwin->w_virtcol / width; - if (n > curwin->w_height_inner / 2) { - n -= curwin->w_height_inner / 2; + n = wp->w_virtcol / width; + if (n > wp->w_height_inner / 2) { + n -= wp->w_height_inner / 2; } else { n = 0; } // don't skip more than necessary - if (n > plines - curwin->w_height_inner + 1) { - n = plines - curwin->w_height_inner + 1; + if (n > plines - wp->w_height_inner + 1) { + n = plines - wp->w_height_inner + 1; } - curwin->w_skipcol = n * width; + wp->w_skipcol = n * width; } else if (extra == 1) { // less then 'scrolloff' lines above, decrease skipcol assert(so <= INT_MAX); - extra = (curwin->w_skipcol + (int)so * width - curwin->w_virtcol + extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol + width - 1) / width; if (extra > 0) { - if ((colnr_T)(extra * width) > curwin->w_skipcol) - extra = curwin->w_skipcol / width; - curwin->w_skipcol -= extra * width; + if ((colnr_T)(extra * width) > wp->w_skipcol) { + extra = wp->w_skipcol / width; + } + wp->w_skipcol -= extra * width; } } else if (extra == 2) { // less then 'scrolloff' lines below, increase skipcol - endcol = (n - curwin->w_height_inner + 1) * width; - while (endcol > curwin->w_virtcol) { + endcol = (n - wp->w_height_inner + 1) * width; + while (endcol > wp->w_virtcol) { endcol -= width; } - if (endcol > curwin->w_skipcol) { - curwin->w_skipcol = endcol; + if (endcol > wp->w_skipcol) { + wp->w_skipcol = endcol; } } - curwin->w_wrow -= curwin->w_skipcol / width; - if (curwin->w_wrow >= curwin->w_height_inner) { + wp->w_wrow -= wp->w_skipcol / width; + if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it - extra = curwin->w_wrow - curwin->w_height_inner + 1; - curwin->w_skipcol += extra * width; - curwin->w_wrow -= extra; + extra = wp->w_wrow - wp->w_height_inner + 1; + wp->w_skipcol += extra * width; + wp->w_wrow -= extra; } // extra could be either positive or negative - extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width; - win_scroll_lines(curwin, 0, extra); + extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width; + win_scroll_lines(wp, 0, extra); } else { - curwin->w_skipcol = 0; + wp->w_skipcol = 0; } - if (prev_skipcol != curwin->w_skipcol) { - redraw_later(curwin, NOT_VALID); + if (prev_skipcol != wp->w_skipcol) { + redraw_later(wp, NOT_VALID); } - /* Redraw when w_virtcol changes and 'cursorcolumn' is set */ - if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0 + // Redraw when w_virtcol changes and 'cursorcolumn' is set + if (wp->w_p_cuc && (wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - redraw_later(curwin, SOME_VALID); + redraw_later(wp, SOME_VALID); } // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise - curwin->w_valid_leftcol = curwin->w_leftcol; + wp->w_valid_leftcol = wp->w_leftcol; - curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; + wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } /// Compute the screen position of text character at "pos" in window "wp" @@ -1231,7 +1238,7 @@ void scrolldown_clamp(void) end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / curwin->w_width_inner; } - if (end_row < curwin->w_height_inner - get_scrolloff_value()) { + if (end_row < curwin->w_height_inner - get_scrolloff_value(curwin)) { if (can_fill) { ++curwin->w_topfill; check_topfill(curwin, true); @@ -1271,7 +1278,7 @@ void scrollup_clamp(void) validate_virtcol(); start_row -= curwin->w_virtcol / curwin->w_width_inner; } - if (start_row >= get_scrolloff_value()) { + if (start_row >= get_scrolloff_value(curwin)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { @@ -1289,22 +1296,22 @@ void scrollup_clamp(void) * Returns the height of the added line in "lp->height". * Lines above the first one are incredibly high: MAXCOL. */ -static void topline_back(lineoff_T *lp) +static void topline_back(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(curwin, lp->lnum)) { - /* Add a filler line. */ - ++lp->fill; + if (lp->fill < diff_check_fill(wp, lp->lnum)) { + // Add a filler line + lp->fill++; lp->height = 1; } else { --lp->lnum; lp->fill = 0; - if (lp->lnum < 1) + if (lp->lnum < 1) { lp->height = MAXCOL; - else if (hasFolding(lp->lnum, &lp->lnum, NULL)) - /* Add a closed fold */ + } else if (hasFolding(lp->lnum, &lp->lnum, NULL)) { + // Add a closed fold lp->height = 1; - else { - lp->height = plines_nofill(lp->lnum); + } else { + lp->height = plines_win_nofill(wp, lp->lnum, true); } } } @@ -1315,22 +1322,23 @@ static void topline_back(lineoff_T *lp) * Returns the height of the added line in "lp->height". * Lines below the last one are incredibly high. */ -static void botline_forw(lineoff_T *lp) +static void botline_forw(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(curwin, lp->lnum + 1)) { - /* Add a filler line. */ - ++lp->fill; + if (lp->fill < diff_check_fill(wp, lp->lnum + 1)) { + // Add a filler line. + lp->fill++; lp->height = 1; } else { ++lp->lnum; lp->fill = 0; - if (lp->lnum > curbuf->b_ml.ml_line_count) { + assert(wp->w_buffer != 0); + if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) { lp->height = MAXCOL; - } else if (hasFolding(lp->lnum, NULL, &lp->lnum)) { + } else if (hasFoldingWin(wp, lp->lnum, NULL, &lp->lnum, true, NULL)) { // Add a closed fold lp->height = 1; } else { - lp->height = plines_nofill(lp->lnum); + lp->height = plines_win_nofill(wp, lp->lnum, true); } } } @@ -1374,7 +1382,7 @@ void scroll_cursor_top(int min_scroll, int always) linenr_T old_topline = curwin->w_topline; linenr_T old_topfill = curwin->w_topfill; linenr_T new_topline; - int off = (int)get_scrolloff_value(); + int off = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) off = mouse_dragging - 1; @@ -1518,7 +1526,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) int old_valid = curwin->w_valid; int old_empty_rows = curwin->w_empty_rows; linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - long so = get_scrolloff_value(); + long so = get_scrolloff_value(curwin); if (set_topbot) { used = 0; @@ -1528,7 +1536,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) curwin->w_topline > 1; curwin->w_topline = loff.lnum) { loff.lnum = curwin->w_topline; - topline_back(&loff); + topline_back(curwin, &loff); if (loff.height == MAXCOL || used + loff.height > curwin->w_height_inner) { break; @@ -1542,8 +1550,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) || curwin->w_topfill != old_topfill ) curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } else - validate_botline(); + } else { + validate_botline(curwin); + } /* The lines of the cursor line itself are always used. */ used = plines_nofill(cln); @@ -1586,8 +1595,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) break; } - /* Add one line above */ - topline_back(&loff); + // Add one line above + topline_back(curwin, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { @@ -1609,8 +1618,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } if (boff.lnum < curbuf->b_ml.ml_line_count) { - /* Add one line below */ - botline_forw(&boff); + // Add one line below + botline_forw(curwin, &boff); used += boff.height; if (used > curwin->w_height_inner) { break; @@ -1647,7 +1656,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) boff.lnum = curwin->w_topline - 1; int i; for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) { - botline_forw(&boff); + botline_forw(curwin, &boff); i += boff.height; ++line_count; } @@ -1701,7 +1710,7 @@ void scroll_cursor_halfway(int atend) while (topline > 1) { if (below <= above) { /* add a line below the cursor first */ if (boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(&boff); + botline_forw(curwin, &boff); used += boff.height; if (used > curwin->w_height_inner) { break; @@ -1714,12 +1723,13 @@ void scroll_cursor_halfway(int atend) } } - if (below > above) { /* add a line above the cursor */ - topline_back(&loff); - if (loff.height == MAXCOL) + if (below > above) { // add a line above the cursor + topline_back(curwin, &loff); + if (loff.height == MAXCOL) { used = MAXCOL; - else + } else { used += loff.height; + } if (used > curwin->w_height_inner) { break; } @@ -1751,8 +1761,8 @@ void cursor_correct(void) * How many lines we would like to have above/below the cursor depends on * whether the first/last line of the file is on screen. */ - int above_wanted = (int)get_scrolloff_value(); - int below_wanted = (int)get_scrolloff_value(); + int above_wanted = (int)get_scrolloff_value(curwin); + int below_wanted = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) { above_wanted = mouse_dragging - 1; below_wanted = mouse_dragging - 1; @@ -1764,7 +1774,7 @@ void cursor_correct(void) below_wanted = max_off; } } - validate_botline(); + validate_botline(curwin); if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 && mouse_dragging == 0) { below_wanted = 0; @@ -1848,21 +1858,19 @@ int onepage(Direction dir, long count) int retval = OK; lineoff_T loff; linenr_T old_topline = curwin->w_topline; - long so = get_scrolloff_value(); + long so = get_scrolloff_value(curwin); if (curbuf->b_ml.ml_line_count == 1) { /* nothing to do */ beep_flush(); return FAIL; } - for (; count > 0; --count) { - validate_botline(); - /* - * It's an error to move a page up when the first line is already on - * the screen. It's an error to move a page down when the last line - * is on the screen and the topline is 'scrolloff' lines from the - * last line. - */ + for (; count > 0; count--) { + validate_botline(curwin); + // It's an error to move a page up when the first line is already on + // the screen. It's an error to move a page down when the last line + // is on the screen and the topline is 'scrolloff' lines from the + // last line. if (dir == FORWARD ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) && curwin->w_botline > curbuf->b_ml.ml_line_count) @@ -1945,11 +1953,12 @@ int onepage(Direction dir, long count) * at the bottom of the window. */ n = 0; while (n <= curwin->w_height_inner && loff.lnum >= 1) { - topline_back(&loff); - if (loff.height == MAXCOL) + topline_back(curwin, &loff); + if (loff.height == MAXCOL) { n = MAXCOL; - else + } else { n += loff.height; + } } if (loff.lnum < 1) { /* at begin of file */ curwin->w_topline = 1; @@ -1958,11 +1967,11 @@ int onepage(Direction dir, long count) } else { /* Go two lines forward again. */ topline_botline(&loff); - botline_forw(&loff); - botline_forw(&loff); + botline_forw(curwin, &loff); + botline_forw(curwin, &loff); botline_topline(&loff); - /* We're at the wrong end of a fold now. */ - (void)hasFolding(loff.lnum, &loff.lnum, NULL); + // We're at the wrong end of a fold now. + (void)hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); /* Always scroll at least one line. Avoid getting stuck on * very long lines. */ @@ -2046,10 +2055,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) return; /* no overlap */ lineoff_T loff0 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h2 = lp->height; if (h2 == MAXCOL || h2 + h1 > min_height) { *lp = loff0; /* no overlap */ @@ -2057,10 +2067,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) } lineoff_T loff1 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h3 = lp->height; if (h3 == MAXCOL || h3 + h2 > min_height) { *lp = loff0; /* no overlap */ @@ -2068,10 +2079,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) } lineoff_T loff2 = *lp; - if (dir > 0) - botline_forw(lp); - else - topline_back(lp); + if (dir > 0) { + botline_forw(curwin, lp); + } else { + topline_back(curwin, lp); + } int h4 = lp->height; if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) *lp = loff1; /* 1 line overlap */ @@ -2094,8 +2106,8 @@ void halfpage(bool flag, linenr_T Prenum) int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr : curwin->w_height_inner; - update_topline(); - validate_botline(); + update_topline(curwin); + validate_botline(curwin); int room = curwin->w_empty_rows + curwin->w_filler_rows; if (flag) { /* @@ -2255,7 +2267,7 @@ void do_check_cursorbind(void) // Only scroll when 'scrollbind' hasn't done this. if (!curwin->w_p_scb) { - update_topline(); + update_topline(curwin); } curwin->w_redr_status = true; } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 68ef4cd41e..a0b439ac45 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -377,6 +377,10 @@ static void request_event(void **argv) Channel *channel = e->channel; MsgpackRpcRequestHandler handler = e->handler; Error error = ERROR_INIT; + if (channel->rpc.closed) { + // channel was closed, abort any pending requests + goto free_ret; + } Object result = handler.fn(channel->id, e->args, &error); if (e->type == kMessageTypeRequest || ERROR_SET(&error)) { // Send the response. @@ -391,6 +395,8 @@ static void request_event(void **argv) } else { api_free_object(result); } + +free_ret: api_free_array(e->args); channel_decref(channel); xfree(e); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0293bb4a73..4e955667dc 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -168,7 +168,7 @@ static const struct nv_cmd { { NL, nv_down, 0, false }, { Ctrl_K, nv_error, 0, 0 }, { Ctrl_L, nv_clear, 0, 0 }, - { Ctrl_M, nv_down, 0, true }, + { CAR, nv_down, 0, true }, { Ctrl_N, nv_down, NV_STS, false }, { Ctrl_O, nv_ctrlo, 0, 0 }, { Ctrl_P, nv_up, NV_STS, false }, @@ -1261,7 +1261,7 @@ static void normal_redraw(NormalState *s) { // Before redrawing, make sure w_topline is correct, and w_leftcol // if lines don't wrap, and w_skipcol if lines wrap. - update_topline(); + update_topline(curwin); validate_cursor(); // If the cursor moves horizontally when 'concealcursor' is active, then the @@ -1341,7 +1341,7 @@ static int normal_check(VimState *state) } else if (do_redraw || stuff_empty()) { // Need to make sure w_topline and w_leftcol are correct before // normal_check_window_scrolled() is called. - update_topline(); + update_topline(curwin); normal_check_cursor_moved(s); normal_check_text_changed(s); @@ -2375,10 +2375,10 @@ do_mouse ( * Also paste at the cursor if the current mode isn't in 'mouse' (only * happens for the GUI). */ - if ((State & INSERT) || !mouse_has(MOUSE_NORMAL)) { - if (regname == '.') + if ((State & INSERT)) { + if (regname == '.') { insert_reg(regname, true); - else { + } else { if (regname == 0 && eval_has_provider("clipboard")) { regname = '*'; } @@ -2558,8 +2558,9 @@ do_mouse ( * on a status line */ if (VIsual_active) jump_flags |= MOUSE_MAY_STOP_VIS; - } else if (mouse_has(MOUSE_VISUAL)) + } else { jump_flags |= MOUSE_MAY_VIS; + } } else if (which_button == MOUSE_RIGHT) { if (is_click && VIsual_active) { /* @@ -2575,8 +2576,7 @@ do_mouse ( } } jump_flags |= MOUSE_FOCUS; - if (mouse_has(MOUSE_VISUAL)) - jump_flags |= MOUSE_MAY_VIS; + jump_flags |= MOUSE_MAY_VIS; } } @@ -2629,7 +2629,7 @@ do_mouse ( /* Set global flag that we are extending the Visual area with mouse * dragging; temporarily minimize 'scrolloff'. */ - if (VIsual_active && is_drag && get_scrolloff_value()) { + if (VIsual_active && is_drag && get_scrolloff_value(curwin)) { // In the very first line, allow scrolling one line if (mouse_row == 0) { mouse_dragging = 2; @@ -2790,8 +2790,7 @@ do_mouse ( /* Handle double clicks, unless on status line */ else if (in_status_line) { } else if (in_sep_line) { - } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)) - && mouse_has(MOUSE_VISUAL)) { + } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))) { if (is_click || !VIsual_active) { if (VIsual_active) { orig_cursor = VIsual; @@ -3052,57 +3051,57 @@ static bool find_is_eval_item( return false; } -/* - * Find the identifier under or to the right of the cursor. - * "find_type" can have one of three values: - * FIND_IDENT: find an identifier (keyword) - * FIND_STRING: find any non-white string - * FIND_IDENT + FIND_STRING: find any non-white string, identifier preferred. - * FIND_EVAL: find text useful for C program debugging - * - * There are three steps: - * 1. Search forward for the start of an identifier/string. Doesn't move if - * already on one. - * 2. Search backward for the start of this identifier/string. - * This doesn't match the real Vi but I like it a little better and it - * shouldn't bother anyone. - * 3. Search forward to the end of this identifier/string. - * When FIND_IDENT isn't defined, we backup until a blank. - * - * Returns the length of the string, or zero if no string is found. - * If a string is found, a pointer to the string is put in "*string". This - * string is not always NUL terminated. - */ -size_t find_ident_under_cursor(char_u **string, int find_type) +// Find the identifier under or to the right of the cursor. +// "find_type" can have one of three values: +// FIND_IDENT: find an identifier (keyword) +// FIND_STRING: find any non-white text +// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred. +// FIND_EVAL: find text useful for C program debugging +// +// There are three steps: +// 1. Search forward for the start of an identifier/text. Doesn't move if +// already on one. +// 2. Search backward for the start of this identifier/text. +// This doesn't match the real Vi but I like it a little better and it +// shouldn't bother anyone. +// 3. Search forward to the end of this identifier/text. +// When FIND_IDENT isn't defined, we backup until a blank. +// +// Returns the length of the text, or zero if no text is found. +// If text is found, a pointer to the text is put in "*text". This +// points into the current buffer line and is not always NUL terminated. +size_t find_ident_under_cursor(char_u **text, int find_type) + FUNC_ATTR_NONNULL_ARG(1) { return find_ident_at_pos(curwin, curwin->w_cursor.lnum, - curwin->w_cursor.col, string, find_type); + curwin->w_cursor.col, text, NULL, find_type); } /* * Like find_ident_under_cursor(), but for any window and any position. * However: Uses 'iskeyword' from the current window!. */ -size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, - char_u **string, int find_type) -{ - char_u *ptr; - int col = 0; /* init to shut up GCC */ +size_t find_ident_at_pos( + win_T *wp, + linenr_T lnum, + colnr_T startcol, + char_u **text, + int *textcol, // column where "text" starts, can be NULL + int find_type) + FUNC_ATTR_NONNULL_ARG(1, 4) +{ + int col = 0; // init to shut up GCC int i; int this_class = 0; int prev_class; int prevcol; int bn = 0; // bracket nesting - /* - * if i == 0: try to find an identifier - * if i == 1: try to find any non-white string - */ - ptr = ml_get_buf(wp->w_buffer, lnum, false); - for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i) { - /* - * 1. skip to start of identifier/string - */ + // if i == 0: try to find an identifier + // if i == 1: try to find any non-white text + char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false); + for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) { + // 1. skip to start of identifier/text col = startcol; while (ptr[col] != NUL) { // Stop at a ']' to evaluate "a[x]". @@ -3120,7 +3119,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, bn = ptr[col] == ']'; // - // 2. Back up to start of identifier/string. + // 2. Back up to start of identifier/text. // // Remember class of character under cursor. if ((find_type & FIND_EVAL) && ptr[col] == ']') { @@ -3143,7 +3142,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, col = prevcol; } - // If we don't want just any old string, or we've found an + // If we don't want just any old text, or we've found an // identifier, stop searching. if (this_class > 2) { this_class = 2; @@ -3154,7 +3153,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, } if (ptr[col] == NUL || (i == 0 && this_class != 2)) { - // Didn't find an identifier or string. + // Didn't find an identifier or text. if (find_type & FIND_STRING) { EMSG(_("E348: No string under cursor")); } else { @@ -3163,11 +3162,12 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, return 0; } ptr += col; - *string = ptr; + *text = ptr; + if (textcol != NULL) { + *textcol = col; + } - /* - * 3. Find the end if the identifier/string. - */ + // 3. Find the end if the identifier/text. bn = 0; startcol -= col; col = 0; @@ -4135,7 +4135,7 @@ void scroll_redraw(int up, long count) scrollup(count, true) : scrolldown(count, true); - if (get_scrolloff_value()) { + if (get_scrolloff_value(curwin)) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. cursor_correct(); @@ -4185,7 +4185,7 @@ static void nv_zet(cmdarg_T *cap) int old_fen = curwin->w_p_fen; bool undo = false; - int l_p_siso = (int)get_sidescrolloff_value(); + int l_p_siso = (int)get_sidescrolloff_value(curwin); assert(l_p_siso <= INT_MAX); if (ascii_isdigit(nchar)) { @@ -4253,12 +4253,13 @@ dozet: /* "z+", "z<CR>" and "zt": put cursor at top of screen */ case '+': if (cap->count0 == 0) { - /* No count given: put cursor at the line below screen */ - validate_botline(); /* make sure w_botline is valid */ - if (curwin->w_botline > curbuf->b_ml.ml_line_count) + // No count given: put cursor at the line below screen + validate_botline(curwin); // make sure w_botline is valid + if (curwin->w_botline > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_cursor.lnum = curwin->w_botline; + } } FALLTHROUGH; case NL: @@ -5053,7 +5054,7 @@ static void nv_scroll(cmdarg_T *cap) setpcmark(); if (cap->cmdchar == 'L') { - validate_botline(); /* make sure curwin->w_botline is valid */ + validate_botline(curwin); // make sure curwin->w_botline is valid curwin->w_cursor.lnum = curwin->w_botline - 1; if (cap->count1 - 1 >= curwin->w_cursor.lnum) curwin->w_cursor.lnum = 1; @@ -5074,7 +5075,7 @@ static void nv_scroll(cmdarg_T *cap) /* Don't count filler lines above the window. */ used -= diff_check_fill(curwin, curwin->w_topline) - curwin->w_topfill; - validate_botline(); // make sure w_empty_rows is valid + validate_botline(curwin); // make sure w_empty_rows is valid half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { // Count half he number of filler lines to be "below this @@ -6653,16 +6654,15 @@ static void nv_g_cmd(cmdarg_T *cap) VIsual = curwin->w_cursor; curwin->w_cursor = tpos; check_cursor(); - update_topline(); - /* - * When called from normal "g" command: start Select mode when - * 'selectmode' contains "cmd". When called for K_SELECT, always - * start Select mode. - */ - if (cap->arg) + update_topline(curwin); + // When called from normal "g" command: start Select mode when + // 'selectmode' contains "cmd". When called for K_SELECT, always + // start Select mode. + if (cap->arg) { VIsual_select = true; - else + } else { may_start_select('c'); + } setmouse(); redraw_curbuf_later(INVERTED); showmode(); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 40dd5f0b5c..052b07ed44 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1197,7 +1197,13 @@ int insert_reg( retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped((const char *)reg->y_array[i], literally); + if (regname == '-') { + AppendCharToRedobuff(Ctrl_R); + AppendCharToRedobuff(regname); + do_put(regname, NULL, BACKWARD, 1L, PUT_CURSEND); + } else { + stuffescaped((const char *)reg->y_array[i], literally); + } // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1683,6 +1689,7 @@ int op_delete(oparg_T *oap) (int)oap->line_count-1, n, deleted_bytes, 0, 0, 0, kExtmarkUndo); } + auto_format(false, true); } msgmore(curbuf->b_ml.ml_line_count - old_lcount); @@ -3547,15 +3554,21 @@ void ex_display(exarg_T *eap) int name; char_u *arg = eap->arg; int clen; + char_u type[2]; if (arg != NULL && *arg == NUL) arg = NULL; int attr = HL_ATTR(HLF_8); - /* Highlight title */ - MSG_PUTS_TITLE(_("\n--- Registers ---")); + // Highlight title + msg_puts_title(_("\nType Name Content")); for (int i = -1; i < NUM_REGISTERS && !got_int; i++) { name = get_register_name(i); + switch (get_reg_type(name, NULL)) { + case kMTLineWise: type[0] = 'l'; break; + case kMTCharWise: type[0] = 'c'; break; + default: type[0] = 'b'; break; + } if (arg != NULL && vim_strchr(arg, name) == NULL) { continue; /* did not ask for this register */ @@ -3580,11 +3593,14 @@ void ex_display(exarg_T *eap) if (yb->y_array != NULL) { msg_putchar('\n'); + msg_puts(" "); + msg_putchar(type[0]); + msg_puts(" "); msg_putchar('"'); msg_putchar(name); MSG_PUTS(" "); - int n = Columns - 6; + int n = Columns - 11; for (size_t j = 0; j < yb->y_size && n > 1; j++) { if (j) { MSG_PUTS_ATTR("^J", attr); @@ -3609,8 +3625,8 @@ void ex_display(exarg_T *eap) */ if ((p = get_last_insert()) != NULL && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) { - MSG_PUTS("\n\". "); - dis_msg(p, TRUE); + msg_puts("\n c \". "); + dis_msg(p, true); } /* @@ -3618,8 +3634,8 @@ void ex_display(exarg_T *eap) */ if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL) && !got_int) { - MSG_PUTS("\n\": "); - dis_msg(last_cmdline, FALSE); + msg_puts("\n c \": "); + dis_msg(last_cmdline, false); } /* @@ -3627,8 +3643,8 @@ void ex_display(exarg_T *eap) */ if (curbuf->b_fname != NULL && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) { - MSG_PUTS("\n\"% "); - dis_msg(curbuf->b_fname, FALSE); + msg_puts("\n c \"% "); + dis_msg(curbuf->b_fname, false); } /* @@ -3639,8 +3655,8 @@ void ex_display(exarg_T *eap) linenr_T dummy; if (buflist_name_nr(0, &fname, &dummy) != FAIL) { - MSG_PUTS("\n\"# "); - dis_msg(fname, FALSE); + msg_puts("\n c \"# "); + dis_msg(fname, false); } } @@ -3649,8 +3665,8 @@ void ex_display(exarg_T *eap) */ if (last_search_pat() != NULL && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) { - MSG_PUTS("\n\"/ "); - dis_msg(last_search_pat(), FALSE); + msg_puts("\n c \"/ "); + dis_msg(last_search_pat(), false); } /* @@ -3658,8 +3674,8 @@ void ex_display(exarg_T *eap) */ if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL) && !got_int) { - MSG_PUTS("\n\"= "); - dis_msg(expr_line, FALSE); + msg_puts("\n c \"= "); + dis_msg(expr_line, false); } } @@ -3669,9 +3685,10 @@ void ex_display(exarg_T *eap) */ static void dis_msg( - char_u *p, - int skip_esc /* if TRUE, ignore trailing ESC */ + const char_u *p, + bool skip_esc // if true, ignore trailing ESC ) + FUNC_ATTR_NONNULL_ALL { int n; int l; diff --git a/src/nvim/option.c b/src/nvim/option.c index 0034117ddc..47b9e9bb07 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -206,7 +206,6 @@ typedef struct vimoption { // local option: indirect option index char_u *def_val[2]; // default values for variable (vi and vim) LastSet last_set; // script in which the option was last set -# define SCTX_INIT , { 0, 0, 0 } } vimoption_T; #define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value @@ -307,7 +306,7 @@ static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", "wipe", NULL }; -static char *(p_bs_values[]) = { "indent", "eol", "start", NULL }; +static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL }; static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", "syntax", "diff", NULL }; static char *(p_fcl_values[]) = { "all", NULL }; @@ -321,7 +320,7 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8", "yes:9", "number", NULL }; -static char *(p_fdc_values[]) = { "auto:1", "auto:2", +static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; @@ -352,9 +351,6 @@ void set_init_1(bool clean_arg) langmap_init(); - // Be nocompatible - p_cp = false; - /* * Find default value for 'shell' option. * Don't use it if it is empty. @@ -683,7 +679,7 @@ set_options_default( { for (int i = 0; options[i].fullname; i++) { if (!(options[i].flags & P_NODEFAULT)) { - set_option_default(i, opt_flags, p_cp); + set_option_default(i, opt_flags, false); } } @@ -763,7 +759,7 @@ void set_init_2(bool headless) // which results in the actual value computed from the window height. idx = findoption("scroll"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { - set_option_default(idx, OPT_LOCAL, p_cp); + set_option_default(idx, OPT_LOCAL, false); } comp_col(); @@ -1113,7 +1109,7 @@ int do_set( if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) { arg += len; - cp_val = p_cp; + cp_val = false; if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') { if (arg[3] == 'm') { // "opt&vim": set to Vim default cp_val = false; @@ -1370,6 +1366,10 @@ int do_set( *(char_u **)varp = vim_strsave( (char_u *)"indent,eol,start"); break; + case 3: + *(char_u **)varp = vim_strsave( + (char_u *)"indent,eol,nostop"); + break; } xfree(oldval); if (origval == oldval) { @@ -2943,7 +2943,7 @@ ambw_end: } } else if (varp == &p_bs) { // 'backspace' if (ascii_isdigit(*p_bs)) { - if (*p_bs >'2' || p_bs[1] != NUL) { + if (*p_bs > '3' || p_bs[1] != NUL) { errmsg = e_invarg; } } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) { @@ -3214,11 +3214,7 @@ ambw_end: } if (varp == &p_mouse) { - if (*p_mouse == NUL) { - ui_call_mouse_off(); - } else { - setmouse(); // in case 'mouse' changed - } + setmouse(); // in case 'mouse' changed } if (curwin->w_curswant != MAXCOL @@ -3617,10 +3613,14 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) { int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int indir = (int)options[opt_idx].indir; - const LastSet last_set = { .script_ctx = - { script_ctx.sc_sid, script_ctx.sc_seq, - script_ctx.sc_lnum + sourcing_lnum }, - current_channel_id }; + const LastSet last_set = { + .script_ctx = { + script_ctx.sc_sid, + script_ctx.sc_seq, + script_ctx.sc_lnum + sourcing_lnum + }, + current_channel_id + }; // Remember where the option was set. For local options need to do that // in the buffer or window structure. @@ -4338,6 +4338,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char buf_old[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", @@ -4950,7 +4951,7 @@ static int optval_default(vimoption_T *p, char_u *varp) if (varp == NULL) { return true; // hidden option is always at default } - dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT; + dvi = (p->flags & P_VI_DEF) ? VI_DEFAULT : VIM_DEFAULT; if (p->flags & P_NUM) { return *(long *)varp == (long)(intptr_t)p->def_val[dvi]; } @@ -4983,11 +4984,7 @@ void ui_refresh_options(void) ui_call_option_set(name, value); } if (p_mouse != NULL) { - if (*p_mouse == NUL) { - ui_call_mouse_off(); - } else { - setmouse(); - } + setmouse(); } } @@ -6808,15 +6805,15 @@ static int check_opt_wim(void) } /// Check if backspacing over something is allowed. -/// The parameter what is one of the following: whatBS_INDENT, BS_EOL -/// or BS_START +/// @param what BS_INDENT, BS_EOL, BS_START, or BS_NOSTOP bool can_bs(int what) { if (what == BS_START && bt_prompt(curbuf)) { return false; } switch (*p_bs) { - case '2': return true; + case '3': return true; + case '2': return what != BS_NOSTOP; case '1': return what != BS_START; case '0': return false; } @@ -7159,18 +7156,90 @@ dict_T *get_winbuf_options(const int bufopt) /// Return the effective 'scrolloff' value for the current window, using the /// global value when appropriate. -long get_scrolloff_value(void) +long get_scrolloff_value(win_T *wp) { // Disallow scrolloff in terminal-mode. #11915 if (State & TERM_FOCUS) { return 0; } - return curwin->w_p_so < 0 ? p_so : curwin->w_p_so; + return wp->w_p_so < 0 ? p_so : wp->w_p_so; } /// Return the effective 'sidescrolloff' value for the current window, using the /// global value when appropriate. -long get_sidescrolloff_value(void) +long get_sidescrolloff_value(win_T *wp) { - return curwin->w_p_siso < 0 ? p_siso : curwin->w_p_siso; + return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso; +} + +Dictionary get_vimoption(String name, Error *err) +{ + int opt_idx = findoption_len((const char *)name.data, name.size); + if (opt_idx < 0) { + api_set_error(err, kErrorTypeValidation, "no such option: '%s'", name.data); + return (Dictionary)ARRAY_DICT_INIT; + } + return vimoption2dict(&options[opt_idx]); +} + +Dictionary get_all_vimoptions(void) +{ + Dictionary retval = ARRAY_DICT_INIT; + for (size_t i = 0; i < PARAM_COUNT; i++) { + Dictionary opt_dict = vimoption2dict(&options[i]); + PUT(retval, options[i].fullname, DICTIONARY_OBJ(opt_dict)); + } + return retval; +} + +static Dictionary vimoption2dict(vimoption_T *opt) +{ + Dictionary dict = ARRAY_DICT_INIT; + + PUT(dict, "name", CSTR_TO_OBJ(opt->fullname)); + PUT(dict, "shortname", CSTR_TO_OBJ(opt->shortname)); + + const char *scope; + if (opt->indir & PV_BUF) { + scope = "buf"; + } else if (opt->indir & PV_WIN) { + scope = "win"; + } else { + scope = "global"; + } + + PUT(dict, "scope", CSTR_TO_OBJ(scope)); + + // welcome to the jungle + PUT(dict, "global_local", BOOL(opt->indir & PV_BOTH)); + PUT(dict, "commalist", BOOL(opt->flags & P_COMMA)); + PUT(dict, "flaglist", BOOL(opt->flags & P_FLAGLIST)); + + PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET)); + + PUT(dict, "last_set_sid", INTEGER_OBJ(opt->last_set.script_ctx.sc_sid)); + PUT(dict, "last_set_linenr", INTEGER_OBJ(opt->last_set.script_ctx.sc_lnum)); + PUT(dict, "last_set_chan", INTEGER_OBJ((int64_t)opt->last_set.channel_id)); + + const char *type; + Object def; + // TODO(bfredl): do you even nocp? + char_u *def_val = opt->def_val[(opt->flags & P_VI_DEF) + ? VI_DEFAULT : VIM_DEFAULT]; + if (opt->flags & P_STRING) { + type = "string"; + def = CSTR_TO_OBJ(def_val ? (char *)def_val : ""); + } else if (opt->flags & P_NUM) { + type = "number"; + def = INTEGER_OBJ((Integer)(intptr_t)def_val); + } else if (opt->flags & P_BOOL) { + type = "boolean"; + def = BOOL((intptr_t)def_val); + } else { + type = ""; def = NIL; + } + PUT(dict, "type", CSTR_TO_OBJ(type)); + PUT(dict, "default", def); + + return dict; } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index af0ea7f4a2..683afc670e 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -282,9 +282,14 @@ enum { #define WIM_BUFLASTUSED 8 // arguments for can_bs() +// each defined char should be unique over all values +// except for BS_START, that intentionally also matches BS_NOSTOP +// because BS_NOSTOP behaves exactly the same except it +// does not stop at the start of the insert point #define BS_INDENT 'i' // "Indent" -#define BS_EOL 'o' // "eOl" +#define BS_EOL 'l' // "eoL" #define BS_START 's' // "Start" +#define BS_NOSTOP 'p' // "nostoP #define LISPWORD_VALUE \ "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" @@ -372,7 +377,6 @@ EXTERN long p_cwh; // 'cmdwinheight' EXTERN long p_ch; // 'cmdheight' EXTERN long p_columns; // 'columns' EXTERN int p_confirm; // 'confirm' -EXTERN int p_cp; // 'compatible' EXTERN char_u *p_cot; // 'completeopt' # ifdef BACKSLASH_IN_FILENAME EXTERN char_u *p_csl; // 'completeslash' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 64a09dc2b4..df2bfbce34 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2313,6 +2313,7 @@ return { deny_duplicates=true, vi_def=true, secure=true, + expand=true, varname='p_shadafile', defaults={if_true={vi=""}} }, diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 879266e3d4..008f5ef63b 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -394,13 +394,21 @@ void os_get_hostname(char *hostname, size_t size) } /// To get the "real" home directory: -/// - get value of $HOME +/// 1. get value of $HOME +/// 2. if $HOME is not set, try the following +/// For Windows: +/// 1. assemble homedir using HOMEDRIVE and HOMEPATH +/// 2. try os_homedir() +/// 3. resolve a direct reference to another system variable +/// 4. guess C drive /// For Unix: -/// - go to that directory -/// - do os_dirname() to get the real name of that directory. -/// This also works with mounts and links. -/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 1. try os_homedir() +/// 2. go to that directory +/// This also works with mounts and links. +/// Don't do this for Windows, it will change the "current dir" for a drive. +/// 3. fall back to current working directory as a last resort static char *homedir = NULL; +static char *os_homedir(void); void init_homedir(void) { @@ -430,7 +438,7 @@ void init_homedir(void) } } if (var == NULL) { - var = os_getenv("USERPROFILE"); + var = os_homedir(); } // Weird but true: $HOME may contain an indirect reference to another @@ -440,6 +448,7 @@ void init_homedir(void) const char *p = strchr(var + 1, '%'); if (p != NULL) { vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1); + var = NULL; const char *exp = os_getenv(os_buf); if (exp != NULL && *exp != NUL && STRLEN(exp) + STRLEN(p) < MAXPATHL) { @@ -458,8 +467,12 @@ void init_homedir(void) } #endif - if (var != NULL) { #ifdef UNIX + if (var == NULL) { + var = os_homedir(); + } + + if (var != NULL) { // Change to the directory and get the actual path. This resolves // links. Don't do it when we can't return. if (os_dirname((char_u *)os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) { @@ -470,11 +483,37 @@ void init_homedir(void) EMSG(_(e_prev_dir)); } } + } + + // Fall back to current working directory if home is not found + if ((var == NULL || *var == NUL) + && os_dirname((char_u *)os_buf, sizeof(os_buf)) == OK) { + var = os_buf; + } #endif + if (var != NULL) { homedir = xstrdup(var); } } +static char homedir_buf[MAXPATHL]; + +static char *os_homedir(void) +{ + homedir_buf[0] = NUL; + size_t homedir_size = MAXPATHL; + uv_mutex_lock(&mutex); + // http://docs.libuv.org/en/v1.x/misc.html#c.uv_os_homedir + int ret_value = uv_os_homedir(homedir_buf, &homedir_size); + uv_mutex_unlock(&mutex); + if (ret_value == 0 && homedir_size < MAXPATHL) { + return homedir_buf; + } + ELOG("uv_os_homedir() failed %d: %s", ret_value, os_strerror(ret_value)); + homedir_buf[0] = NUL; + return NULL; +} + #if defined(EXITFREE) void free_homedir(void) diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index b7878d9da8..9d6518841a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -393,7 +393,7 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events) prof_inchar_enter(); } - if ((ms == - 1 || ms > 0) && events == NULL && !input_eof) { + if ((ms == - 1 || ms > 0) && events != main_loop.events && !input_eof) { // The pending input provoked a blocking wait. Do special events now. #6247 blocking = true; multiqueue_process_events(ch_before_blocking_events); diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 603191a0ff..b63faacaae 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -8,9 +8,12 @@ # undef Boolean #endif +#include "auto/config.h" + #ifdef HAVE_LOCALE_H # include <locale.h> #endif + #include "nvim/os/lang.h" #include "nvim/os/os.h" diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 9374693550..16e17a9c60 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -5,6 +5,8 @@ #include <uv.h> +#include "auto/config.h" + #include "nvim/ascii.h" #include "nvim/os/os.h" #include "nvim/garray.h" diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index f242b7d71a..aef7ffa397 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -742,6 +742,7 @@ static int pum_set_selected(int n, int repeat) // Edit a new, empty buffer. Set options for a "wipeout" // buffer. set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bl", 0L, NULL, OPT_LOCAL); set_option_value("bt", 0L, "nofile", OPT_LOCAL); set_option_value("bh", 0L, "wipe", OPT_LOCAL); set_option_value("diff", 0L, NULL, OPT_LOCAL); @@ -808,7 +809,7 @@ static int pum_set_selected(int n, int repeat) no_u_sync++; win_enter(curwin_save, true); no_u_sync--; - update_topline(); + update_topline(curwin); } // Update the screen before drawing the popup menu. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7fdc998e20..a625c09f78 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -143,6 +143,7 @@ struct efm_S { // 'E' error message // 'W' warning message // 'I' informational message + // 'N' note message // 'C' continuation line // 'Z' end of multi-line message // 'G' general, unspecific message @@ -457,7 +458,7 @@ static const char_u *efm_analyze_prefix(const char_u *efmp, efm_T *efminfo, if (vim_strchr((char_u *)"+-", *efmp) != NULL) { efminfo->flags = *efmp++; } - if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) { + if (vim_strchr((char_u *)"DXAEWINCZGOPQ", *efmp) != NULL) { efminfo->prefix = *efmp; } else { snprintf((char *)errmsg, errmsglen, @@ -967,7 +968,7 @@ restofline: fmt_start = fmt_ptr; } - if (vim_strchr((char_u *)"AEWI", idx) != NULL) { + if (vim_strchr((char_u *)"AEWIN", idx) != NULL) { qfl->qf_multiline = true; // start of a multi-line message qfl->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { @@ -1499,7 +1500,7 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, if ((idx == 'C' || idx == 'Z') && !qf_multiline) { return QF_FAIL; } - if (vim_strchr((char_u *)"EWI", idx) != NULL) { + if (vim_strchr((char_u *)"EWIN", idx) != NULL) { fields->type = idx; } else { fields->type = 0; @@ -3300,6 +3301,24 @@ void qf_history(exarg_T *eap) qf_info_T *qi = qf_cmd_get_stack(eap, false); int i; + if (eap->addr_count > 0) { + if (qi == NULL) { + EMSG(_(e_loclist)); + return; + } + + // Jump to the specified quickfix list + if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount) { + qi->qf_curlist = (int)(eap->line2 - 1); + qf_msg(qi, qi->qf_curlist, ""); + qf_update_buffer(qi, NULL); + } else { + EMSG(_(e_invrange)); + } + + return; + } + if (qf_stack_empty(qi)) { MSG(_("No entries")); } else { @@ -3414,11 +3433,13 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, // e or E 0 " error" // w or W 0 " warning" // i or I 0 " info" +// n or N 0 " note" // 0 0 "" // other 0 " c" // e or E n " error n" // w or W n " warning n" // i or I n " info n" +// n or N n " note n" // 0 n " error n" // other n " c n" // 1 x "" :helpgrep @@ -3428,15 +3449,17 @@ static char_u *qf_types(int c, int nr) static char_u cc[3]; char_u *p; - if (c == 'W' || c == 'w') + if (c == 'W' || c == 'w') { p = (char_u *)" warning"; - else if (c == 'I' || c == 'i') + } else if (c == 'I' || c == 'i') { p = (char_u *)" info"; - else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) + } else if (c == 'N' || c == 'n') { + p = (char_u *)" note"; + } else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) { p = (char_u *)" error"; - else if (c == 0 || c == 1) + } else if (c == 0 || c == 1) { p = (char_u *)""; - else { + } else { cc[0] = ' '; cc[1] = (char_u)c; cc[2] = NUL; @@ -3622,7 +3645,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) // Set the options for the quickfix buffer/window (if not already done) // Do this even if the quickfix buffer was already present, as an autocmd // might have previously deleted (:bdelete) the quickfix buffer. - if (curbuf->b_p_bt[0] != 'q') { + if (!bt_quickfix(curbuf)) { qf_set_cwindow_options(); } @@ -3696,7 +3719,7 @@ void ex_copen(exarg_T *eap) curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; check_cursor(); - update_topline(); // scroll to show the line + update_topline(curwin); // scroll to show the line } // Move the cursor in the quickfix window to "lnum". @@ -3710,7 +3733,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum) curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; curwin->w_curswant = 0; - update_topline(); // scroll to show the line + update_topline(curwin); // scroll to show the line redraw_later(curwin, VALID); curwin->w_redr_status = true; // update ruler curwin = old_curwin; @@ -3824,17 +3847,21 @@ static buf_T *qf_find_buf(qf_info_T *qi) return NULL; } -/// Update the w:quickfix_title variable in the quickfix/location list window +/// Update the w:quickfix_title variable in the quickfix/location list window in +/// all the tab pages. static void qf_update_win_titlevar(qf_info_T *qi) + FUNC_ATTR_NONNULL_ALL { - win_T *win; + qf_list_T *const qfl = qf_get_curlist(qi); + win_T *const save_curwin = curwin; - if ((win = qf_find_win(qi)) != NULL) { - win_T *curwin_save = curwin; - curwin = win; - qf_set_title_var(qf_get_curlist(qi)); - curwin = curwin_save; + FOR_ALL_TAB_WINDOWS(tp, win) { + if (is_qf_win(win, qi)) { + curwin = win; + qf_set_title_var(qfl); + } } + curwin = save_curwin; } // Find the quickfix buffer. If it exists, update the contents. @@ -4265,11 +4292,22 @@ static char_u *get_mef_name(void) return name; } -/// Returns the number of valid entries in the current quickfix/location list. +/// Returns the number of entries in the current quickfix/location list. size_t qf_get_size(exarg_T *eap) FUNC_ATTR_NONNULL_ALL { qf_info_T *qi; + + if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { + return 0; + } + return (size_t)qf_get_curlist(qi)->qf_count; +} + +/// Returns the number of valid entries in the current quickfix/location list. +size_t qf_get_valid_size(exarg_T *eap) +{ + qf_info_T *qi; qf_list_T *qfl; if ((qi = qf_cmd_get_stack(eap, false)) == NULL) { @@ -4558,74 +4596,150 @@ static qfline_T * qf_find_last_entry_on_line(qfline_T *entry, int *errornr) return entry; } -/// Find the first quickfix entry below line 'lnum' in buffer 'bnr'. +// Returns true if the specified quickfix entry is +// after the given line (linewise is true) +// or after the line and column. +static bool qf_entry_after_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum > pos->lnum; + } + return qfp->qf_lnum > pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col > pos->col); +} + +// Returns true if the specified quickfix entry is +// before the given line (linewise is true) +// or before the line and column. +static bool qf_entry_before_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum < pos->lnum; + } + return qfp->qf_lnum < pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col < pos->col); +} + +// Returns true if the specified quickfix entry is +// on or after the given line (linewise is true) +// or on or after the line and column. +static bool qf_entry_on_or_after_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum >= pos->lnum; + } + return qfp->qf_lnum > pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col >= pos->col); +} + +// Returns true if the specified quickfix entry is +// on or before the given line (linewise is true) +// or on or before the line and column. +static bool qf_entry_on_or_before_pos(const qfline_T *qfp, const pos_T *pos, + bool linewise) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (linewise) { + return qfp->qf_lnum <= pos->lnum; + } + return qfp->qf_lnum < pos->lnum + || (qfp->qf_lnum == pos->lnum && qfp->qf_col <= pos->col); +} + +/// Find the first quickfix entry after position 'pos' in buffer 'bnr'. +/// If 'linewise' is true, returns the entry after the specified line and treats +/// multiple entries on a single line as one. Otherwise returns the entry after +/// the specified line and column. /// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// index of the very first entry in the quickfix list. -/// Returns NULL if an entry is not found after 'lnum'. -static qfline_T *qf_find_entry_on_next_line(int bnr, - linenr_T lnum, - qfline_T *qfp, - int *errornr) +/// Returns NULL if an entry is not found after 'pos'. +static qfline_T *qf_find_entry_after_pos( + int bnr, + const pos_T *pos, + bool linewise, + qfline_T *qfp, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { - if (qfp->qf_lnum > lnum) { - // First entry is after line 'lnum' + if (qf_entry_after_pos(qfp, pos, linewise)) { + // First entry is after postion 'pos' return qfp; } - // Find the entry just before or at the line 'lnum' + // Find the entry just before or at the position 'pos' while (qfp->qf_next != NULL && qfp->qf_next->qf_fnum == bnr - && qfp->qf_next->qf_lnum <= lnum) { + && qf_entry_on_or_before_pos(qfp->qf_next, pos, linewise)) { qfp = qfp->qf_next; (*errornr)++; } if (qfp->qf_next == NULL || qfp->qf_next->qf_fnum != bnr) { - // No entries found after 'lnum' + // No entries found after position 'pos' return NULL; } - // Use the entry just after line 'lnum' + // Use the entry just after position 'pos' qfp = qfp->qf_next; (*errornr)++; return qfp; } -/// Find the first quickfix entry before line 'lnum' in buffer 'bnr'. +/// Find the first quickfix entry before position 'pos' in buffer 'bnr'. +/// If 'linewise' is true, returns the entry before the specified line and +/// treats multiple entries on a single line as one. Otherwise returns the entry +/// before the specified line and column. /// 'qfp' points to the very first entry in the buffer and 'errornr' is the /// index of the very first entry in the quickfix list. -/// Returns NULL if an entry is not found before 'lnum'. -static qfline_T *qf_find_entry_on_prev_line(int bnr, - linenr_T lnum, - qfline_T *qfp, - int *errornr) +/// Returns NULL if an entry is not found before 'pos'. +static qfline_T *qf_find_entry_before_pos( + int bnr, + const pos_T *pos, + bool linewise, + qfline_T *qfp, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { - // Find the entry just before the line 'lnum' + // Find the entry just before the position 'pos' while (qfp->qf_next != NULL && qfp->qf_next->qf_fnum == bnr - && qfp->qf_next->qf_lnum < lnum) { + && qf_entry_before_pos(qfp->qf_next, pos, linewise)) { qfp = qfp->qf_next; (*errornr)++; } - if (qfp->qf_lnum >= lnum) { // entry is after 'lnum' + if (qf_entry_on_or_after_pos(qfp, pos, linewise)) { return NULL; } - // If multiple entries are on the same line, then use the first entry - qfp = qf_find_first_entry_on_line(qfp, errornr); + if (linewise) { + // If multiple entries are on the same line, then use the first entry + qfp = qf_find_first_entry_on_line(qfp, errornr); + } return qfp; } -/// Find a quickfix entry in 'qfl' closest to line 'lnum' in buffer 'bnr' in +/// Find a quickfix entry in 'qfl' closest to position 'pos' in buffer 'bnr' in /// the direction 'dir'. -static qfline_T *qf_find_closest_entry(qf_list_T *qfl, - int bnr, - linenr_T lnum, - int dir, - int *errornr) +static qfline_T *qf_find_closest_entry( + qf_list_T *qfl, + int bnr, + const pos_T *pos, + Direction dir, + bool linewise, + int *errornr +) + FUNC_ATTR_NONNULL_ALL { qfline_T *qfp; @@ -4638,33 +4752,38 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, } if (dir == FORWARD) { - qfp = qf_find_entry_on_next_line(bnr, lnum, qfp, errornr); + qfp = qf_find_entry_after_pos(bnr, pos, linewise, qfp, errornr); } else { - qfp = qf_find_entry_on_prev_line(bnr, lnum, qfp, errornr); + qfp = qf_find_entry_before_pos(bnr, pos, linewise, qfp, errornr); } return qfp; } -/// Get the nth quickfix entry below the specified entry treating multiple -/// entries on a single line as one. Searches forward in the list. -static void qf_get_nth_below_entry(qfline_T *entry, - int *errornr, - linenr_T n) +/// Get the nth quickfix entry below the specified entry. Searches forward in +/// the list. If linewise is true, then treat multiple entries on a single line +/// as one. +static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, + bool linewise, int *errornr) + FUNC_ATTR_NONNULL_ALL { while (n-- > 0 && !got_int) { // qfline_T *first_entry = entry; int first_errornr = *errornr; - // Treat all the entries on the same line in this file as one - entry = qf_find_last_entry_on_line(entry, errornr); + if (linewise) { + // Treat all the entries on the same line in this file as one + entry = qf_find_last_entry_on_line(entry, errornr); + } if (entry->qf_next == NULL || entry->qf_next->qf_fnum != entry->qf_fnum) { - // If multiple entries are on the same line, then use the first - // entry - // entry = first_entry; - *errornr = first_errornr; + if (linewise) { + // If multiple entries are on the same line, then use the first + // entry + // entry = first_entry; + *errornr = first_errornr; + } break; } @@ -4673,11 +4792,12 @@ static void qf_get_nth_below_entry(qfline_T *entry, } } -/// Get the nth quickfix entry above the specified entry treating multiple -/// entries on a single line as one. Searches backwards in the list. -static void qf_get_nth_above_entry(qfline_T *entry, - int *errornr, - linenr_T n) +/// Get the nth quickfix entry above the specified entry. Searches backwards in +/// the list. If linewise is TRUE, then treat multiple entries on a single line +/// as one. +static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n, + bool linewise, int *errornr) + FUNC_ATTR_NONNULL_ALL { while (n-- > 0 && !got_int) { if (entry->qf_prev == NULL @@ -4688,25 +4808,30 @@ static void qf_get_nth_above_entry(qfline_T *entry, entry = entry->qf_prev; (*errornr)--; - // If multiple entries are on the same line, then use the first entry - entry = qf_find_first_entry_on_line(entry, errornr); + if (linewise) { + entry = qf_find_first_entry_on_line(entry, errornr); + } } } -/// Find the n'th quickfix entry adjacent to line 'lnum' in buffer 'bnr' in the -/// specified direction. -/// Returns the error number in the quickfix list or 0 if an entry is not found. -static int qf_find_nth_adj_entry(qf_list_T *qfl, - int bnr, - linenr_T lnum, - linenr_T n, - int dir) +/// Find the n'th quickfix entry adjacent to position 'pos' in buffer 'bnr' in +/// the specified direction. Returns the error number in the quickfix list or 0 +/// if an entry is not found. +static int qf_find_nth_adj_entry( + qf_list_T *qfl, + int bnr, + pos_T *pos, + linenr_T n, + Direction dir, + bool linewise +) + FUNC_ATTR_NONNULL_ALL { - qfline_T *adj_entry; int errornr; - // Find an entry closest to the specified line - adj_entry = qf_find_closest_entry(qfl, bnr, lnum, dir, &errornr); + // Find an entry closest to the specified position + qfline_T *const adj_entry = qf_find_closest_entry(qfl, bnr, pos, dir, + linewise, &errornr); if (adj_entry == NULL) { return 0; } @@ -4714,24 +4839,25 @@ static int qf_find_nth_adj_entry(qf_list_T *qfl, if (--n > 0) { // Go to the n'th entry in the current buffer if (dir == FORWARD) { - qf_get_nth_below_entry(adj_entry, &errornr, n); + qf_get_nth_below_entry(adj_entry, n, linewise, &errornr); } else { - qf_get_nth_above_entry(adj_entry, &errornr, n); + qf_get_nth_above_entry(adj_entry, n, linewise, &errornr); } } return errornr; } -/// Jump to a quickfix entry in the current file nearest to the current line. -/// ":cabove", ":cbelow", ":labove" and ":lbelow" commands +/// Jump to a quickfix entry in the current file nearest to the current line or +/// current line/col. +/// ":cabove", ":cbelow", ":labove", ":lbelow", ":cafter", ":cbefore", +/// ":lafter" and ":lbefore" commands void ex_cbelow(exarg_T *eap) { qf_info_T *qi; qf_list_T *qfl; int dir; int buf_has_flag; - int errornr = 0; if (eap->addr_count > 0 && eap->line2 <= 0) { EMSG(_(e_invrange)); @@ -4739,7 +4865,8 @@ void ex_cbelow(exarg_T *eap) } // Check whether the current buffer has any quickfix entries - if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow) { + if (eap->cmdidx == CMD_cabove || eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_cbefore || eap->cmdidx == CMD_cafter) { buf_has_flag = BUF_HAS_QF_ENTRY; } else { buf_has_flag = BUF_HAS_LL_ENTRY; @@ -4760,14 +4887,30 @@ void ex_cbelow(exarg_T *eap) return; } - if (eap->cmdidx == CMD_cbelow || eap->cmdidx == CMD_lbelow) { + if (eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cafter + || eap->cmdidx == CMD_lafter) { + // Forward motion commands dir = FORWARD; } else { dir = BACKWARD; } - errornr = qf_find_nth_adj_entry(qfl, curbuf->b_fnum, curwin->w_cursor.lnum, - eap->addr_count > 0 ? eap->line2 : 0, dir); + pos_T pos = curwin->w_cursor; + // A quickfix entry column number is 1 based whereas cursor column + // number is 0 based. Adjust the column number. + pos.col++; + const int errornr = qf_find_nth_adj_entry( + qfl, + curbuf->b_fnum, + &pos, + eap->addr_count > 0 ? eap->line2 : 0, + dir, + eap->cmdidx == CMD_cbelow + || eap->cmdidx == CMD_lbelow + || eap->cmdidx == CMD_cabove + || eap->cmdidx == CMD_labove); if (errornr > 0) { qf_jump(qi, 0, errornr, false); @@ -6814,8 +6957,9 @@ void ex_helpgrep(exarg_T *eap) if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf); - if (!new_qi && IS_LL_STACK(qi) && qf_find_buf(qi) == NULL) { - // autocommands made "qi" invalid + // When adding a location list to an existing location list stack, + // if the autocmd made the stack invalid, then just return. + if (!new_qi && IS_LL_STACK(qi) && qf_find_win_with_loclist(qi) == NULL) { decr_quickfix_busy(); return; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1c88bd4ba4..a2589ac431 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3605,6 +3605,24 @@ theend: if (backpos.ga_maxlen > BACKPOS_INITIAL) ga_clear(&backpos); + if (retval > 0) { + // Make sure the end is never before the start. Can happen when \zs + // and \ze are used. + if (REG_MULTI) { + const lpos_T *const start = &rex.reg_mmatch->startpos[0]; + const lpos_T *const end = &rex.reg_mmatch->endpos[0]; + + if (end->lnum < start->lnum + || (end->lnum == start->lnum && end->col < start->col)) { + rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; + } + } else { + if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { + rex.reg_match->endp[0] = rex.reg_match->startp[0]; + } + } + } + return retval; } @@ -3706,8 +3724,7 @@ static long regtry(bt_regprog_T *prog, } else { if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) re_extmatch_out->matches[i] = - vim_strnsave(reg_startzp[i], - (int)(reg_endzp[i] - reg_startzp[i])); + vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); } } } @@ -6547,7 +6564,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { - s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s)); + s = vim_strnsave(s, rsm.sm_match->endp[i] - s); } TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; TV_LIST_ITEM_TV(li)->vval.v_string = s; @@ -7066,7 +7083,7 @@ char_u *reg_submatch(int no) if (s == NULL || rsm.sm_match->endp[no] == NULL) { retval = NULL; } else { - retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s)); + retval = vim_strnsave(s, rsm.sm_match->endp[no] - s); } } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 7dfd16fb4f..8b5ee59d40 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -5243,9 +5243,12 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, switch (t->state->c) { case NFA_MATCH: { - // If the match ends before a composing characters and - // rex.reg_icombine is not set, that is not really a match. - if (!rex.reg_icombine && utf_iscomposing(curc)) { + // If the match is not at the start of the line, ends before a + // composing characters and rex.reg_icombine is not set, that + // is not really a match. + if (!rex.reg_icombine + && rex.input != rex.line + && utf_iscomposing(curc)) { break; } nfa_match = true; @@ -6477,8 +6480,7 @@ static long nfa_regtry(nfa_regprog_T *prog, if (lpos->start != NULL && lpos->end != NULL) re_extmatch_out->matches[i] = - vim_strnsave(lpos->start, - (int)(lpos->end - lpos->start)); + vim_strnsave(lpos->start, lpos->end - lpos->start); } } } @@ -6591,6 +6593,24 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, #endif theend: + if (retval > 0) { + // Make sure the end is never before the start. Can happen when \zs and + // \ze are used. + if (REG_MULTI) { + const lpos_T *const start = &rex.reg_mmatch->startpos[0]; + const lpos_T *const end = &rex.reg_mmatch->endpos[0]; + + if (end->lnum < start->lnum + || (end->lnum == start->lnum && end->col < start->col)) { + rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; + } + } else { + if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { + rex.reg_match->endp[0] = rex.reg_match->startp[0]; + } + } + } + return retval; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2eeeebb88d..a78f905a70 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -683,7 +683,7 @@ void conceal_check_cursor_line(void) redrawWinline(curwin, curwin->w_cursor.lnum); // Need to recompute cursor column, e.g., when starting Visual mode // without concealing. */ - curs_columns(true); + curs_columns(curwin, true); } } @@ -1698,7 +1698,7 @@ static void win_update(win_T *wp, Providers *providers) const int new_wcol = wp->w_wcol; recursive = true; curwin->w_valid &= ~VALID_TOPLINE; - update_topline(); // may invalidate w_botline again + update_topline(curwin); // may invalidate w_botline again if (old_wcol != new_wcol && (wp->w_valid & (VALID_WCOL|VALID_WROW)) @@ -2013,6 +2013,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // end-of-line int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used + bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra = 0; @@ -2196,7 +2197,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (wp->w_p_spell - && foldinfo.fi_lines == 0 + && !has_fold && *wp->w_s->b_p_spl != NUL && !GA_EMPTY(&wp->w_s->b_langp) && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { @@ -2299,6 +2300,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // handle 'incsearch' and ":s///c" highlighting } else if (highlight_match && wp == curwin + && !has_fold && lnum >= curwin->w_cursor.lnum && lnum <= curwin->w_cursor.lnum + search_match_lines) { if (lnum == curwin->w_cursor.lnum) { @@ -2415,8 +2417,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_p_list) { - if (curwin->w_p_lcs_chars.space + if (wp->w_p_list && !has_fold) { + if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.nbsp) { extra_check = true; @@ -2551,7 +2553,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, cur = wp->w_match_head; shl_flag = false; while ((cur != NULL || !shl_flag) && !number_only - && foldinfo.fi_lines == 0 + && !has_fold ) { if (!shl_flag) { shl = &search_hl; @@ -2663,7 +2665,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - //sign column + // sign column, this is hit until sign_idx reaches count if (draw_state == WL_SIGN - 1 && n_extra == 0) { draw_state = WL_SIGN; /* Show the sign column when there are any signs in this @@ -2881,8 +2883,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && vcol == 0 && n_extra == 0 && row == startrow) { @@ -2903,8 +2904,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col < grid->Columns && n_extra == 0 && row == startrow) { @@ -2916,8 +2916,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (draw_state == WL_LINE - && foldinfo.fi_level != 0 - && foldinfo.fi_lines > 0 + && has_fold && col >= grid->Columns && n_extra != 0 && row == startrow) { @@ -3085,7 +3084,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, || vcol < fromcol || vcol_prev < fromcol_prev || vcol >= tocol)) { char_attr = line_attr; - } else { + } else { attr_pri = false; if (has_syntax) { char_attr = syntax_attr; @@ -3740,7 +3739,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } wp->w_wrow = row; did_wcol = true; - curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; + wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } // Don't override visual selection highlighting. @@ -3833,9 +3832,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // Add a blank character to highlight. schar_from_ascii(linebuf_char[off], ' '); } - if (area_attr == 0) { - /* Use attributes from match with highest priority among - * 'search_hl' and the match list. */ + if (area_attr == 0 && !has_fold) { + // Use attributes from match with highest priority among + // 'search_hl' and the match list. char_attr = search_hl.attr; cur = wp->w_match_head; shl_flag = FALSE; @@ -3883,8 +3882,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, /* check if line ends before left margin */ if (vcol < v + col - win_col_off(wp)) vcol = v + col - win_col_off(wp); - /* Get rid of the boguscols now, we want to draw until the right - * edge for 'cursorcolumn'. */ + // Get rid of the boguscols now, we want to draw until the right + // edge for 'cursorcolumn'. col -= boguscols; // boguscols = 0; // Disabled because value never read after this @@ -4051,6 +4050,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && !wp->w_p_wrap && filler_todo <= 0 && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1) + && !has_fold && (*ptr != NUL || lcs_eol_one > 0 || (n_extra && (c_extra != NUL || *p_extra != NUL)))) { @@ -4345,6 +4345,10 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) // Get information needed to display the sign in line 'lnum' in window 'wp'. // If 'nrcol' is TRUE, the sign is going to be displayed in the number column. // Otherwise the sign is going to be displayed in the sign column. +// +// @param count max number of signs +// @param[out] n_extrap number of characters from pp_extra to display +// @param[in, out] sign_idxp Index of the displayed sign static void get_sign_display_info( bool nrcol, win_T *wp, @@ -4421,6 +4425,8 @@ static void get_sign_display_info( (*sign_idxp)++; if (*sign_idxp < count) { *draw_statep = WL_SIGN - 1; + } else { + *sign_idxp = 0; } } @@ -7380,7 +7386,7 @@ void screen_resize(int width, int height) cmdline_pum_display(false); } } else { - update_topline(); + update_topline(curwin); if (pum_drawn()) { // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first. // For now make sure the nested update_screen(0) won't redraw the diff --git a/src/nvim/search.c b/src/nvim/search.c index 90e1e25de2..787a464070 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -89,7 +89,7 @@ static struct spat spats[2] = static int last_idx = 0; /* index in spats[] for RE_LAST */ static char_u lastc[2] = { NUL, NUL }; // last character searched for -static int lastcdir = FORWARD; // last direction of character search +static Direction lastcdir = FORWARD; // last direction of character search static int last_t_cmd = true; // last search t_cmd static char_u lastc_bytes[MB_MAXBYTES + 1]; static int lastc_bytelen = 1; // >1 for multi-byte char @@ -437,7 +437,7 @@ void set_last_csearch(int c, char_u *s, int len) memset(lastc_bytes, 0, sizeof(lastc_bytes)); } -void set_csearch_direction(int cdir) +void set_csearch_direction(Direction cdir) { lastcdir = cdir; } @@ -1430,7 +1430,7 @@ end_do_search: * ADDING is set. If p_ic is set then the pattern must be in lowercase. * Return OK for success, or FAIL if no line found. */ -int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) +int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) { linenr_T start = 0; char_u *ptr; @@ -1496,10 +1496,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * Return FAIL or OK. */ int searchc(cmdarg_T *cap, int t_cmd) + FUNC_ATTR_NONNULL_ALL { - int c = cap->nchar; /* char to search for */ - int dir = cap->arg; /* TRUE for searching forward */ - long count = cap->count1; /* repeat count */ + int c = cap->nchar; // char to search for + Direction dir = cap->arg; // TRUE for searching forward + long count = cap->count1; // repeat count int col; char_u *p; int len; @@ -4462,7 +4463,7 @@ static void search_stat(int dirc, pos_T *pos, void find_pattern_in_path( char_u *ptr, // pointer to search pattern - int dir, // direction of expansion + Direction dir, // direction of expansion size_t len, // length of search pattern bool whole, // match whole words only bool skip_comments, // don't match inside comments diff --git a/src/nvim/sign.c b/src/nvim/sign.c index ffe51287c5..fc9f53c192 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -173,13 +173,15 @@ static void insert_sign( const char_u *group, // sign group; NULL for global group int prio, // sign priority linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *newsign = xmalloc(sizeof(signlist_T)); newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; + newsign->has_text_or_icon = has_text_or_icon; if (group != NULL) { newsign->group = sign_group_ref(group); } else { @@ -210,13 +212,14 @@ static void insert_sign( /// Insert a new sign sorted by line number and sign priority. static void insert_sign_by_lnum_prio( - buf_T *buf, // buffer to store sign in - signlist_T *prev, // previous sign entry - int id, // sign ID - const char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + const char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *sign; @@ -234,7 +237,7 @@ static void insert_sign_by_lnum_prio( sign = prev->next; } - insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); + insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon); } /// Get the name of a sign by its typenr. @@ -342,12 +345,13 @@ static void sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign) /// Add the sign into the signlist. Find the right spot to do it though. void buf_addsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID + buf_T *buf, // buffer to store sign in + int id, // sign ID const char_u *groupname, // sign group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr, // typenr of sign we are adding + bool has_text_or_icon // sign has text or icon ) { signlist_T *sign; // a sign in the signlist @@ -363,13 +367,29 @@ void buf_addsign( sign_sort_by_prio_on_line(buf, sign); return; } else if (lnum < sign->lnum) { - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + insert_sign_by_lnum_prio( + buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); return; } prev = sign; } - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + insert_sign_by_lnum_prio( + buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); } // For an existing, placed sign "markId" change the type to "typenr". @@ -786,11 +806,15 @@ static int sign_define_init_text(sign_T *sp, char_u *text) } cells += utf_ptr2cells(s); } - // Currently must be one or two display cells - if (s != endp || cells < 1 || cells > 2) { + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { EMSG2(_("E239: Invalid sign text: %s"), text); return FAIL; } + if (cells < 1) { + sp->sn_text = NULL; + return OK; + } xfree(sp->sn_text); // Allocate one byte more if we need to pad up @@ -939,7 +963,15 @@ int sign_place( if (lnum > 0) { // ":sign place {id} line={lnum} name={name} file={fname}": // place a sign - buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); + bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL; + buf_addsign( + buf, + *sign_id, + sign_group, + prio, + lnum, + sp->sn_typenr, + has_text_or_icon); } else { // ":sign place {id} file={fname}": change sign type lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 687c15bbd6..c898dba890 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -1,6 +1,7 @@ #ifndef NVIM_SIGN_DEFS_H #define NVIM_SIGN_DEFS_H +#include <stdbool.h> #include "nvim/pos.h" #include "nvim/types.h" @@ -22,13 +23,14 @@ typedef struct signlist signlist_T; struct signlist { - int id; // unique identifier for each placed sign - linenr_T lnum; // line number which has this sign - int typenr; // typenr of sign - signgroup_T *group; // sign group - int priority; // priority for highlighting - signlist_T *next; // next signlist entry - signlist_T *prev; // previous entry -- for easy reordering + int id; // unique identifier for each placed sign + linenr_T lnum; // line number which has this sign + int typenr; // typenr of sign + bool has_text_or_icon; // has text or icon + signgroup_T *group; // sign group + int priority; // priority for highlighting + signlist_T *next; // next signlist entry + signlist_T *prev; // previous entry -- for easy reordering }; // Default sign priority for highlighting diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 797fe41320..5714f5e425 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -79,7 +79,6 @@ /* for offsetof() */ #include <stddef.h> -#include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/spell.h" #include "nvim/buffer.h" @@ -6653,7 +6652,7 @@ void spell_dump_compl ( char_u *pat, // leading part of the word int ic, // ignore case - int *dir, // direction for adding matches + Direction *dir, // direction for adding matches int dumpflags_arg // DUMPFLAG_* ) { @@ -6820,7 +6819,9 @@ spell_dump_compl ( // Dumps one word: apply case modifications and append a line to the buffer. // When "lnum" is zero add insert mode completion. -static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int dumpflags, int wordflags, linenr_T lnum) +static void dump_word(slang_T *slang, char_u *word, char_u *pat, + Direction *dir, int dumpflags, int wordflags, + linenr_T lnum) { bool keepcap = false; char_u *p; @@ -6906,7 +6907,7 @@ dump_prefixes ( slang_T *slang, char_u *word, // case-folded word char_u *pat, - int *dir, + Direction *dir, int dumpflags, int flags, // flags with prefix ID linenr_T startlnum diff --git a/src/nvim/spell.h b/src/nvim/spell.h index ad66df4c5d..e93c82b91d 100644 --- a/src/nvim/spell.h +++ b/src/nvim/spell.h @@ -6,6 +6,7 @@ #include "nvim/spell_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "spell.h.generated.h" diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index e91d560284..f99eca7953 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -4186,10 +4186,10 @@ get_syn_options( arg = skiptowhite(arg); if (gname_start == arg) return NULL; - gname = vim_strnsave(gname_start, (int)(arg - gname_start)); - if (STRCMP(gname, "NONE") == 0) + gname = vim_strnsave(gname_start, arg - gname_start); + if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; - else { + } else { syn_id = syn_name2id(gname); int i; for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) @@ -4275,7 +4275,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) * Everything that's left, up to the next command, should be the * filename to include. */ - eap->argt |= (XFILE | NOSPC); + eap->argt |= (EX_XFILE | EX_NOSPC); separate_nextcmd(eap); if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) { // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the @@ -4587,7 +4587,7 @@ syn_cmd_region( while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') ++key_end; xfree(key); - key = vim_strnsave_up(rest, (int)(key_end - rest)); + key = vim_strnsave_up(rest, key_end - rest); if (STRCMP(key, "MATCHGROUP") == 0) { item = ITEM_MATCHGROUP; } else if (STRCMP(key, "START") == 0) { @@ -5047,8 +5047,8 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) EMSG2(_("E401: Pattern delimiter not found: %s"), arg); return NULL; } - /* store the pattern and compiled regexp program */ - ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1)); + // store the pattern and compiled regexp program + ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1); /* Make 'cpoptions' empty, to avoid the 'l' flag */ cpo_save = p_cpo; @@ -5136,7 +5136,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) arg_end = skiptowhite(arg_start); next_arg = skipwhite(arg_end); xfree(key); - key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start)); + key = vim_strnsave_up(arg_start, arg_end - arg_start); if (STRCMP(key, "CCOMMENT") == 0) { if (!eap->skip) curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; @@ -5195,7 +5195,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) if (!eap->skip) { /* store the pattern and compiled regexp program */ curwin->w_s->b_syn_linecont_pat = - vim_strnsave(next_arg + 1, (int)(arg_end - next_arg - 1)); + vim_strnsave(next_arg + 1, arg_end - next_arg - 1); curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; /* Make 'cpoptions' empty, to avoid the 'l' flag */ @@ -5555,18 +5555,17 @@ void ex_syntax(exarg_T *eap) { char_u *arg = eap->arg; char_u *subcmd_end; - char_u *subcmd_name; - int i; syn_cmdlinep = eap->cmdlinep; - /* isolate subcommand name */ - for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end) - ; - subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg)); - if (eap->skip) /* skip error messages for all subcommands */ - ++emsg_skip; - for (i = 0;; ++i) { + // isolate subcommand name + for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) { + } + char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg); + if (eap->skip) { // skip error messages for all subcommands + emsg_skip++; + } + for (int i = 0;; i++) { if (subcommands[i].name == NULL) { EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name); break; @@ -6719,7 +6718,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } xfree(key); key = (char *)vim_strnsave_up((const char_u *)key_start, - (int)(linep - key_start)); + linep - key_start); linep = (const char *)skipwhite((const char_u *)linep); if (strcmp(key, "NONE") == 0) { diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 7f6b7dcfec..24d3959f83 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -12,6 +12,9 @@ endfunc " Command to check for the presence of a working option. command -nargs=1 CheckOption call CheckOption(<f-args>) func CheckOption(name) + if !exists('&' .. a:name) + throw 'Checking for non-existent option ' .. a:name + endif if !exists('+' .. a:name) throw 'Skipped: ' .. a:name .. ' option not supported' endif @@ -74,6 +77,14 @@ func CheckCanRunGui() endif endfunc +" Command to check that we are using the GUI +command CheckGui call CheckGui() +func CheckGui() + if !has('gui_running') + throw 'Skipped: only works in the GUI' + endif +endfunc + " Command to check that not currently using the GUI command CheckNotGui call CheckNotGui() func CheckNotGui() diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b02514143c..275edece1e 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -414,7 +414,7 @@ for s:test in sort(s:tests) " Repeat a flaky test. Give up when: " - it fails again with the same message - " - it fails five times (with a different mesage) + " - it fails five times (with a different message) if len(v:errors) > 0 \ && (index(s:flaky_tests, s:test) >= 0 \ || v:errors[0] =~ s:flaky_errors_re) diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index d032c9a739..fd9cfb54be 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -20,6 +20,7 @@ set tags=./tags,tags set undodir^=. set wildoptions= set startofline +set sessionoptions&vi " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/test49.ok b/src/nvim/testdir/test49.ok index 50fc5d2cef..9f283e808b 100644 --- a/src/nvim/testdir/test49.ok +++ b/src/nvim/testdir/test49.ok @@ -1,6 +1,4 @@ Results of test49.vim: -*** Test 16: OK (8722) -*** Test 17: OK (285127993) *** Test 18: OK (67224583) *** Test 19: OK (69275973) *** Test 20: OK (1874575085) @@ -8,16 +6,11 @@ Results of test49.vim: *** Test 22: OK (4161) *** Test 23: OK (49) *** Test 24: OK (41) -*** Test 25: OK (260177811) -*** Test 26: OK (1681500476) *** Test 27: OK (1996459) *** Test 28: OK (1996459) *** Test 29: OK (170428555) *** Test 30: OK (190905173) *** Test 31: OK (190905173) -*** Test 32: OK (354833067) ---- Test 33: sum = 178275600 (ok) -*** Test 33: OK (1216907538) *** Test 34: OK (2146584868) *** Test 35: OK (2146584868) *** Test 36: OK (1071644672) @@ -25,17 +18,8 @@ Results of test49.vim: *** Test 38: OK (357908480) *** Test 39: OK (357908480) *** Test 40: OK (357908480) -*** Test 41: OK (3076095) -*** Test 42: OK (1505155949) -*** Test 43: OK (1157763329) -*** Test 44: OK (1031761407) -*** Test 45: OK (1157763329) -*** Test 46: OK (739407) -*** Test 47: OK (371213935) -*** Test 48: OK (756255461) *** Test 49: OK (179000669) *** Test 50: OK (363550045) -*** Test 51: OK (40744667) *** Test 52: OK (1247112011) *** Test 53: OK (131071) *** Test 54: OK (2047) @@ -53,13 +37,6 @@ Results of test49.vim: *** Test 66: OK (5464) *** Test 67: OK (212514423) *** Test 68: OK (212514423) -*** Test 69: OK (8995471) -*** Test 70: OK (69544277) -*** Test 71: OK (34886997) -*** Test 72: OK (1789569365) -*** Test 73: OK (9032615) -*** Test 74: OK (224907669) -*** Test 75: OK (2000403408) *** Test 76: OK (1610087935) *** Test 77: OK (1388671) *** Test 78: OK (134217728) @@ -72,11 +49,6 @@ Results of test49.vim: *** Test 85: OK (198689) --- Test 86: No Crash for vimgrep on BufUnload *** Test 86: OK (0) ---- Test 87: 3 ---- Test 87: 5 ---- Test 87: abcdefghijk ---- Test 87: Successfully executed funcref Add2 -*** Test 87: OK (0) --- Test 88: All tests were run with throwing exceptions on error. The $VIMNOERRTHROW control is not configured. --- Test 88: All tests were run with throwing exceptions on interrupt. diff --git a/src/nvim/testdir/test49.vim b/src/nvim/testdir/test49.vim index 5468f7c4aa..3ee5e9ff7c 100644 --- a/src/nvim/testdir/test49.vim +++ b/src/nvim/testdir/test49.vim @@ -1,6 +1,6 @@ " Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> -" Last Change: 2019 Oct 08 +" Last Change: 2019 Nov 03 "------------------------------------------------------------------------------- " Test environment {{{1 @@ -521,7 +521,7 @@ endfunction " " Create a script that consists of the body of the function a:funcname. " Replace any ":return" by a ":finish", any argument variable by a global -" variable, and and every ":call" by a ":source" for the next following argument +" variable, and every ":call" by a ":source" for the next following argument " in the variable argument list. This function is useful if similar tests are " to be made for a ":return" from a function call or a ":finish" in a script " file. @@ -608,196 +608,8 @@ com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>) " END_OF_TEST_ENVIRONMENT - do not change or remove this line. -" Tests 1 to 15 were moved to test_vimscript.vim -let Xtest = 16 - -"------------------------------------------------------------------------------- -" Test 16: Double :else or :elseif after :else {{{1 -" -" Multiple :elses or an :elseif after an :else are forbidden. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() abort - if 0 - Xpath 1 " X: 0 - else - Xpath 2 " X: 2 - else " aborts function - Xpath 4 " X: 0 - endif -endfunction - -function! G() abort - if 0 - Xpath 8 " X: 0 - else - Xpath 16 " X: 16 - elseif 1 " aborts function - Xpath 32 " X: 0 - else - Xpath 64 " X: 0 - endif -endfunction - -function! H() abort - if 0 - Xpath 128 " X: 0 - elseif 0 - Xpath 256 " X: 0 - else - Xpath 512 " X: 512 - else " aborts function - Xpath 1024 " X: 0 - endif -endfunction - -function! I() abort - if 0 - Xpath 2048 " X: 0 - elseif 0 - Xpath 4096 " X: 0 - else - Xpath 8192 " X: 8192 - elseif 1 " aborts function - Xpath 16384 " X: 0 - else - Xpath 32768 " X: 0 - endif -endfunction - -call F() -call G() -call H() -call I() - -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 8722 - - -"------------------------------------------------------------------------------- -" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 -" -" The :while/:endwhile takes precedence in nesting over an unclosed -" :if or an unopened :endif. -"------------------------------------------------------------------------------- - -XpathINIT - -function! MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = escape(v:errmsg, '"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunction - -let messages = "" - -" While loops inside a function are continued on error. -function! F() - let v:errmsg = "" - XloopINIT 1 16 - let loops = 3 - while loops > 0 - let loops = loops - 1 " 2: 1: 0: - Xloop 1 " X: 1 + 1*16 + 1*16*16 - if (loops == 1) - Xloop 2 " X: 2*16 - XloopNEXT - continue - elseif (loops == 0) - Xloop 4 " X: 4*16*16 - break - elseif 1 - Xloop 8 " X: 8 - XloopNEXT - " endif missing! - endwhile " :endwhile after :if 1 - Xpath 4096 " X: 16*16*16 - if MSG('E171', "Missing :endif") - let g:messages = g:messages . "A" - endif - - let v:errmsg = "" - XloopINIT! 8192 4 - let loops = 2 - while loops > 0 " 2: 1: - XloopNEXT - let loops = loops - 1 - Xloop 1 " X: 8192 + 8192*4 - if 0 - Xloop 2 " X: 0 - " endif missing - endwhile " :endwhile after :if 0 - Xpath 131072 " X: 8192*4*4 - if MSG('E171', "Missing :endif") - let g:messages = g:messages . "B" - endif - - let v:errmsg = "" - XloopINIT 262144 4 - let loops = 2 - while loops > 0 " 2: 1: - let loops = loops - 1 - Xloop 1 " X: 262144 + 262144 * 4 - " if missing! - endif " :endif without :if in while - Xloop 2 " X: 524288 + 524288 * 4 - XloopNEXT - endwhile - Xpath 4194304 " X: 262144*4*4 - if MSG('E580', ":endif without :if") - let g:messages = g:messages . "C" - endif -endfunction - -call F() - -" Error continuation outside a function is at the outermost :endwhile or :endif. -let v:errmsg = "" -XloopINIT! 8388608 4 -let loops = 2 -while loops > 0 " 2: 1: - XloopNEXT - let loops = loops - 1 - Xloop 1 " X: 8388608 + 0 * 4 - if 0 - Xloop 2 " X: 0 - " endif missing! Following :endwhile fails. -endwhile | Xpath 134217728 " X: 0 -Xpath 268435456 " X: 2*8388608*4*4 -if MSG('E171', "Missing :endif") - let messages = g:messages . "D" -endif - -if messages != "ABCD" - Xpath 536870912 " X: 0 - Xout "messages is" messages "instead of ABCD" -endif - -unlet loops messages -delfunction F -delfunction MSG - -Xcheck 285127993 - +" Tests 1 to 17 were moved to test_vimscript.vim +let Xtest = 18 "------------------------------------------------------------------------------- " Test 18: Interrupt (Ctrl-C pressed) {{{1 @@ -1313,140 +1125,8 @@ endif Xcheck 41 - -"------------------------------------------------------------------------------- -" Test 25: Executing :finally clauses on normal control flow {{{1 -" -" Control flow in a :try conditional should always fall through to its -" :finally clause. A :finally clause of a :try conditional inside an -" inactive conditional should never be executed. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - let loops = 3 - XloopINIT 1 256 - while loops > 0 " 3: 2: 1: - Xloop 1 " X: 1 + 1*256 + 1*256*256 - if loops >= 2 - try - Xloop 2 " X: 2 + 2*256 - if loops == 2 - try - Xloop 4 " X: 4*256 - finally - Xloop 8 " X: 8*256 - endtry - endif - finally - Xloop 16 " X: 16 + 16*256 - if loops == 2 - try - Xloop 32 " X: 32*256 - finally - Xloop 64 " X: 64*256 - endtry - endif - endtry - endif - Xloop 128 " X: 128 + 128*256 + 128*256*256 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 16777216 " X: 16777216 -endfunction - -if 1 - try - Xpath 33554432 " X: 33554432 - call F() - Xpath 67108864 " X: 67108864 - finally - Xpath 134217728 " X: 134217728 - endtry -else - try - Xpath 268435456 " X: 0 - finally - Xpath 536870912 " X: 0 - endtry -endif - -delfunction F - -Xcheck 260177811 - - -"------------------------------------------------------------------------------- -" Test 26: Executing :finally clauses after :continue or :break {{{1 -" -" For a :continue or :break dynamically enclosed in a :try/:endtry -" region inside the next surrounding :while/:endwhile, if the -" :continue/:break is before the :finally, the :finally clause is -" executed first. If the :continue/:break is after the :finally, the -" :finally clause is broken (like an :if/:endif region). -"------------------------------------------------------------------------------- - -XpathINIT - -try - let loops = 3 - XloopINIT! 1 32 - while loops > 0 - XloopNEXT - try - try - if loops == 2 " 3: 2: 1: - Xloop 1 " X: 1*32 - let loops = loops - 1 - continue - elseif loops == 1 - Xloop 2 " X: 2*32*32 - break - finish - endif - Xloop 4 " X: 4 - endtry - finally - Xloop 8 " X: 8 + 8*32 + 8*32*32 - endtry - Xloop 16 " X: 16 - let loops = loops - 1 - endwhile - Xpath 32768 " X: 32768 -finally - Xpath 65536 " X: 65536 - let loops = 3 - XloopINIT 131072 16 - while loops > 0 - try - finally - try - if loops == 2 - Xloop 1 " X: 131072*16 - let loops = loops - 1 - XloopNEXT - continue - elseif loops == 1 - Xloop 2 " X: 131072*2*16*16 - break - finish - endif - endtry - Xloop 4 " X: 131072*4 - endtry - Xloop 8 " X: 131072*8 - let loops = loops - 1 - XloopNEXT - endwhile - Xpath 536870912 " X: 536870912 -endtry -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1681500476 +" Tests 25 and 26 were moved to test_trycatch.vim +let Xtest = 27 "------------------------------------------------------------------------------- @@ -1783,252 +1463,8 @@ endif Xcheck 190905173 - -"------------------------------------------------------------------------------- -" Test 32: Remembering the :return value on :finally {{{1 -" -" If a :finally clause is executed due to a :return specifying -" a value, this is the value visible to the caller if not overwritten -" by a new :return in the :finally clause. A :return without a value -" in the :finally clause overwrites with value 0. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - return "ABCD" - Xpath 4 " X: 0 - finally - Xpath 8 " X: 8 - endtry - Xpath 16 " X: 0 - finally - Xpath 32 " X: 32 - endtry - Xpath 64 " X: 0 -endfunction - -function! G() - try - Xpath 128 " X: 128 - return 8 - Xpath 256 " X: 0 - finally - Xpath 512 " X: 512 - return 16 + strlen(F()) - Xpath 1024 " X: 0 - endtry - Xpath 2048 " X: 0 -endfunction - -function! H() - try - Xpath 4096 " X: 4096 - return 32 - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - return - Xpath 32768 " X: 0 - endtry - Xpath 65536 " X: 0 -endfunction - -function! I() - try - Xpath 131072 " X: 131072 - finally - Xpath 262144 " X: 262144 - return G() + H() + 64 - Xpath 524288 " X: 0 - endtry - Xpath 1048576 " X: 0 -endfunction - -let retcode = I() -Xpath 2097152 " X: 2097152 - -if retcode < 0 - Xpath 4194304 " X: 0 -endif -if retcode % 4 - Xpath 8388608 " X: 0 -endif -if (retcode/4) % 2 - Xpath 16777216 " X: 16777216 -endif -if (retcode/8) % 2 - Xpath 33554432 " X: 0 -endif -if (retcode/16) % 2 - Xpath 67108864 " X: 67108864 -endif -if (retcode/32) % 2 - Xpath 134217728 " X: 0 -endif -if (retcode/64) % 2 - Xpath 268435456 " X: 268435456 -endif -if retcode/128 - Xpath 536870912 " X: 0 -endif - -unlet retcode -delfunction F -delfunction G -delfunction H -delfunction I - -Xcheck 354833067 - - -"------------------------------------------------------------------------------- -" Test 33: :return under :execute or user command and :finally {{{1 -" -" A :return command may be executed under an ":execute" or from -" a user command. Executing of :finally clauses and passing through -" the return code works also then. -"------------------------------------------------------------------------------- -XpathINIT - -command! -nargs=? RETURN - \ try | return <args> | finally | return <args> * 2 | endtry - -function! F() - try - RETURN 8 - Xpath 1 " X: 0 - finally - Xpath 2 " X: 2 - endtry - Xpath 4 " X: 0 -endfunction - -function! G() - try - RETURN 32 - Xpath 8 " X: 0 - finally - Xpath 16 " X: 16 - RETURN 128 - Xpath 32 " X: 0 - endtry - Xpath 64 " X: 0 -endfunction - -function! H() - try - execute "try | return 512 | finally | return 1024 | endtry" - Xpath 128 " X: 0 - finally - Xpath 256 " X: 256 - endtry - Xpath 512 " X: 0 -endfunction - -function! I() - try - execute "try | return 2048 | finally | return 4096 | endtry" - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - execute "try | return 8192 | finally | return 16384 | endtry" - Xpath 4096 " X: 0 - endtry - Xpath 8192 " X: 0 -endfunction - -function! J() - try - RETURN 32768 - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 32768 - return - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 -endfunction - -function! K() - try - execute "try | return 131072 | finally | return 262144 | endtry" - Xpath 262144 " X: 0 - finally - Xpath 524288 " X: 524288 - execute "try | return 524288 | finally | return | endtry" - Xpath 1048576 " X: 0 - endtry - Xpath 2097152 " X: 0 -endfunction - -function! L() - try - return - Xpath 4194304 " X: 0 - finally - Xpath 8388608 " X: 8388608 - RETURN 1048576 - Xpath 16777216 " X: 0 - endtry - Xpath 33554432 " X: 0 -endfunction - -function! M() - try - return - Xpath 67108864 " X: 0 - finally - Xpath 134217728 " X: 134217728 - execute "try | return 4194304 | finally | return 8388608 | endtry" - Xpath 268435456 " X: 0 - endtry - Xpath 536870912 " X: 0 -endfunction - -function! N() - RETURN 16777216 -endfunction - -function! O() - execute "try | return 67108864 | finally | return 134217728 | endtry" -endfunction - -let sum = F() + G() + H() + I() + J() + K() + L() + M() -let expected = 16 + 256 + 1024 + 16384 + 0 + 0 + 2097152 + 8388608 -let sum = sum + N() + O() -let expected = expected + 33554432 + 134217728 - -if sum == expected - Xout "sum = " . sum . " (ok)" -else - Xout "sum = " . sum . ", expected: " . expected -endif - -Xpath 1073741824 " X: 1073741824 - -if sum != expected - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 -endif - -unlet sum expected -delfunction F -delfunction G -delfunction H -delfunction I -delfunction J -delfunction K -delfunction L -delfunction M -delfunction N -delfunction O - -Xcheck 1216907538 +" Tests 32 and 33 were moved to test_trycatch.vim +let Xtest = 34 "------------------------------------------------------------------------------- @@ -2654,643 +2090,8 @@ endif Xcheck 357908480 - -"------------------------------------------------------------------------------- -" Test 41: Skipped :throw finding next command {{{1 -" -" A :throw in an inactive conditional must not hide a following -" command. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 1 " X: 1 - if 0 | throw "never" | endif | Xpath 2 " X: 2 - Xpath 4 " X: 4 -endfunction - -function! G() - Xpath 8 " X: 8 - while 0 | throw "never" | endwhile | Xpath 16 " X: 16 - Xpath 32 " X: 32 -endfunction - -function H() - Xpath 64 " X: 64 - if 0 | try | throw "never" | endtry | endif | Xpath 128 " X: 128 - Xpath 256 " X: 256 -endfunction - -Xpath 512 " X: 512 - -try - Xpath 1024 " X: 1024 - call F() - Xpath 2048 " X: 2048 -catch /.*/ - Xpath 4096 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 8192 " X: 8192 - -try - Xpath 16384 " X: 16384 - call G() - Xpath 32768 " X: 32768 -catch /.*/ - Xpath 65536 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 131072 " X: 131072 - -try - Xpath 262144 " X: 262144 - call H() - Xpath 524288 " X: 524288 -catch /.*/ - Xpath 1048576 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 2097152 " X: 2097152 - -delfunction F -delfunction G -delfunction H - -Xcheck 3076095 - - -"------------------------------------------------------------------------------- -" Test 42: Catching number and string exceptions {{{1 -" -" When a number is thrown, it is converted to a string exception. -" Numbers and strings may be caught by specifying a regular exception -" as argument to the :catch command. -"------------------------------------------------------------------------------- - -XpathINIT - -try - - try - Xpath 1 " X: 1 - throw 4711 - Xpath 2 " X: 0 - catch /4711/ - Xpath 4 " X: 4 - endtry - - try - Xpath 8 " X: 8 - throw 4711 - Xpath 16 " X: 0 - catch /^4711$/ - Xpath 32 " X: 32 - endtry - - try - Xpath 64 " X: 64 - throw 4711 - Xpath 128 " X: 0 - catch /\d/ - Xpath 256 " X: 256 - endtry - - try - Xpath 512 " X: 512 - throw 4711 - Xpath 1024 " X: 0 - catch /^\d\+$/ - Xpath 2048 " X: 2048 - endtry - - try - Xpath 4096 " X: 4096 - throw "arrgh" - Xpath 8192 " X: 0 - catch /arrgh/ - Xpath 16384 " X: 16384 - endtry - - try - Xpath 32768 " X: 32768 - throw "arrgh" - Xpath 65536 " X: 0 - catch /^arrgh$/ - Xpath 131072 " X: 131072 - endtry - - try - Xpath 262144 " X: 262144 - throw "arrgh" - Xpath 524288 " X: 0 - catch /\l/ - Xpath 1048576 " X: 1048576 - endtry - - try - Xpath 2097152 " X: 2097152 - throw "arrgh" - Xpath 4194304 " X: 0 - catch /^\l\+$/ - Xpath 8388608 " X: 8388608 - endtry - - try - try - Xpath 16777216 " X: 16777216 - throw "ARRGH" - Xpath 33554432 " X: 0 - catch /^arrgh$/ - Xpath 67108864 " X: 0 - endtry - catch /^\carrgh$/ - Xpath 134217728 " X: 134217728 - endtry - - try - Xpath 268435456 " X: 268435456 - throw "" - Xpath 536870912 " X: 0 - catch /^$/ - Xpath 1073741824 " X: 1073741824 - endtry - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xcheck 1505155949 - - -"------------------------------------------------------------------------------- -" Test 43: Selecting the correct :catch clause {{{1 -" -" When an exception is thrown and there are multiple :catch clauses, -" the first matching one is taken. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 1024 -let loops = 3 -while loops > 0 - try - if loops == 3 - Xloop 1 " X: 1 - throw "a" - Xloop 2 " X: 0 - elseif loops == 2 - Xloop 4 " X: 4*1024 - throw "ab" - Xloop 8 " X: 0 - elseif loops == 1 - Xloop 16 " X: 16*1024*1024 - throw "abc" - Xloop 32 " X: 0 - endif - catch /abc/ - Xloop 64 " X: 64*1024*1024 - catch /ab/ - Xloop 128 " X: 128*1024 - catch /.*/ - Xloop 256 " X: 256 - catch /a/ - Xloop 512 " X: 0 - endtry - - let loops = loops - 1 - XloopNEXT -endwhile -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1157763329 - - -"------------------------------------------------------------------------------- -" Test 44: Missing or empty :catch patterns {{{1 -" -" A missing or empty :catch pattern means the same as /.*/, that is, -" catches everything. To catch only empty exceptions, /^$/ must be -" used. A :catch with missing, empty, or /.*/ argument also works -" when followed by another command separated by a bar on the same -" line. :catch patterns cannot be specified between ||. But other -" pattern separators can be used instead of //. -"------------------------------------------------------------------------------- - -XpathINIT - -try - try - Xpath 1 " X: 1 - throw "" - catch /^$/ - Xpath 2 " X: 2 - endtry - - try - Xpath 4 " X: 4 - throw "" - catch /.*/ - Xpath 8 " X: 8 - endtry - - try - Xpath 16 " X: 16 - throw "" - catch // - Xpath 32 " X: 32 - endtry - - try - Xpath 64 " X: 64 - throw "" - catch - Xpath 128 " X: 128 - endtry - - try - Xpath 256 " X: 256 - throw "oops" - catch /^$/ - Xpath 512 " X: 0 - catch /.*/ - Xpath 1024 " X: 1024 - endtry - - try - Xpath 2048 " X: 2048 - throw "arrgh" - catch /^$/ - Xpath 4096 " X: 0 - catch // - Xpath 8192 " X: 8192 - endtry - - try - Xpath 16384 " X: 16384 - throw "brrr" - catch /^$/ - Xpath 32768 " X: 0 - catch - Xpath 65536 " X: 65536 - endtry - - try | Xpath 131072 | throw "x" | catch /.*/ | Xpath 262144 | endtry - " X: 131072 + 262144 - - try | Xpath 524288 | throw "y" | catch // | Xpath 1048576 | endtry - " X: 524288 + 1048576 - - while 1 - try - let caught = 0 - let v:errmsg = "" - " Extra try level: if ":catch" without arguments below raises - " a syntax error because it misinterprets the "Xpath" as a pattern, - " let it be caught by the ":catch /.*/" below. - try - try | Xpath 2097152 | throw "z" | catch | Xpath 4194304 | : - endtry " X: 2097152 + 4194304 - endtry - catch /.*/ - let caught = 1 - Xout v:exception "in" v:throwpoint - finally - if $VIMNOERRTHROW && v:errmsg != "" - Xout v:errmsg - endif - if caught || $VIMNOERRTHROW && v:errmsg != "" - Xpath 8388608 " X: 0 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - let cologne = 4711 - try - try - Xpath 16777216 " X: 16777216 - throw "throw cologne" - " Next lines catches all and throws 4711: - catch |throw cologne| - Xpath 33554432 " X: 0 - endtry - catch /4711/ - Xpath 67108864 " X: 67108864 - endtry - - try - Xpath 134217728 " X: 134217728 - throw "plus" - catch +plus+ - Xpath 268435456 " X: 268435456 - endtry - - Xpath 536870912 " X: 536870912 -catch /.*/ - Xpath 1073741824 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet! caught cologne - -Xcheck 1031761407 - - -"------------------------------------------------------------------------------- -" Test 45: Catching exceptions from nested :try blocks {{{1 -" -" When :try blocks are nested, an exception is caught by the innermost -" try conditional that has a matching :catch clause. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT 1 1024 -let loops = 3 -while loops > 0 - try - try - try - try - if loops == 3 - Xloop 1 " X: 1 - throw "a" - Xloop 2 " X: 0 - elseif loops == 2 - Xloop 4 " X: 4*1024 - throw "ab" - Xloop 8 " X: 0 - elseif loops == 1 - Xloop 16 " X: 16*1024*1024 - throw "abc" - Xloop 32 " X: 0 - endif - catch /abc/ - Xloop 64 " X: 64*1024*1024 - endtry - catch /ab/ - Xloop 128 " X: 128*1024 - endtry - catch /.*/ - Xloop 256 " X: 256 - endtry - catch /a/ - Xloop 512 " X: 0 - endtry - - let loops = loops - 1 - XloopNEXT -endwhile -Xpath 1073741824 " X: 1073741824 - -unlet loops - -Xcheck 1157763329 - - -"------------------------------------------------------------------------------- -" Test 46: Executing :finally after a :throw in nested :try {{{1 -" -" When an exception is thrown from within nested :try blocks, the -" :finally clauses of the non-catching try conditionals should be -" executed before the matching :catch of the next surrounding :try -" gets the control. If this also has a :finally clause, it is -" executed afterwards. -"------------------------------------------------------------------------------- - -XpathINIT - -let sum = 0 - -try - Xpath 1 " X: 1 - try - Xpath 2 " X: 2 - try - Xpath 4 " X: 4 - try - Xpath 8 " X: 8 - throw "ABC" - Xpath 16 " X: 0 - catch /xyz/ - Xpath 32 " X: 0 - finally - Xpath 64 " X: 64 - if sum != 0 - Xpath 128 " X: 0 - endif - let sum = sum + 1 - endtry - Xpath 256 " X: 0 - catch /123/ - Xpath 512 " X: 0 - catch /321/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - if sum != 1 - Xpath 4096 " X: 0 - endif - let sum = sum + 2 - endtry - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - if sum != 3 - Xpath 32768 " X: 0 - endif - let sum = sum + 4 - endtry - Xpath 65536 " X: 0 -catch /ABC/ - Xpath 131072 " X: 131072 - if sum != 7 - Xpath 262144 " X: 0 - endif - let sum = sum + 8 -finally - Xpath 524288 " X: 524288 - if sum != 15 - Xpath 1048576 " X: 0 - endif - let sum = sum + 16 -endtry -Xpath 65536 " X: 65536 -if sum != 31 - Xpath 131072 " X: 0 -endif - -unlet sum - -Xcheck 739407 - - -"------------------------------------------------------------------------------- -" Test 47: Throwing exceptions from a :catch clause {{{1 -" -" When an exception is thrown from a :catch clause, it should not be -" caught by a :catch of the same :try conditional. After executing -" the :finally clause (if present), surrounding try conditionals -" should be checked for a matching :catch. -"------------------------------------------------------------------------------- - -XpathINIT - -Xpath 1 " X: 1 -try - Xpath 2 " X: 2 - try - Xpath 4 " X: 4 - try - Xpath 8 " X: 8 - throw "x1" - Xpath 16 " X: 0 - catch /x1/ - Xpath 32 " X: 32 - try - Xpath 64 " X: 64 - throw "x2" - Xpath 128 " X: 0 - catch /x1/ - Xpath 256 " X: 0 - catch /x2/ - Xpath 512 " X: 512 - try - Xpath 1024 " X: 1024 - throw "x3" - Xpath 2048 " X: 0 - catch /x1/ - Xpath 4096 " X: 0 - catch /x2/ - Xpath 8192 " X: 0 - finally - Xpath 16384 " X: 16384 - endtry - Xpath 32768 " X: 0 - catch /x3/ - Xpath 65536 " X: 0 - endtry - Xpath 131072 " X: 0 - catch /x1/ - Xpath 262144 " X: 0 - catch /x2/ - Xpath 524288 " X: 0 - catch /x3/ - Xpath 1048576 " X: 0 - finally - Xpath 2097152 " X: 2097152 - endtry - Xpath 4194304 " X: 0 - catch /x1/ - Xpath 8388608 " X: 0 - catch /x2/ - Xpath 16777216 " X: 0 - catch /x3/ - Xpath 33554432 " X: 33554432 - endtry - Xpath 67108864 " X: 67108864 -catch /.*/ - Xpath 134217728 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 268435456 " X: 268435456 - -Xcheck 371213935 - - -"------------------------------------------------------------------------------- -" Test 48: Throwing exceptions from a :finally clause {{{1 -" -" When an exception is thrown from a :finally clause, it should not be -" caught by a :catch of the same :try conditional. Surrounding try -" conditionals should be checked for a matching :catch. A previously -" thrown exception is discarded. -"------------------------------------------------------------------------------- - -XpathINIT - -try - - try - try - Xpath 1 " X: 1 - catch /x1/ - Xpath 2 " X: 0 - finally - Xpath 4 " X: 4 - throw "x1" - Xpath 8 " X: 0 - endtry - Xpath 16 " X: 0 - catch /x1/ - Xpath 32 " X: 32 - endtry - Xpath 64 " X: 64 - - try - try - Xpath 128 " X: 128 - throw "x2" - Xpath 256 " X: 0 - catch /x2/ - Xpath 512 " X: 512 - catch /x3/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - throw "x3" - Xpath 4096 " X: 0 - endtry - Xpath 8192 " X: 0 - catch /x2/ - Xpath 16384 " X: 0 - catch /x3/ - Xpath 32768 " X: 32768 - endtry - Xpath 65536 " X: 65536 - - try - try - try - Xpath 131072 " X: 131072 - throw "x4" - Xpath 262144 " X: 0 - catch /x5/ - Xpath 524288 " X: 0 - finally - Xpath 1048576 " X: 1048576 - throw "x5" " discards "x4" - Xpath 2097152 " X: 0 - endtry - Xpath 4194304 " X: 0 - catch /x4/ - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - endtry - Xpath 33554432 " X: 0 - catch /x5/ - Xpath 67108864 " X: 67108864 - endtry - Xpath 134217728 " X: 134217728 - -catch /.*/ - Xpath 268435456 " X: 0 - Xout v:exception "in" v:throwpoint -endtry -Xpath 536870912 " X: 536870912 - -Xcheck 756255461 +" Tests 41 to 48 were moved to test_trycatch.vim +let Xtest = 49 "------------------------------------------------------------------------------- @@ -3443,124 +2244,8 @@ delfunction F Xcheck 363550045 - -"------------------------------------------------------------------------------- -" Test 51: Throwing exceptions across :execute and user commands {{{1 -" -" A :throw command may be executed under an ":execute" or from -" a user command. -"------------------------------------------------------------------------------- - -XpathINIT - -command! -nargs=? THROW1 throw <args> | throw 1 -command! -nargs=? THROW2 try | throw <args> | endtry | throw 2 -command! -nargs=? THROW3 try | throw 3 | catch /3/ | throw <args> | endtry -command! -nargs=? THROW4 try | throw 4 | finally | throw <args> | endtry - -try - - try - try - Xpath 1 " X: 1 - THROW1 "A" - catch /A/ - Xpath 2 " X: 2 - endtry - catch /1/ - Xpath 4 " X: 0 - endtry - - try - try - Xpath 8 " X: 8 - THROW2 "B" - catch /B/ - Xpath 16 " X: 16 - endtry - catch /2/ - Xpath 32 " X: 0 - endtry - - try - try - Xpath 64 " X: 64 - THROW3 "C" - catch /C/ - Xpath 128 " X: 128 - endtry - catch /3/ - Xpath 256 " X: 0 - endtry - - try - try - Xpath 512 " X: 512 - THROW4 "D" - catch /D/ - Xpath 1024 " X: 1024 - endtry - catch /4/ - Xpath 2048 " X: 0 - endtry - - try - try - Xpath 4096 " X: 4096 - execute 'throw "E" | throw 5' - catch /E/ - Xpath 8192 " X: 8192 - endtry - catch /5/ - Xpath 16384 " X: 0 - endtry - - try - try - Xpath 32768 " X: 32768 - execute 'try | throw "F" | endtry | throw 6' - catch /F/ - Xpath 65536 " X: 65536 - endtry - catch /6/ - Xpath 131072 " X: 0 - endtry - - try - try - Xpath 262144 " X: 262144 - execute'try | throw 7 | catch /7/ | throw "G" | endtry' - catch /G/ - Xpath 524288 " X: 524288 - endtry - catch /7/ - Xpath 1048576 " X: 0 - endtry - - try - try - Xpath 2097152 " X: 2097152 - execute 'try | throw 8 | finally | throw "H" | endtry' - catch /H/ - Xpath 4194304 " X: 4194304 - endtry - catch /8/ - Xpath 8388608 " X: 0 - endtry - -catch /.*/ - Xpath 16777216 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 33554432 " X: 33554432 - -delcommand THROW1 -delcommand THROW2 -delcommand THROW3 -delcommand THROW4 - -Xcheck 40744667 +" Test 51 was moved to test_trycatch.vim +let Xtest = 52 "------------------------------------------------------------------------------- @@ -3694,7 +2379,7 @@ endif if ExtraVim(msgfile) try Xpath 4194304 " X: 4194304 - let x = novar " error E121; exception: E121 + let x = novar " error E121/E15; exception: E121 catch /E15:/ " should not catch Xpath 8388608 " X: 0 endtry @@ -3702,7 +2387,7 @@ if ExtraVim(msgfile) endif Xpath 33554432 " X: 33554432 -if !MESSAGES('E121', "Undefined variable") +if !MESSAGES('E121', "Undefined variable", 'E15', "Invalid expression") Xpath 67108864 " X: 0 endif @@ -4236,14 +2921,18 @@ if MESSAGES('E170', "Missing :endwhile") endif if ExtraVim(msgfile) -" try -" throw "a" -" catch /a/ -" if 1 -" endtry + try + Xpath 4194304 " X: 4194304 + let x = novar " error E121; exception: E121 + catch /E15:/ " should not catch + Xpath 8388608 " X: 0 + endtry + Xpath 16777216 " X: 0 endif -if MESSAGES('E171', "Missing :endif") - Xpath 128 " X: 128 + +Xpath 33554432 " X: 33554432 +if !MESSAGES('E121', "Undefined variable") + Xpath 67108864 " X: 0 endif if ExtraVim(msgfile) @@ -6227,954 +4916,8 @@ delfunction F Xcheck 212514423 - -"------------------------------------------------------------------------------- -" Test 69: :throw across :if, :elseif, :while {{{1 -" -" On an :if, :elseif, or :while command, an exception might be thrown -" during evaluation of the expression to test. The exception can be -" caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -XloopINIT! 1 2 - -function! THROW(x) - XloopNEXT - Xloop 1 " X: 1 + 2 + 4 - throw a:x -endfunction - -try - - try - Xpath 8 " X: 8 - if 4711 == THROW("if") + 111 - Xpath 16 " X: 0 - else - Xpath 32 " X: 0 - endif - Xpath 64 " X: 0 - catch /^if$/ - Xpath 128 " X: 128 - catch /.*/ - Xpath 256 " X: 0 - Xout "if:" v:exception "in" v:throwpoint - endtry - - try - Xpath 512 " X: 512 - if 4711 == 4 + 7 + 1 + 1 - Xpath 1024 " X: 0 - elseif 4711 == THROW("elseif") + 222 - Xpath 2048 " X: 0 - else - Xpath 4096 " X: 0 - endif - Xpath 8192 " X: 0 - catch /^elseif$/ - Xpath 16384 " X: 16384 - catch /.*/ - Xpath 32768 " X: 0 - Xout "elseif:" v:exception "in" v:throwpoint - endtry - - try - Xpath 65536 " X: 65536 - while 4711 == THROW("while") + 4711 - Xpath 131072 " X: 0 - break - endwhile - Xpath 262144 " X: 0 - catch /^while$/ - Xpath 524288 " X: 524288 - catch /.*/ - Xpath 1048576 " X: 0 - Xout "while:" v:exception "in" v:throwpoint - endtry - -catch /^0$/ " default return value - Xpath 2097152 " X: 0 - Xout v:throwpoint -catch /.*/ - Xout v:exception "in" v:throwpoint - Xpath 4194304 " X: 0 -endtry - -Xpath 8388608 " X: 8388608 - -delfunction THROW - -Xcheck 8995471 - - -"------------------------------------------------------------------------------- -" Test 70: :throw across :return or :throw {{{1 -" -" On a :return or :throw command, an exception might be thrown during -" evaluation of the expression to return or throw, respectively. The -" exception can be caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! F(x, y, n) - let g:taken = g:taken . "F" . a:n - return a:x + THROW(a:y, a:n) -endfunction - -function! G(x, y, n) - let g:taken = g:taken . "G" . a:n - throw a:x . THROW(a:y, a:n) - return a:x -endfunction - -try - try - Xpath 1 " X: 1 - call F(4711, "return", 1) - Xpath 2 " X: 0 - catch /^return$/ - Xpath 4 " X: 4 - catch /.*/ - Xpath 8 " X: 0 - Xout "return:" v:exception "in" v:throwpoint - endtry - - try - Xpath 16 " X: 16 - let var = F(4712, "return-var", 2) - Xpath 32 " X: 0 - catch /^return-var$/ - Xpath 64 " X: 64 - catch /.*/ - Xpath 128 " X: 0 - Xout "return-var:" v:exception "in" v:throwpoint - finally - unlet! var - endtry - - try - Xpath 256 " X: 256 - throw "except1" . THROW("throw1", 3) - Xpath 512 " X: 0 - catch /^except1/ - Xpath 1024 " X: 0 - catch /^throw1$/ - Xpath 2048 " X: 2048 - catch /.*/ - Xpath 4096 " X: 0 - Xout "throw1:" v:exception "in" v:throwpoint - endtry - - try - Xpath 8192 " X: 8192 - call G("except2", "throw2", 4) - Xpath 16384 " X: 0 - catch /^except2/ - Xpath 32768 " X: 0 - catch /^throw2$/ - Xpath 65536 " X: 65536 - catch /.*/ - Xpath 131072 " X: 0 - Xout "throw2:" v:exception "in" v:throwpoint - endtry - - try - Xpath 262144 " X: 262144 - let var = G("except3", "throw3", 5) - Xpath 524288 " X: 0 - catch /^except3/ - Xpath 1048576 " X: 0 - catch /^throw3$/ - Xpath 2097152 " X: 2097152 - catch /.*/ - Xpath 4194304 " X: 0 - Xout "throw3:" v:exception "in" v:throwpoint - finally - unlet! var - endtry - - let expected = "F1T1F2T2T3G4T4G5T5" - if taken != expected - Xpath 8388608 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /^0$/ " default return value - Xpath 16777216 " X: 0 - Xout v:throwpoint -catch /.*/ - Xpath 33554432 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 67108864 " X: 67108864 - -unlet taken expected -delfunction THROW -delfunction F -delfunction G - -Xcheck 69544277 - - -"------------------------------------------------------------------------------- -" Test 71: :throw across :echo variants and :execute {{{1 -" -" On an :echo, :echon, :echomsg, :echoerr, or :execute command, an -" exception might be thrown during evaluation of the arguments to -" be displayed or executed as a command, respectively. Any following -" arguments are not evaluated, then. The exception can be caught by -" the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! F(n) - let g:taken = g:taken . "F" . a:n - return "F" . a:n -endfunction - -try - try - Xpath 1 " X: 1 - echo "echo" . THROW("echo-except", 1) F(1) - Xpath 2 " X: 0 - catch /^echo-except$/ - Xpath 4 " X: 4 - catch /.*/ - Xpath 8 " X: 0 - Xout "echo:" v:exception "in" v:throwpoint - endtry - - try - Xpath 16 " X: 16 - echon "echon" . THROW("echon-except", 2) F(2) - Xpath 32 " X: 0 - catch /^echon-except$/ - Xpath 64 " X: 64 - catch /.*/ - Xpath 128 " X: 0 - Xout "echon:" v:exception "in" v:throwpoint - endtry - - try - Xpath 256 " X: 256 - echomsg "echomsg" . THROW("echomsg-except", 3) F(3) - Xpath 512 " X: 0 - catch /^echomsg-except$/ - Xpath 1024 " X: 1024 - catch /.*/ - Xpath 2048 " X: 0 - Xout "echomsg:" v:exception "in" v:throwpoint - endtry - - try - Xpath 4096 " X: 4096 - echoerr "echoerr" . THROW("echoerr-except", 4) F(4) - Xpath 8192 " X: 0 - catch /^echoerr-except$/ - Xpath 16384 " X: 16384 - catch /Vim/ - Xpath 32768 " X: 0 - catch /echoerr/ - Xpath 65536 " X: 0 - catch /.*/ - Xpath 131072 " X: 0 - Xout "echoerr:" v:exception "in" v:throwpoint - endtry - - try - Xpath 262144 " X: 262144 - execute "echo 'execute" . THROW("execute-except", 5) F(5) "'" - Xpath 524288 " X: 0 - catch /^execute-except$/ - Xpath 1048576 " X: 1048576 - catch /.*/ - Xpath 2097152 " X: 0 - Xout "execute:" v:exception "in" v:throwpoint - endtry - - let expected = "T1T2T3T4T5" - if taken != expected - Xpath 4194304 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /^0$/ " default return value - Xpath 8388608 " X: 0 - Xout v:throwpoint -catch /.*/ - Xpath 16777216 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 33554432 " X: 33554432 - -unlet taken expected -delfunction THROW -delfunction F - -Xcheck 34886997 - - -"------------------------------------------------------------------------------- -" Test 72: :throw across :let or :unlet {{{1 -" -" On a :let command, an exception might be thrown during evaluation -" of the expression to assign. On an :let or :unlet command, the -" evaluation of the name of the variable to be assigned or list or -" deleted, respectively, may throw an exception. Any following -" arguments are not evaluated, then. The exception can be caught by -" the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let throwcount = 0 - -function! THROW(x) - let g:throwcount = g:throwcount + 1 - throw a:x -endfunction - -try - try - let $VAR = "old_value" - Xpath 1 " X: 1 - let $VAR = "let(" . THROW("var") . ")" - Xpath 2 " X: 0 - catch /^var$/ - Xpath 4 " X: 4 - finally - if $VAR != "old_value" - Xpath 8 " X: 0 - endif - endtry - - try - let @a = "old_value" - Xpath 16 " X: 16 - let @a = "let(" . THROW("reg") . ")" - Xpath 32 " X: 0 - catch /^reg$/ - try - Xpath 64 " X: 64 - let @A = "let(" . THROW("REG") . ")" - Xpath 128 " X: 0 - catch /^REG$/ - Xpath 256 " X: 256 - endtry - finally - if @a != "old_value" - Xpath 512 " X: 0 - endif - if @A != "old_value" - Xpath 1024 " X: 0 - endif - endtry - - try - let saved_gpath = &g:path - let saved_lpath = &l:path - Xpath 2048 " X: 2048 - let &path = "let(" . THROW("opt") . ")" - Xpath 4096 " X: 0 - catch /^opt$/ - try - Xpath 8192 " X: 8192 - let &g:path = "let(" . THROW("gopt") . ")" - Xpath 16384 " X: 0 - catch /^gopt$/ - try - Xpath 32768 " X: 32768 - let &l:path = "let(" . THROW("lopt") . ")" - Xpath 65536 " X: 0 - catch /^lopt$/ - Xpath 131072 " X: 131072 - endtry - endtry - finally - if &g:path != saved_gpath || &l:path != saved_lpath - Xpath 262144 " X: 0 - endif - let &g:path = saved_gpath - let &l:path = saved_lpath - endtry - - unlet! var1 var2 var3 - - try - Xpath 524288 " X: 524288 - let var1 = "let(" . THROW("var1") . ")" - Xpath 1048576 " X: 0 - catch /^var1$/ - Xpath 2097152 " X: 2097152 - finally - if exists("var1") - Xpath 4194304 " X: 0 - endif - endtry - - try - let var2 = "old_value" - Xpath 8388608 " X: 8388608 - let var2 = "let(" . THROW("var2"). ")" - Xpath 16777216 " X: 0 - catch /^var2$/ - Xpath 33554432 " X: 33554432 - finally - if var2 != "old_value" - Xpath 67108864 " X: 0 - endif - endtry - - try - Xpath 134217728 " X: 134217728 - let var{THROW("var3")} = 4711 - Xpath 268435456 " X: 0 - catch /^var3$/ - Xpath 536870912 " X: 536870912 - endtry - - let addpath = "" - - function ADDPATH(p) - let g:addpath = g:addpath . a:p - endfunction - - try - call ADDPATH("T1") - let var{THROW("var4")} var{ADDPATH("T2")} | call ADDPATH("T3") - call ADDPATH("T4") - catch /^var4$/ - call ADDPATH("T5") - endtry - - try - call ADDPATH("T6") - unlet var{THROW("var5")} var{ADDPATH("T7")} | call ADDPATH("T8") - call ADDPATH("T9") - catch /^var5$/ - call ADDPATH("T10") - endtry - - if addpath != "T1T5T6T10" || throwcount != 11 - throw "addpath: " . addpath . ", throwcount: " . throwcount - endif - - Xpath 1073741824 " X: 1073741824 - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet! var1 var2 var3 addpath throwcount -delfunction THROW - -Xcheck 1789569365 - - -"------------------------------------------------------------------------------- -" Test 73: :throw across :function, :delfunction {{{1 -" -" The :function and :delfunction commands may cause an expression -" specified in braces to be evaluated. During evaluation, an -" exception might be thrown. The exception can be caught by the -" script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! EXPR(x, n) - let g:taken = g:taken . "E" . a:n - if a:n % 2 == 0 - call THROW(a:x, a:n) - endif - return 2 - a:n % 2 -endfunction - -try - try - " Define function. - Xpath 1 " X: 1 - function! F0() - endfunction - Xpath 2 " X: 2 - function! F{EXPR("function-def-ok", 1)}() - endfunction - Xpath 4 " X: 4 - function! F{EXPR("function-def", 2)}() - endfunction - Xpath 8 " X: 0 - catch /^function-def-ok$/ - Xpath 16 " X: 0 - catch /^function-def$/ - Xpath 32 " X: 32 - catch /.*/ - Xpath 64 " X: 0 - Xout "def:" v:exception "in" v:throwpoint - endtry - - try - " List function. - Xpath 128 " X: 128 - function F0 - Xpath 256 " X: 256 - function F{EXPR("function-lst-ok", 3)} - Xpath 512 " X: 512 - function F{EXPR("function-lst", 4)} - Xpath 1024 " X: 0 - catch /^function-lst-ok$/ - Xpath 2048 " X: 0 - catch /^function-lst$/ - Xpath 4096 " X: 4096 - catch /.*/ - Xpath 8192 " X: 0 - Xout "lst:" v:exception "in" v:throwpoint - endtry - - try - " Delete function - Xpath 16384 " X: 16384 - delfunction F0 - Xpath 32768 " X: 32768 - delfunction F{EXPR("function-del-ok", 5)} - Xpath 65536 " X: 65536 - delfunction F{EXPR("function-del", 6)} - Xpath 131072 " X: 0 - catch /^function-del-ok$/ - Xpath 262144 " X: 0 - catch /^function-del$/ - Xpath 524288 " X: 524288 - catch /.*/ - Xpath 1048576 " X: 0 - Xout "del:" v:exception "in" v:throwpoint - endtry - - let expected = "E1E2T2E3E4T4E5E6T6" - if taken != expected - Xpath 2097152 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /.*/ - Xpath 4194304 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -Xpath 8388608 " X: 8388608 - -unlet taken expected -delfunction THROW -delfunction EXPR - -Xcheck 9032615 - - -"------------------------------------------------------------------------------- -" Test 74: :throw across builtin functions and commands {{{1 -" -" Some functions like exists(), searchpair() take expression -" arguments, other functions or commands like substitute() or -" :substitute cause an expression (specified in the regular -" expression) to be evaluated. During evaluation an exception -" might be thrown. The exception can be caught by the script. -"------------------------------------------------------------------------------- - -XpathINIT - -let taken = "" - -function! THROW(x, n) - let g:taken = g:taken . "T" . a:n - throw a:x -endfunction - -function! EXPR(x, n) - let g:taken = g:taken . "E" . a:n - call THROW(a:x . a:n, a:n) - return "EXPR" -endfunction - -function! SKIP(x, n) - let g:taken = g:taken . "S" . a:n . "(" . line(".") - let theline = getline(".") - if theline =~ "skip" - let g:taken = g:taken . "s)" - return 1 - elseif theline =~ "throw" - let g:taken = g:taken . "t)" - call THROW(a:x . a:n, a:n) - else - let g:taken = g:taken . ")" - return 0 - endif -endfunction - -function! SUBST(x, n) - let g:taken = g:taken . "U" . a:n . "(" . line(".") - let theline = getline(".") - if theline =~ "not" " SUBST() should not be called for this line - let g:taken = g:taken . "n)" - call THROW(a:x . a:n, a:n) - elseif theline =~ "throw" - let g:taken = g:taken . "t)" - call THROW(a:x . a:n, a:n) - else - let g:taken = g:taken . ")" - return "replaced" - endif -endfunction - -try - try - Xpath 1 " X: 1 - let result = exists('*{EXPR("exists", 1)}') - Xpath 2 " X: 0 - catch /^exists1$/ - Xpath 4 " X: 4 - try - let result = exists('{EXPR("exists", 2)}') - Xpath 8 " X: 0 - catch /^exists2$/ - Xpath 16 " X: 16 - catch /.*/ - Xpath 32 " X: 0 - Xout "exists2:" v:exception "in" v:throwpoint - endtry - catch /.*/ - Xpath 64 " X: 0 - Xout "exists1:" v:exception "in" v:throwpoint - endtry - - try - let file = tempname() - exec "edit" file - insert -begin - xx -middle 3 - xx -middle 5 skip - xx -middle 7 throw - xx -end -. - normal! gg - Xpath 128 " X: 128 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 3)') - Xpath 256 " X: 256 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 4)') - Xpath 512 " X: 0 - let result = - \ searchpair("begin", "middle", "end", '', 'SKIP("searchpair", 5)') - Xpath 1024 " X: 0 - catch /^searchpair[35]$/ - Xpath 2048 " X: 0 - catch /^searchpair4$/ - Xpath 4096 " X: 4096 - catch /.*/ - Xpath 8192 " X: 0 - Xout "searchpair:" v:exception "in" v:throwpoint - finally - bwipeout! - call delete(file) - endtry - - try - let file = tempname() - exec "edit" file - insert -subst 1 -subst 2 -not -subst 4 -subst throw -subst 6 -. - normal! gg - Xpath 16384 " X: 16384 - 1,2substitute/subst/\=SUBST("substitute", 6)/ - try - Xpath 32768 " X: 32768 - try - let v:errmsg = "" - 3substitute/subst/\=SUBST("substitute", 7)/ - finally - if v:errmsg != "" - " If exceptions are not thrown on errors, fake the error - " exception in order to get the same execution path. - throw "faked Vim(substitute)" - endif - endtry - catch /Vim(substitute)/ " Pattern not found ('e' flag missing) - Xpath 65536 " X: 65536 - 3substitute/subst/\=SUBST("substitute", 8)/e - Xpath 131072 " X: 131072 - endtry - Xpath 262144 " X: 262144 - 4,6substitute/subst/\=SUBST("substitute", 9)/ - Xpath 524288 " X: 0 - catch /^substitute[678]/ - Xpath 1048576 " X: 0 - catch /^substitute9/ - Xpath 2097152 " X: 2097152 - finally - bwipeout! - call delete(file) - endtry - - try - Xpath 4194304 " X: 4194304 - let var = substitute("sub", "sub", '\=THROW("substitute()y", 10)', '') - Xpath 8388608 " X: 0 - catch /substitute()y/ - Xpath 16777216 " X: 16777216 - catch /.*/ - Xpath 33554432 " X: 0 - Xout "substitute()y:" v:exception "in" v:throwpoint - endtry - - try - Xpath 67108864 " X: 67108864 - let var = substitute("not", "sub", '\=THROW("substitute()n", 11)', '') - Xpath 134217728 " X: 134217728 - catch /substitute()n/ - Xpath 268435456 " X: 0 - catch /.*/ - Xpath 536870912 " X: 0 - Xout "substitute()n:" v:exception "in" v:throwpoint - endtry - - let expected = "E1T1E2T2S3(3)S4(5s)S4(7t)T4U6(1)U6(2)U9(4)U9(5t)T9T10" - if taken != expected - Xpath 1073741824 " X: 0 - Xout "'taken' is" taken "instead of" expected - endif - -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -endtry - -unlet result var taken expected -delfunction THROW -delfunction EXPR -delfunction SKIP -delfunction SUBST - -Xcheck 224907669 - - -"------------------------------------------------------------------------------- -" Test 75: Errors in builtin functions. {{{1 -" -" On an error in a builtin function called inside a :try/:endtry -" region, the evaluation of the expression calling that function and -" the command containing that expression are abandoned. The error can -" be caught as an exception. -" -" A simple :call of the builtin function is a trivial case. If the -" builtin function is called in the argument list of another function, -" no further arguments are evaluated, and the other function is not -" executed. If the builtin function is called from the argument of -" a :return command, the :return command is not executed. If the -" builtin function is called from the argument of a :throw command, -" the :throw command is not executed. The evaluation of the -" expression calling the builtin function is abandoned. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F1(arg1) - Xpath 1 " X: 0 -endfunction - -function! F2(arg1, arg2) - Xpath 2 " X: 0 -endfunction - -function! G() - Xpath 4 " X: 0 -endfunction - -function! H() - Xpath 8 " X: 0 -endfunction - -function! R() - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 16 " X: 16 - return append(1, "s") - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 32 " X: 0 - finally - Xpath 64 " X: 64 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 128 " X: 128 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - Xpath 256 " X: 256 -endfunction - -try - set noma " let append() fail with "E21" - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 512 " X: 512 - call append(1, "s") - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 1024 " X: 0 - finally - Xpath 2048 " X: 2048 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 4096 " X: 4096 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 8192 " X: 8192 - call F1('x' . append(1, "s")) - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 16384 " X: 0 - finally - Xpath 32768 " X: 32768 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 65536 " X: 65536 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 131072 " X: 131072 - call F2('x' . append(1, "s"), G()) - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 262144 " X: 0 - finally - Xpath 524288 " X: 524288 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 1048576 " X: 1048576 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - call R() - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 2097152 " X: 2097152 - throw "T" . append(1, "s") - catch /E21/ - let caught = 1 - catch /^T.*/ - Xpath 4194304 " X: 0 - catch /.*/ - Xpath 8388608 " X: 0 - finally - Xpath 16777216 " X: 16777216 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 33554432 " X: 33554432 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile - - while 1 - try - let caught = 0 - let v:errmsg = "" - Xpath 67108864 " X: 67108864 - let x = "a" - let x = x . "b" . append(1, "s") . H() - catch /E21/ - let caught = 1 - catch /.*/ - Xpath 134217728 " X: 0 - finally - Xpath 268435456 " X: 268435456 - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' - Xpath 536870912 " X: 536870912 - endif - if x == "a" - Xpath 1073741824 " X: 1073741824 - endif - break " discard error for $VIMNOERRTHROW - endtry - endwhile -catch /.*/ - " The Xpath command does not accept 2^31 (negative); add explicitly: - let Xpath = Xpath + 2147483648 " X: 0 - Xout v:exception "in" v:throwpoint -finally - set ma& -endtry - -unlet! caught x -delfunction F1 -delfunction F2 -delfunction G -delfunction H -delfunction R - -Xcheck 2000403408 +" Tests 69 to 75 were moved to test_trycatch.vim +let Xtest = 76 "------------------------------------------------------------------------------- @@ -8780,36 +6523,9 @@ delfunction F Xout "No Crash for vimgrep on BufUnload" Xcheck 0 -"------------------------------------------------------------------------------- -" Test 87 using (expr) ? funcref : funcref {{{1 -" -" Vim needs to correctly parse the funcref and even when it does -" not execute the funcref, it needs to consume the trailing () -"------------------------------------------------------------------------------- - -XpathINIT - -func Add2(x1, x2) - return a:x1 + a:x2 -endfu - -func GetStr() - return "abcdefghijklmnopqrstuvwxyp" -endfu +" Test 87 was moved to test_vimscript.vim +let Xtest = 88 -echo function('Add2')(2,3) - -Xout 1 ? function('Add2')(1,2) : function('Add2')(2,3) -Xout 0 ? function('Add2')(1,2) : function('Add2')(2,3) -" Make sure, GetStr() still works. -Xout GetStr()[0:10] - - -delfunction GetStr -delfunction Add2 -Xout "Successfully executed funcref Add2" - -Xcheck 0 "------------------------------------------------------------------------------- " Test 88: $VIMNOERRTHROW and $VIMNOINTTHROW support {{{1 diff --git a/src/nvim/testdir/test_backspace_opt.vim b/src/nvim/testdir/test_backspace_opt.vim new file mode 100644 index 0000000000..d680b442db --- /dev/null +++ b/src/nvim/testdir/test_backspace_opt.vim @@ -0,0 +1,151 @@ +" Tests for 'backspace' settings + +func Exec(expr) + let str='' + try + exec a:expr + catch /.*/ + let str=v:exception + endtry + return str +endfunc + +func Test_backspace_option() + set backspace= + call assert_equal('', &backspace) + set backspace=indent + call assert_equal('indent', &backspace) + set backspace=eol + call assert_equal('eol', &backspace) + set backspace=start + call assert_equal('start', &backspace) + set backspace=nostop + call assert_equal('nostop', &backspace) + " Add the value + set backspace= + set backspace=indent + call assert_equal('indent', &backspace) + set backspace+=eol + call assert_equal('indent,eol', &backspace) + set backspace+=start + call assert_equal('indent,eol,start', &backspace) + set backspace+=nostop + call assert_equal('indent,eol,start,nostop', &backspace) + " Delete the value + set backspace-=nostop + call assert_equal('indent,eol,start', &backspace) + set backspace-=indent + call assert_equal('eol,start', &backspace) + set backspace-=start + call assert_equal('eol', &backspace) + set backspace-=eol + call assert_equal('', &backspace) + " Check the error + call assert_equal(0, match(Exec('set backspace=ABC'), '.*E474')) + call assert_equal(0, match(Exec('set backspace+=def'), '.*E474')) + " NOTE: Vim doesn't check following error... + "call assert_equal(0, match(Exec('set backspace-=ghi'), '.*E474')) + + " Check backwards compatibility with version 5.4 and earlier + set backspace=0 + call assert_equal('0', &backspace) + set backspace=1 + call assert_equal('1', &backspace) + set backspace=2 + call assert_equal('2', &backspace) + set backspace=3 + call assert_equal('3', &backspace) + call assert_false(match(Exec('set backspace=4'), '.*E474')) + call assert_false(match(Exec('set backspace=10'), '.*E474')) + + " Cleared when 'compatible' is set + " set compatible + " call assert_equal('', &backspace) + set nocompatible viminfo+=nviminfo +endfunc + +" Test with backspace set to the non-compatible setting +func Test_backspace_ctrl_u() + new + call append(0, [ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted", + \ "5 this shouldn't be deleted", + \ "6 this shouldn't be deleted", + \ "7 this shouldn't be deleted", + \ "8 this shouldn't be deleted (not touched yet)"]) + call cursor(2, 1) + + " set compatible + set backspace=2 + + exe "normal Avim1\<C-U>\<Esc>\<CR>" + exe "normal Avim2\<C-G>u\<C-U>\<Esc>\<CR>" + + set cpo-=< + inoremap <c-u> <left><c-u> + exe "normal Avim3\<C-U>\<Esc>\<CR>" + iunmap <c-u> + exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>" + + " Test with backspace set to the compatible setting + set backspace= visualbell + exe "normal A vim5\<Esc>A\<C-U>\<C-U>\<Esc>\<CR>" + exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>" + + inoremap <c-u> <left><c-u> + exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>" + + call assert_equal([ + \ "1 this shouldn't be deleted", + \ "2 this shouldn't be deleted", + \ "3 this shouldn't be deleted", + \ "4 this should be deleted3", + \ "", + \ "6 this shouldn't be deleted vim5", + \ "7 this shouldn't be deleted vim6", + \ "8 this shouldn't be deleted (not touched yet) vim7", + \ ""], getline(1, '$')) + + " Reset values + set compatible&vim + set visualbell&vim + set backspace&vim + + " Test new nostop option + %d_ + let expected = "foo bar foobar" + call setline(1, expected) + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1)) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1)) + + let content = getline(1) + for value in ['indent,nostop', 'eol,nostop', 'indent,eol,nostop', 'indent,eol,start,nostop'] + exe ":set bs=".. value + %d _ + call setline(1, content) + let expected = " foobar" + call cursor(1, 8) + exe ":norm! ianotherone\<c-u>" + call assert_equal(expected, getline(1), 'CTRL-U backspace value: '.. &bs) + let expected = "foo foobar" + call setline(1, content) + call cursor(1, 8) + exe ":norm! ianothertwo\<c-w>" + call assert_equal(expected, getline(1), 'CTRL-W backspace value: '.. &bs) + endfor + + " Reset options + set compatible&vim + set visualbell&vim + set backspace&vim + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index 3377f86126..53704bd094 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -61,6 +61,15 @@ func Test_client_server() call assert_fails('call remote_send("XXX", ":let testvar = ''yes''\<CR>")', 'E241') + call writefile(['one'], 'Xclientfile') + let cmd = GetVimProg() .. ' --servername ' .. name .. ' --remote Xclientfile' + call system(cmd) + call WaitForAssert({-> assert_equal('Xclientfile', remote_expr(name, "bufname()", "", 2))}) + call WaitForAssert({-> assert_equal('one', remote_expr(name, "getline(1)", "", 2))}) + call writefile(['one', 'two'], 'Xclientfile') + call system(cmd) + call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))}) + " Expression evaluated locally. if v:servername == '' call remote_startserver('MYSELF') diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 81f653c393..39f865144a 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -51,6 +51,22 @@ func Test_complete_wildmenu() call feedkeys(":e Xdir1/\<Tab>\<Down>\<Up>\<Right>\<CR>", 'tx') call assert_equal('testfile1', getline(1)) + " this fails in some Unix GUIs, not sure why + if !has('unix') || !has('gui_running') + " <C-J>/<C-K> mappings to go up/down directories when 'wildcharm' is + " different than 'wildchar'. + set wildcharm=<C-Z> + cnoremap <C-J> <Down><C-Z> + cnoremap <C-K> <Up><C-Z> + call feedkeys(":e Xdir1/\<Tab>\<C-J>\<CR>", 'tx') + call assert_equal('testfile3', getline(1)) + call feedkeys(":e Xdir1/\<Tab>\<C-J>\<C-K>\<CR>", 'tx') + call assert_equal('testfile1', getline(1)) + set wildcharm=0 + cunmap <C-J> + cunmap <C-K> + endif + " cleanup %bwipe call delete('Xdir1/Xdir2/Xtestfile4') @@ -62,6 +78,33 @@ func Test_complete_wildmenu() set nowildmenu endfunc +func Test_wildmenu_screendump() + CheckScreendump + + let lines =<< trim [SCRIPT] + set wildmenu hlsearch + [SCRIPT] + call writefile(lines, 'XTest_wildmenu') + + let buf = RunVimInTerminal('-S XTest_wildmenu', {'rows': 8}) + call term_sendkeys(buf, ":vim\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_1', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_2', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_3', {}) + + call term_sendkeys(buf, "\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_4', {}) + call term_sendkeys(buf, "\<Esc>") + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_wildmenu') +endfunc + func Test_map_completion() if !has('cmdline_compl') return @@ -548,6 +591,13 @@ func Test_cmdline_complete_user_names() endif endfunc +func Test_cmdline_complete_bang() + if executable('whoami') + call feedkeys(":!whoam\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^".*\<whoami\>', @:) + endif +endfunc + funct Test_cmdline_complete_languages() let lang = substitute(execute('language messages'), '.*"\(.*\)"$', '\1', '') @@ -570,6 +620,17 @@ funct Test_cmdline_complete_languages() endif endfunc +func Test_cmdline_complete_expression() + let g:SomeVar = 'blah' + for cmd in ['exe', 'echo', 'echon', 'echomsg'] + call feedkeys(":" .. cmd .. " SomeV\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"' .. cmd .. ' SomeVar', @:) + call feedkeys(":" .. cmd .. " foo SomeV\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_match('"' .. cmd .. ' foo SomeVar', @:) + endfor + unlet g:SomeVar +endfunc + func Test_cmdline_write_alternatefile() new call setline('.', ['one', 'two']) @@ -819,6 +880,36 @@ func Test_cmdwin_cedit() delfunc CmdWinType endfunc +func Test_cmdwin_restore() + CheckScreendump + + let lines =<< trim [SCRIPT] + call setline(1, range(30)) + 2split + [SCRIPT] + call writefile(lines, 'XTest_restore') + + let buf = RunVimInTerminal('-S XTest_restore', {'rows': 12}) + call term_wait(buf, 100) + call term_sendkeys(buf, "q:") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_1', {}) + + " normal restore + call term_sendkeys(buf, ":q\<CR>") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_2', {}) + + " restore after setting 'lines' with one window + call term_sendkeys(buf, ":close\<CR>") + call term_sendkeys(buf, "q:") + call term_sendkeys(buf, ":set lines=18\<CR>") + call term_sendkeys(buf, ":q\<CR>") + call VerifyScreenDump(buf, 'Test_cmdwin_restore_3', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_restore') +endfunc + func Test_buffers_lastused() " check that buffers are sorted by time when wildmode has lastused edit bufc " oldest @@ -894,5 +985,23 @@ func Test_zero_line_search() q! endfunc +func Test_read_shellcmd() + CheckUnix + if executable('ls') + " There should be ls in the $PATH + call feedkeys(":r! l\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_match('^"r! .*\<ls\>', @:) + endif + + if executable('rm') + call feedkeys(":r! ++enc=utf-8 r\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_notmatch('^"r!.*\<runtest.vim\>', @:) + call assert_match('^"r!.*\<rm\>', @:) + + call feedkeys(":r ++enc=utf-8 !rm\<c-a>\<c-b>\"\<cr>", 'tx') + call assert_notmatch('^"r.*\<runtest.vim\>', @:) + call assert_match('^"r ++enc\S\+ !.*\<rm\>', @:) + endif +endfunc -" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_const.vim b/src/nvim/testdir/test_const.vim index fc7ea71f6e..ea69c8cba4 100644 --- a/src/nvim/testdir/test_const.vim +++ b/src/nvim/testdir/test_const.vim @@ -109,7 +109,7 @@ func Test_define_script_var_with_lock() unlet s:x endfunc -func Test_descructuring_with_lock() +func Test_destructuring_with_lock() const [a, b, c] = [1, 1.1, 'vim'] call assert_fails('let a = 1', 'E741:') diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 9eea27740d..b6d9687560 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -1,8 +1,8 @@ " Tests for digraphs -if !has("digraphs") - finish -endif +source check.vim +CheckFeature digraphs +source term_util.vim func Put_Dig(chars) exe "norm! o\<c-k>".a:chars @@ -487,4 +487,20 @@ func Test_show_digraph_cp1251() bwipe! endfunc +" Test for the characters displayed on the screen when entering a digraph +func Test_entering_digraph() + CheckRunVimInTerminal + let buf = RunVimInTerminal('', {'rows': 6}) + call term_sendkeys(buf, "i\<C-K>") + call term_wait(buf) + call assert_equal('?', term_getline(buf, 1)) + call term_sendkeys(buf, "1") + call term_wait(buf) + call assert_equal('1', term_getline(buf, 1)) + call term_sendkeys(buf, "2") + call term_wait(buf) + call assert_equal('½', term_getline(buf, 1)) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 4a027c3864..20508b12d3 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -1,5 +1,7 @@ " Tests for various Ex commands. +source check.vim + func Test_ex_delete() new call setline(1, ['a', 'b', 'c']) @@ -9,6 +11,17 @@ func Test_ex_delete() call assert_equal(['a', 'c'], getline(1, 2)) endfunc +func Test_range_error() + call assert_fails(':.echo 1', 'E481:') + call assert_fails(':$echo 1', 'E481:') + call assert_fails(':1,2echo 1', 'E481:') + call assert_fails(':+1echo 1', 'E481:') + call assert_fails(':/1/echo 1', 'E481:') + call assert_fails(':\/echo 1', 'E481:') + normal vv + call assert_fails(":'<,'>echo 1", 'E481:') +endfunc + func Test_buffers_lastused() edit bufc " oldest @@ -40,3 +53,81 @@ func Test_buffers_lastused() bwipeout bufb bwipeout bufc endfunc + +" Test for the :confirm command dialog +func Test_confirm_cmd() + CheckNotGui + CheckRunVimInTerminal + call writefile(['foo1'], 'foo') + call writefile(['bar1'], 'bar') + " Test for saving all the modified buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo2')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar2')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "A") + call StopVimInTerminal(buf) + call assert_equal(['foo2'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + " Test for discarding all the changes to modified buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo3')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar3')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "D") + call StopVimInTerminal(buf) + call assert_equal(['foo2'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + " Test for saving and discarding changes to some buffers + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new foo\n") + call term_sendkeys(buf, ":call setline(1, 'foo4')\n") + call term_sendkeys(buf, ":new bar\n") + call term_sendkeys(buf, ":call setline(1, 'bar4')\n") + call term_sendkeys(buf, ":wincmd b\n") + call term_sendkeys(buf, ":confirm qall\n") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "N") + call WaitForAssert({-> assert_match('\[Y\]es, (N)o, (C)ancel: ', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "Y") + call StopVimInTerminal(buf) + call assert_equal(['foo4'], readfile('foo')) + call assert_equal(['bar2'], readfile('bar')) + + call delete('foo') + call delete('bar') +endfunc + +func Test_confirm_cmd_cancel() + CheckNotGui + CheckRunVimInTerminal + + " Test for closing a window with a modified buffer + let buf = RunVimInTerminal('', {'rows': 20}) + call term_sendkeys(buf, ":set nomore\n") + call term_sendkeys(buf, ":new\n") + call term_sendkeys(buf, ":call setline(1, 'abc')\n") + call term_sendkeys(buf, ":confirm close\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "C") + call WaitForAssert({-> assert_equal('', term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, ":confirm close\n") + call WaitForAssert({-> assert_match('^\[Y\]es, (N)o, (C)ancel: *$', + \ term_getline(buf, 20))}, 1000) + call term_sendkeys(buf, "N") + call WaitForAssert({-> assert_match('^ *0,0-1 All$', + \ term_getline(buf, 20))}, 1000) + call StopVimInTerminal(buf) +endfunc diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim index 8dc25f62b1..465613f1cf 100644 --- a/src/nvim/testdir/test_fileformat.vim +++ b/src/nvim/testdir/test_fileformat.vim @@ -31,3 +31,26 @@ func Test_fileformat_autocommand() au! BufReadPre Xfile bw! endfunc + +" Test for changing the fileformat using ++read +func Test_fileformat_plusplus_read() + new + call setline(1, ['one', 'two', 'three']) + w ++ff=dos Xfile1 + enew! + set ff=unix + " A :read doesn't change the fileformat, but does apply to the read lines. + r ++fileformat=unix Xfile1 + call assert_equal('unix', &fileformat) + call assert_equal("three\r", getline('$')) + 3r ++edit Xfile1 + call assert_equal('dos', &fileformat) + close! + call delete('Xfile1') + set fileformat& + call assert_fails('e ++fileformat Xfile1', 'E474:') + call assert_fails('e ++ff=abc Xfile1', 'E474:') + call assert_fails('e ++abc1 Xfile1', 'E474:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 52b5884c8b..1cb68e7fef 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -150,6 +150,7 @@ let s:filename_checks = { \ 'dsl': ['file.dsl'], \ 'dtd': ['file.dtd'], \ 'dts': ['file.dts', 'file.dtsi'], + \ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'], \ 'dylan': ['file.dylan'], \ 'dylanintr': ['file.intr'], \ 'dylanlid': ['file.lid'], @@ -181,6 +182,7 @@ let s:filename_checks = { \ 'gdb': ['.gdbinit'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt'], + \ 'gift': ['file.gift'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'], \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig'], \ 'gitolite': ['gitolite.conf'], @@ -321,9 +323,10 @@ let s:filename_checks = { \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'], \ 'nsis': ['file.nsi', 'file.nsh'], \ 'obj': ['file.obj'], - \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit'], + \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit', 'file.mlt', 'file.mlp', 'file.mlip', 'file.mli.cppo', 'file.ml.cppo'], \ 'occam': ['file.occ'], \ 'omnimark': ['file.xom', 'file.xin'], + \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'ora': ['file.ora'], \ 'pamconf': ['/etc/pam.conf'], @@ -397,6 +400,7 @@ let s:filename_checks = { \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], \ 'scilab': ['file.sci', 'file.sce'], \ 'screen': ['.screenrc', 'screenrc'], + \ 'sexplib': ['file.sexp'], \ 'scss': ['file.scss'], \ 'sd': ['file.sd'], \ 'sdc': ['file.sdc'], @@ -463,6 +467,7 @@ let s:filename_checks = { \ 'tilde': ['file.t.html'], \ 'tli': ['file.tli'], \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'], + \ 'toml': ['file.toml'], \ 'tpp': ['file.tpp'], \ 'treetop': ['file.treetop'], \ 'trustees': ['trustees.conf'], @@ -471,7 +476,6 @@ let s:filename_checks = { \ 'tssgm': ['file.tssgm'], \ 'tssop': ['file.tssop'], \ 'twig': ['file.twig'], - \ 'typescript': ['file.ts'], \ 'typescriptreact': ['file.tsx'], \ 'uc': ['file.uc'], \ 'udevconf': ['/etc/udev/udev.conf'], @@ -668,5 +672,22 @@ func Test_hook_file() filetype off endfunc +func Test_ts_file() + filetype on + + call writefile(['<?xml version="1.0" encoding="utf-8"?>'], 'Xfile.ts') + split Xfile.ts + call assert_equal('xml', &filetype) + bwipe! + + call writefile(['// looks like Typescript'], 'Xfile.ts') + split Xfile.ts + call assert_equal('typescript', &filetype) + bwipe! + + call delete('Xfile.hook') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim index a7bc135d47..0a00d9432f 100644 --- a/src/nvim/testdir/test_find_complete.vim +++ b/src/nvim/testdir/test_find_complete.vim @@ -15,22 +15,22 @@ func Test_find_complete() new set path= - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E345:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=. - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=.,, - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close new set path=./** - call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + call assert_fails('call feedkeys(":find \t\n", "xt")', 'E471:') close " We shouldn't find any file till this point diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index f5488a6a27..2195bf527e 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -184,3 +184,46 @@ func Test_finddir_error() call assert_fails('call finddir("x", "**x")', 'E343:') call assert_fails('call finddir("x", repeat("x", 5000))', 'E854:') endfunc + +" Test for the :find, :sfind and :tabfind commands +func Test_find_cmd() + new + let save_path = &path + let save_dir = getcwd() + set path=.,./**/* + call CreateFiles() + cd Xdir1 + " Test for :find + find foo + call assert_equal('foo', expand('%:.')) + 2find foo + call assert_equal('Xdir2/foo', expand('%:.')) + call assert_fails('3find foo', 'E347:') + " Test for :sfind + enew + sfind barfoo + call assert_equal('Xdir2/Xdir3/barfoo', expand('%:.')) + call assert_equal(3, winnr('$')) + close + call assert_fails('sfind baz', 'E345:') + call assert_equal(2, winnr('$')) + " Test for :tabfind + enew + tabfind foobar + call assert_equal('Xdir2/foobar', expand('%:.')) + call assert_equal(2, tabpagenr('$')) + tabclose + call assert_fails('tabfind baz', 'E345:') + call assert_equal(1, tabpagenr('$')) + " call chdir(save_dir) + exe 'cd ' . save_dir + call CleanFiles() + let &path = save_path + close + + call assert_fails('find', 'E471:') + call assert_fails('sfind', 'E471:') + call assert_fails('tabfind', 'E471:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 3c90c45952..88ce64b9eb 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -815,4 +815,11 @@ func Test_fold_create_delete_create() bwipe! endfunc +" this was crashing +func Test_fold_create_delete() + new + norm zFzFzdzj + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index bdeaf8e2cf..7ccf2812ff 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -1,3 +1,4 @@ +source check.vim func Test_yank_put_clipboard() new @@ -10,6 +11,16 @@ func Test_yank_put_clipboard() bwipe! endfunc +func Test_global_set_clipboard() + CheckFeature clipboard_working + new + set clipboard=unnamedplus + let @+='clipboard' | g/^/set cb= | let @" = 'unnamed' | put + call assert_equal(['','unnamed'], getline(1, '$')) + set clipboard& + bwipe! +endfunc + func Test_nested_global() new call setline(1, ['nothing', 'found', 'found bad', 'bad']) diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim index 6125f9b993..e390bd5cc8 100644 --- a/src/nvim/testdir/test_hardcopy.vim +++ b/src/nvim/testdir/test_hardcopy.vim @@ -1,5 +1,7 @@ " Test :hardcopy +source check.vim + func Test_printoptions() edit test_hardcopy.vim syn on @@ -8,8 +10,10 @@ func Test_printoptions() \ 'left:2in,top:30pt,right:16mm,bottom:3pc', \ 'header:3,syntax:y,number:y,wrap:n', \ 'header:3,syntax:n,number:y,wrap:y', + \ 'header:0,syntax:a,number:y,wrap:y', \ 'duplex:short,collate:n,jobsplit:y,portrait:n', \ 'duplex:long,collate:y,jobsplit:n,portrait:y', + \ 'duplex:off,collate:y,jobsplit:y,portrait:y', \ 'paper:10x14', \ 'paper:A3', \ 'paper:A4', @@ -28,7 +32,7 @@ func Test_printoptions() \ ''] exe 'set printoptions=' .. opt if has('postscript') - hardcopy > Xhardcopy_printoptions + 1,50hardcopy > Xhardcopy_printoptions let lines = readfile('Xhardcopy_printoptions') call assert_true(len(lines) > 20, opt) call assert_true(lines[0] =~ 'PS-Adobe', opt) @@ -44,8 +48,8 @@ func Test_printoptions() endfunc func Test_printmbfont() - " Print a small help page which contains tabs to cover code that expands tabs to spaces. - help help + " Print a help page which contains tabs, underlines (etc) to recover more code. + help syntax.txt syn on for opt in [':WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no', @@ -63,10 +67,39 @@ func Test_printmbfont() bwipe endfunc +func Test_printmbcharset() + CheckFeature postscript + + " digraph.txt has plenty of non-latin1 characters. + help digraph.txt + set printmbcharset=ISO10646 printencoding=utf-8 + for courier in ['yes', 'no'] + for ascii in ['yes', 'no'] + exe 'set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-BoldItalic' + \ .. ',c:' .. courier .. ',a:' .. ascii + hardcopy > Xhardcopy_printmbcharset + let lines = readfile('Xhardcopy_printmbcharset') + call assert_true(len(lines) > 20) + call assert_true(lines[0] =~ 'PS-Adobe') + endfor + endfor + + set printmbcharset=does-not-exist printencoding=utf-8 printmbfont=r:WadaMin-Regular + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E456:') + + set printmbcharset=GB_2312-80 printencoding=utf-8 printmbfont=r:WadaMin-Regular + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E673:') + + set printmbcharset=ISO10646 printencoding=utf-8 printmbfont= + call assert_fails('hardcopy > Xhardcopy_printmbcharset', 'E675:') + + call delete('Xhardcopy_printmbcharset') + set printmbcharset& printencoding& printmbfont& + bwipe +endfunc + func Test_printexpr() - if !has('unix') - return - endif + CheckFeature postscript " Not a very useful printexpr value, but enough to test " hardcopy with 'printexpr'. @@ -84,7 +117,7 @@ func Test_printexpr() \ readfile('Xhardcopy_printexpr')) call delete('Xhardcopy_printexpr') - " Function return 1 to test print failure. + " Function returns 1 to test print failure. function PrintFails(fname) call delete(a:fname) return 1 @@ -97,12 +130,11 @@ func Test_printexpr() endfunc func Test_errors() - " FIXME: Windows fails differently than Unix. - if has('unix') - edit test_hardcopy.vim - call assert_fails('hardcopy >', 'E324:') - bwipe - endif + CheckFeature postscript + + edit test_hardcopy.vim + call assert_fails('hardcopy >', 'E324:') + bwipe endfunc func Test_dark_background() @@ -126,12 +158,11 @@ func Test_dark_background() endfun func Test_empty_buffer() - " FIXME: Unclear why this fails on Windows. - if has('unix') - new - call assert_equal("\nNo text to be printed", execute('hardcopy')) - bwipe - endif + CheckFeature postscript + + new + call assert_equal("\nNo text to be printed", execute('hardcopy')) + bwipe endfunc func Test_printheader_parsing() @@ -145,9 +176,8 @@ func Test_printheader_parsing() endfunc func Test_fname_with_spaces() - if !has('postscript') - return - endif + CheckFeature postscript + split t\ e\ s\ t.txt call setline(1, ['just', 'some', 'text']) hardcopy > %.ps @@ -157,9 +187,11 @@ func Test_fname_with_spaces() endfunc func Test_illegal_byte() - if !has('postscript') || &enc != 'utf-8' + CheckFeature postscript + if &enc != 'utf-8' return endif + new " conversion of 0xff will fail, this used to cause a crash call setline(1, "\xff") @@ -168,3 +200,5 @@ func Test_illegal_byte() bwipe! call delete('Xpstest') endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 01fb9917e9..8e59efd22d 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -56,3 +56,49 @@ func Test_help_local_additions() call delete('Xruntime', 'rf') let &rtp = rtp_save endfunc + +" Test for the :helptags command +func Test_helptag_cmd() + call mkdir('Xdir/a/doc', 'p') + + " No help file to process in the directory + call assert_fails('helptags Xdir', 'E151:') + + call writefile([], 'Xdir/a/doc/sample.txt') + + " Test for ++t argument + helptags ++t Xdir + call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags')) + call delete('Xdir/tags') + + " The following tests fail on FreeBSD for some reason + if has('unix') && !has('bsd') + " Read-only tags file + call mkdir('Xdir/doc', 'p') + call writefile([''], 'Xdir/doc/tags') + call writefile([], 'Xdir/doc/sample.txt') + call setfperm('Xdir/doc/tags', 'r-xr--r--') + call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags')) + + let rtp = &rtp + let &rtp = 'Xdir' + helptags ALL + let &rtp = rtp + + call delete('Xdir/doc/tags') + + " No permission to read the help file + call setfperm('Xdir/a/doc/sample.txt', '-w-------') + call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt')) + call delete('Xdir/a/doc/sample.txt') + call delete('Xdir/tags') + endif + + " Duplicate tags in the help file + call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt') + call assert_fails('helptags Xdir', 'E154:') + + call delete('Xdir', 'rf') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 57a0a7aaf4..9435931d41 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -280,7 +280,7 @@ func Test_omni_dash() set omnifunc=Omni new exe "normal Gofind -\<C-x>\<C-o>" - call assert_equal("\n-\nmatch 1 of 2", execute(':2mess')) + call assert_equal("find -help", getline('$')) bwipe! delfunc Omni @@ -312,6 +312,24 @@ func Test_completefunc_args() delfunc CompleteFunc endfunc +func CompleteTest(findstart, query) + if a:findstart + return col('.') + endif + return ['matched'] +endfunc + +func Test_completefunc_info() + new + set completeopt=menuone + set completefunc=CompleteTest + call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx") + call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1)) + bwipe! + set completeopt& + set completefunc& +endfunc + " Check that when using feedkeys() typeahead does not interrupt searching for " completions. func Test_compl_feedkeys() @@ -450,3 +468,17 @@ func Test_pum_with_folds_two_tabs() call StopVimInTerminal(buf) call delete('Xpumscript') endfunc + +" Test to ensure 'Scanning...' messages are not recorded in messages history +func Test_z1_complete_no_history() + new + messages clear + let currmess = execute('messages') + setlocal dictionary=README.txt + exe "normal owh\<C-X>\<C-K>" + exe "normal owh\<C-N>" + call assert_equal(currmess, execute('messages')) + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 66df57ea39..2fd82a4b6d 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -171,6 +171,11 @@ func Test_delmarks() " Deleting an already deleted mark should not fail. delmarks x + " getpos() should return all zeros after deleting a filemark. + norm mA + delmarks A + call assert_equal([0, 0, 0, 0], getpos("'A")) + " Test deleting a range of marks. norm ma norm mb @@ -201,3 +206,28 @@ func Test_mark_error() call assert_fails('mark xx', 'E488:') call assert_fails('mark _', 'E191:') endfunc + +" Test for the getmarklist() function +func Test_getmarklist() + new + " global marks + delmarks A-Z 0-9 \" ^.[] + call assert_equal([], getmarklist()) + call setline(1, ['one', 'two', 'three']) + mark A + call cursor(3, 5) + normal mN + call assert_equal([{'file' : '', 'mark' : "'A", 'pos' : [bufnr(), 1, 1, 0]}, + \ {'file' : '', 'mark' : "'N", 'pos' : [bufnr(), 3, 5, 0]}], + \ getmarklist()) + " buffer local marks + delmarks! + call assert_equal([{'mark' : "''", 'pos' : [bufnr(), 1, 1, 0]}, + \ {'mark' : "'\"", 'pos' : [bufnr(), 1, 1, 0]}], getmarklist(bufnr())) + call cursor(2, 2) + normal mr + call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 2, 0]}, + \ getmarklist(bufnr())[0]) + call assert_equal([], getmarklist({})) + close! +endfunc diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 215065f941..f71da92bf8 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -128,6 +128,7 @@ func Test_mksession() call delete('Xtest_mks.out') call delete(tmpfile) let &wrap = wrap_save + set sessionoptions& endfunc func Test_mksession_winheight() @@ -154,7 +155,7 @@ func Test_mksession_rtp() return endif new - set sessionoptions+=options + set sessionoptions&vi let _rtp=&rtp " Make a real long (invalid) runtimepath value, " that should exceed PATH_MAX (hopefully) @@ -174,17 +175,23 @@ func Test_mksession_rtp() call assert_equal(expected, li) call delete('Xtest_mks.out') + set sessionoptions& endfunc -" Verify that arglist is stored correctly to the session file. func Test_mksession_arglist() %argdel next file1 file2 file3 file4 + new + next | next mksession! Xtest_mks.out source Xtest_mks.out call assert_equal(['file1', 'file2', 'file3', 'file4'], argv()) + call assert_equal(2, argidx()) + wincmd w + call assert_equal(0, argidx()) call delete('Xtest_mks.out') + enew | only argdel * endfunc @@ -218,6 +225,72 @@ func Test_mksession_one_buffer_two_windows() call delete('Xtest_mks.out') endfunc +func Test_mksession_lcd_multiple_tabs() + tabnew + tabnew + lcd . + tabfirst + lcd . + mksession! Xtest_mks.out + tabonly + source Xtest_mks.out + call assert_true(haslocaldir(), 'Tab 1 localdir') + tabnext 2 + call assert_true(!haslocaldir(), 'Tab 2 localdir') + tabnext 3 + call assert_true(haslocaldir(), 'Tab 3 localdir') + call delete('Xtest_mks.out') +endfunc + +func Test_mksession_blank_tabs() + tabnew + tabnew + tabnew + tabnext 3 + mksession! Xtest_mks.out + tabnew + tabnew + tabnext 2 + source Xtest_mks.out + call assert_equal(4, tabpagenr('$'), 'session restore should restore number of tabs') + call assert_equal(3, tabpagenr(), 'session restore should restore the active tab') + call delete('Xtest_mks.out') +endfunc + +func Test_mksession_blank_windows() + split + split + split + 3 wincmd w + mksession! Xtest_mks.out + split + split + 2 wincmd w + source Xtest_mks.out + call assert_equal(4, winnr('$'), 'session restore should restore number of windows') + call assert_equal(3, winnr(), 'session restore should restore the active window') + call delete('Xtest_mks.out') +endfunc + +if has('extra_search') + +func Test_mksession_hlsearch() + set sessionoptions&vi + set hlsearch + mksession! Xtest_mks.out + nohlsearch + source Xtest_mks.out + call assert_equal(1, v:hlsearch, 'session should restore search highlighting state') + nohlsearch + mksession! Xtest_mks.out + source Xtest_mks.out + call assert_equal(0, v:hlsearch, 'session should restore search highlighting state') + set sessionoptions& + call delete('Xtest_mks.out') +endfunc + +endif + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. @@ -335,24 +408,28 @@ func Test_mksession_globals() set sessionoptions+=globals " create different global variables - let g:Global_string = "Sun is shining" + let g:Global_string = "Sun is shining\r\n" let g:Global_count = 100 let g:Global_pi = 3.14 + let g:Global_neg_float = -2.68 mksession! Xtest_mks.out unlet g:Global_string unlet g:Global_count unlet g:Global_pi + unlet g:Global_neg_float source Xtest_mks.out - call assert_equal("Sun is shining", g:Global_string) + call assert_equal("Sun is shining\r\n", g:Global_string) call assert_equal(100, g:Global_count) call assert_equal(3.14, g:Global_pi) + call assert_equal(-2.68, g:Global_neg_float) unlet g:Global_string unlet g:Global_count unlet g:Global_pi + unlet g:Global_neg_float call delete('Xtest_mks.out') set sessionoptions& endfunc @@ -411,20 +488,123 @@ func Test_mksession_resize() for line in lines if line =~ '^set lines=' let found_resize = v:true + break endif endfor - call assert_equal(v:false, found_resize) + call assert_false(found_resize) let lines = readfile('Xtest_mks2.out') let found_resize = v:false for line in lines if line =~ '^set lines=' let found_resize = v:true + break + endif + endfor + call assert_true(found_resize) + + call delete('Xtest_mks1.out') + call delete('Xtest_mks2.out') + set sessionoptions& +endfunc + +" Test for mksession with a named scratch buffer +func Test_mksession_scratch() + set sessionoptions&vi + enew | only + file Xscratch + set buftype=nofile + mksession! Xtest_mks.out + %bwipe + source Xtest_mks.out + call assert_equal('Xscratch', bufname('')) + call assert_equal('nofile', &buftype) + %bwipe + call delete('Xtest_mks.out') + set sessionoptions& +endfunc + +" Test for mksession with fold options +func Test_mksession_foldopt() + set sessionoptions-=options + set sessionoptions+=folds + new + setlocal foldenable + setlocal foldmethod=expr + setlocal foldmarker=<<<,>>> + setlocal foldignore=% + setlocal foldlevel=2 + setlocal foldminlines=10 + setlocal foldnestmax=15 + mksession! Xtest_mks.out + close + %bwipe + + source Xtest_mks.out + call assert_true(&foldenable) + call assert_equal('expr', &foldmethod) + call assert_equal('<<<,>>>', &foldmarker) + call assert_equal('%', &foldignore) + call assert_equal(2, &foldlevel) + call assert_equal(10, &foldminlines) + call assert_equal(15, &foldnestmax) + + close + %bwipe + set sessionoptions& +endfunc + +" Test for mksession with window position +func Test_mksession_winpos() + if !has('gui_running') + " Only applicable in GUI Vim + return + endif + set sessionoptions+=winpos + mksession! Xtest_mks.out + let found_winpos = v:false + let lines = readfile('Xtest_mks.out') + for line in lines + if line =~ '^winpos ' + let found_winpos = v:true + break + endif + endfor + call assert_true(found_winpos) + call delete('Xtest_mks.out') + set sessionoptions& +endfunc + +" Test for mksession with 'compatible' option +func Test_mksession_compatible() + throw 'skipped: Nvim does not support "compatible" option' + mksession! Xtest_mks1.out + set compatible + mksession! Xtest_mks2.out + set nocp + + let test_success = v:false + let lines = readfile('Xtest_mks1.out') + for line in lines + if line =~ '^if &cp | set nocp | endif' + let test_success = v:true + break + endif + endfor + call assert_true(test_success) + + let test_success = v:false + let lines = readfile('Xtest_mks2.out') + for line in lines + if line =~ '^if !&cp | set cp | endif' + let test_success = v:true + break endif endfor - call assert_equal(v:true, found_resize) + call assert_true(test_success) call delete('Xtest_mks1.out') call delete('Xtest_mks2.out') + set compatible& set sessionoptions& endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index ad6d325510..7a846e5ea0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -222,6 +222,21 @@ func Test_normal05_formatexpr_setopt() set formatexpr= endfunc +" When 'formatexpr' returns non-zero, internal formatting is used. +func Test_normal_formatexpr_returns_nonzero() + new + call setline(1, ['one', 'two']) + func! Format() + return 1 + endfunc + setlocal formatexpr=Format() + normal VGgq + call assert_equal(['one two'], getline(1, '$')) + setlocal formatexpr= + delfunc Format + close! +endfunc + func Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 15c1836c9a..ccc5e6ffc9 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -348,6 +348,16 @@ func Test_set_values() endif endfunc +func Test_renderoptions() + throw 'skipped: Nvim does not support renderoptions' + " Only do this for Windows Vista and later, fails on Windows XP and earlier. + " Doesn't hurt to do this on a non-Windows system. + if windowsversion() !~ '^[345]\.' + set renderoptions=type:directx + set rop=type:directx + endif +endfunc + func ResetIndentexpr() set indentexpr= endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index e06c4f59da..48c0a83053 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -29,7 +29,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args> command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args> command! -nargs=* -bang Xlast <mods>clast<bang> <args> - command! -nargs=* -bang -range Xnfile <mods><count>cnfile<bang> <args> + command! -count -nargs=* -bang Xnfile <mods><count>cnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args> command! -nargs=* Xexpr <mods>cexpr <args> command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args> @@ -40,6 +40,8 @@ func s:setup_commands(cchar) command! -nargs=0 -count Xcc <count>cc command! -count=1 -nargs=0 Xbelow <mods><count>cbelow command! -count=1 -nargs=0 Xabove <mods><count>cabove + command! -count=1 -nargs=0 Xbefore <mods><count>cbefore + command! -count=1 -nargs=0 Xafter <mods><count>cafter let g:Xgetlist = function('getqflist') let g:Xsetlist = function('setqflist') call setqflist([], 'f') @@ -64,7 +66,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args> command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args> command! -nargs=* -bang Xlast <mods>llast<bang> <args> - command! -nargs=* -bang -range Xnfile <mods><count>lnfile<bang> <args> + command! -count -nargs=* -bang Xnfile <mods><count>lnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args> command! -nargs=* Xexpr <mods>lexpr <args> command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args> @@ -75,6 +77,8 @@ func s:setup_commands(cchar) command! -nargs=0 -count Xcc <count>ll command! -count=1 -nargs=0 Xbelow <mods><count>lbelow command! -count=1 -nargs=0 Xabove <mods><count>labove + command! -count=1 -nargs=0 Xbefore <mods><count>lbefore + command! -count=1 -nargs=0 Xafter <mods><count>lafter let g:Xgetlist = function('getloclist', [0]) let g:Xsetlist = function('setloclist', [0]) call setloclist(0, [], 'f') @@ -260,6 +264,9 @@ func XwindowTests(cchar) \ winheight('.') == 7 && \ getline('.') ==# '|| non-error 1') + " :cnext in quickfix window should move to the next entry + Xnext + call assert_equal(2, g:Xgetlist({'idx' : 0}).idx) " Calling cwindow should close the quickfix window with no valid errors Xwindow @@ -431,13 +438,19 @@ func Xtest_browse(cchar) " result in failure if a:cchar == 'c' let err = 'E42:' + let cmd = '$cc' else let err = 'E776:' + let cmd = '$ll' endif call assert_fails('Xnext', err) call assert_fails('Xprev', err) call assert_fails('Xnfile', err) call assert_fails('Xpfile', err) + call assert_fails(cmd, err) + + Xexpr '' + call assert_fails(cmd, 'E42:') call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') @@ -1247,6 +1260,36 @@ func Test_efm2() let &efm = save_efm endfunc +" Test for '%t' (error type) field in 'efm' +func Test_efm_error_type() + let save_efm = &efm + + " error type + set efm=%f:%l:%t:%m + cexpr ["Xfile1:10:E:msg1", "Xfile1:20:W:msg2", "Xfile1:30:I:msg3", + \ "Xfile1:40:N:msg4", "Xfile1:50:R:msg5"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10 error: msg1', + \ ' 2 Xfile1:20 warning: msg2', + \ ' 3 Xfile1:30 info: msg3', + \ ' 4 Xfile1:40 note: msg4', + \ ' 5 Xfile1:50 R: msg5'], output) + + " error type and a error number + set efm=%f:%l:%t:%n:%m + cexpr ["Xfile1:10:E:2:msg1", "Xfile1:20:W:4:msg2", "Xfile1:30:I:6:msg3", + \ "Xfile1:40:N:8:msg4", "Xfile1:50:R:3:msg5"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10 error 2: msg1', + \ ' 2 Xfile1:20 warning 4: msg2', + \ ' 3 Xfile1:30 info 6: msg3', + \ ' 4 Xfile1:40 note 8: msg4', + \ ' 5 Xfile1:50 R 3: msg5'], output) + let &efm = save_efm +endfunc + func XquickfixChangedByAutocmd(cchar) call s:setup_commands(a:cchar) if a:cchar == 'c' @@ -1744,7 +1787,7 @@ func Test_switchbuf() call assert_equal(1, bufwinnr('Xqftestfile3')) " If only quickfix window is open in the current tabpage, jumping to an - " entry with 'switchubf' set to 'usetab' should search in other tabpages. + " entry with 'switchbuf' set to 'usetab' should search in other tabpages. enew | only set switchbuf=usetab tabedit Xqftestfile1 @@ -1810,14 +1853,27 @@ func s:test_xgrep(cchar) enew! | only set makeef&vim silent Xgrep Grep_Test_Text: test_quickfix.vim - call assert_true(len(g:Xgetlist()) == 3) + call assert_true(len(g:Xgetlist()) == 5) Xopen call assert_true(w:quickfix_title =~ '^:grep') Xclose enew set makeef=Temp_File_## silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim - call assert_true(len(g:Xgetlist()) == 6) + + " Try with 'grepprg' set to 'internal' + set grepprg=internal + silent Xgrep Grep_Test_Text: test_quickfix.vim + silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim + call assert_true(len(g:Xgetlist()) == 9) + set grepprg&vim + + call writefile(['Vim'], 'XtestTempFile') + set makeef=XtestTempFile + silent Xgrep Grep_Test_Text: test_quickfix.vim + call assert_equal(5, len(g:Xgetlist())) + call assert_false(filereadable('XtestTempFile')) + set makeef&vim endfunc func Test_grep() @@ -1914,9 +1970,23 @@ func HistoryTest(cchar) call assert_equal(' error list 2 of 3; 2 ' . common, res[1]) call assert_equal('> error list 3 of 3; 3 ' . common, res[2]) + " Test for changing the quickfix lists + call assert_equal(3, g:Xgetlist({'nr' : 0}).nr) + exe '1' . a:cchar . 'hist' + call assert_equal(1, g:Xgetlist({'nr' : 0}).nr) + exe '3' . a:cchar . 'hist' + call assert_equal(3, g:Xgetlist({'nr' : 0}).nr) + call assert_fails('-2' . a:cchar . 'hist', 'E16:') + call assert_fails('4' . a:cchar . 'hist', 'E16:') + call g:Xsetlist([], 'f') let l = split(execute(a:cchar . 'hist'), "\n") call assert_equal('No entries', l[0]) + if a:cchar == 'c' + call assert_fails('4chist', 'E16:') + else + call assert_fails('4lhist', 'E776:') + endif " An empty list should still show the stack history call g:Xsetlist([]) @@ -2365,14 +2435,28 @@ func Test_Autocmd() silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim silent grep abc123def Xtest silent grepadd abc123def Xtest + set grepprg=internal + silent grep Grep_Autocmd_Text test_quickfix.vim + silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim + silent lgrep Grep_Autocmd_Text test_quickfix.vim + silent lgrepadd GrepAdd_Autocmd_Text test_quickfix.vim + set grepprg&vim let l = ['pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd', - \ 'pregrep', - \ 'postgrep', - \ 'pregrepadd', - \ 'postgrepadd'] + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'pregrep', + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'pregrep', + \ 'postgrep', + \ 'pregrepadd', + \ 'postgrepadd', + \ 'prelgrep', + \ 'postlgrep', + \ 'prelgrepadd', + \ 'postlgrepadd'] call assert_equal(l, g:acmds) endif @@ -2491,6 +2575,19 @@ func Test_cwindow_jump() call assert_true(winnr('$') == 2) call assert_true(winnr() == 1) + " Jumping to a file from the location list window should find a usuable + " window by wrapping around the window list. + enew | only + call setloclist(0, [], 'f') + new | new + lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + lopen + 1close + call assert_equal(0, getloclist(3, {'id' : 0}).id) + lnext + call assert_equal(3, winnr()) + call assert_equal(getloclist(1, {'id' : 0}).id, getloclist(3, {'id' : 0}).id) + enew | only set efm&vim endfunc @@ -3762,6 +3859,30 @@ func Test_qftitle() call setqflist([], 'r', {'items' : [{'filename' : 'a.c', 'lnum' : 10}]}) call assert_equal('Errors', w:quickfix_title) cclose + + " Switching to another quickfix list in one tab page should update the + " quickfix window title and statusline in all the other tab pages also + call setqflist([], 'f') + %bw! + cgetexpr ['file_one:1:1: error in the first quickfix list'] + call setqflist([], 'a', {'title': 'first quickfix list'}) + cgetexpr ['file_two:2:1: error in the second quickfix list'] + call setqflist([], 'a', {'title': 'second quickfix list'}) + copen + wincmd t + tabnew two + copen + wincmd t + colder + call assert_equal('first quickfix list', gettabwinvar(1, 2, 'quickfix_title')) + call assert_equal('first quickfix list', gettabwinvar(2, 2, 'quickfix_title')) + call assert_equal(1, tabpagewinnr(1)) + call assert_equal(1, tabpagewinnr(2)) + tabnew + call setqflist([], 'a', {'title': 'new quickfix title'}) + call assert_equal('new quickfix title', gettabwinvar(1, 2, 'quickfix_title')) + call assert_equal('new quickfix title', gettabwinvar(2, 2, 'quickfix_title')) + %bw! endfunc func Test_lbuffer_with_bwipe() @@ -4177,17 +4298,22 @@ func Test_empty_qfbuf() endfunc " Test for the :cbelow, :cabove, :lbelow and :labove commands. +" And for the :cafter, :cbefore, :lafter and :lbefore commands. func Xtest_below(cchar) call s:setup_commands(a:cchar) " No quickfix/location list call assert_fails('Xbelow', 'E42:') call assert_fails('Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') " Empty quickfix/location list call g:Xsetlist([]) call assert_fails('Xbelow', 'E42:') call assert_fails('Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') call s:create_test_file('X1') call s:create_test_file('X2') @@ -4201,39 +4327,74 @@ func Xtest_below(cchar) call assert_fails('Xabove', 'E42:') call assert_fails('3Xbelow', 'E42:') call assert_fails('4Xabove', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') + call assert_fails('3Xbefore', 'E42:') + call assert_fails('4Xafter', 'E42:') " Test the commands with various arguments - Xexpr ["X1:5:L5", "X2:5:L5", "X2:10:L10", "X2:15:L15", "X3:3:L3"] + Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"] edit +7 X2 Xabove call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_fails('Xabove', 'E553:') + normal 7G + Xbefore + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + call assert_fails('Xbefore', 'E553:') + normal 2j Xbelow call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal 7G + Xafter + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + " Last error in this file Xbelow 99 call assert_equal(['X2', 15], [bufname(''), line('.')]) call assert_fails('Xbelow', 'E553:') + normal gg + Xafter 99 + call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')]) + call assert_fails('Xafter', 'E553:') + " First error in this file Xabove 99 call assert_equal(['X2', 5], [bufname(''), line('.')]) call assert_fails('Xabove', 'E553:') + normal G + Xbefore 99 + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + call assert_fails('Xbefore', 'E553:') + normal gg Xbelow 2 call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal gg + Xafter 2 + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + normal G Xabove 2 call assert_equal(['X2', 10], [bufname(''), line('.')]) + normal G + Xbefore 2 + call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')]) + edit X4 call assert_fails('Xabove', 'E42:') call assert_fails('Xbelow', 'E42:') + call assert_fails('Xbefore', 'E42:') + call assert_fails('Xafter', 'E42:') if a:cchar == 'l' " If a buffer has location list entries from some other window but not " from the current window, then the commands should fail. edit X1 | split | call setloclist(0, [], 'f') call assert_fails('Xabove', 'E776:') call assert_fails('Xbelow', 'E776:') + call assert_fails('Xbefore', 'E776:') + call assert_fails('Xafter', 'E776:') close endif @@ -4244,31 +4405,52 @@ func Xtest_below(cchar) edit +1 X2 Xbelow 2 call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) + normal 1G + Xafter 2 + call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')]) + normal gg Xbelow 99 call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) + normal gg + Xafter 99 + call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')]) + normal G Xabove 2 call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')]) normal G + Xbefore 2 + call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')]) + + normal G Xabove 99 call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal G + Xbefore 99 + call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal 10G Xabove call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')]) + normal 10G$ + 2Xbefore + call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')]) + normal 10G Xbelow call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')]) + normal 9G + 5Xafter + call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')]) " Invalid range if a:cchar == 'c' - call assert_fails('-2cbelow', 'E553:') - " TODO: should go to first error in the current line? - 0cabove + call assert_fails('-2cbelow', 'E16:') + call assert_fails('-2cafter', 'E16:') else - call assert_fails('-2lbelow', 'E553:') - " TODO: should go to first error in the current line? - 0labove + call assert_fails('-2lbelow', 'E16:') + call assert_fails('-2lafter', 'E16:') endif call delete('X1') @@ -4282,6 +4464,42 @@ func Test_cbelow() call Xtest_below('l') endfunc +func Test_quickfix_count() + let commands = [ + \ 'cNext', + \ 'cNfile', + \ 'cabove', + \ 'cbelow', + \ 'cfirst', + \ 'clast', + \ 'cnewer', + \ 'cnext', + \ 'cnfile', + \ 'colder', + \ 'cprevious', + \ 'crewind', + \ + \ 'lNext', + \ 'lNfile', + \ 'labove', + \ 'lbelow', + \ 'lfirst', + \ 'llast', + \ 'lnewer', + \ 'lnext', + \ 'lnfile', + \ 'lolder', + \ 'lprevious', + \ 'lrewind', + \ ] + for cmd in commands + call assert_fails('-1' .. cmd, 'E16:') + call assert_fails('.' .. cmd, 'E16:') + call assert_fails('%' .. cmd, 'E16:') + call assert_fails('$' .. cmd, 'E16:') + endfor +endfunc + " Test for aborting quickfix commands using QuickFixCmdPre func Xtest_qfcmd_abort(cchar) call s:setup_commands(a:cchar) @@ -4437,6 +4655,24 @@ func Test_cquit() call assert_fails('-3cquit', 'E16:') endfunc +" Running :lhelpgrep command more than once in a help window, doesn't jump to +" the help topic +func Test_lhelpgrep_from_help_window() + call mkdir('Xtestdir/doc', 'p') + call writefile(['window'], 'Xtestdir/doc/a.txt') + call writefile(['buffer'], 'Xtestdir/doc/b.txt') + let save_rtp = &rtp + let &rtp = 'Xtestdir' + lhelpgrep window + lhelpgrep buffer + call assert_equal('b.txt', fnamemodify(@%, ":p:t")) + lhelpgrep window + call assert_equal('a.txt', fnamemodify(@%, ":p:t")) + let &rtp = save_rtp + call delete('Xtestdir', 'rf') + new | only! +endfunc + " Test for adding an invalid entry with the quickfix window open and making " sure that the window contents are not changed func Test_add_invalid_entry_with_qf_window() diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 2ee0ee1c0c..1bb2ee53de 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -755,6 +755,13 @@ func Test_start_end_of_buffer_match() bwipe! endfunc +func Test_ze_before_zs() + call assert_equal('', matchstr(' ', '\%#=1\ze \zs')) + call assert_equal('', matchstr(' ', '\%#=2\ze \zs')) + call assert_equal(repeat([''], 10), matchlist(' ', '\%#=1\ze \zs')) + call assert_equal(repeat([''], 10), matchlist(' ', '\%#=2\ze \zs')) +endfunc + " Check for detecting error func Test_regexp_error() set regexpengine=2 diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 4466ad436a..513780938e 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -533,4 +533,15 @@ func Test_search_with_end_offset() close! endfunc +" Check that "^" matches even when the line starts with a combining char +func Test_match_start_of_line_combining() + new + call setline(1, ['', "\u05ae", '']) + exe "normal gg/^\<CR>" + call assert_equal(2, getcurpos()[1]) + bwipe! +endfunc + + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 19a7c6c9d0..8d2a768ba0 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -2,6 +2,9 @@ " Tests for register operations " +source check.vim +source view_util.vim + " This test must be executed first to check for empty and unset registers. func Test_aaa_empty_reg_test() call assert_fails('normal @@', 'E748:') @@ -52,31 +55,44 @@ func Test_display_registers() let b = execute('registers') call assert_equal(a, b) - call assert_match('^\n--- Registers ---\n' - \ . '"" a\n' - \ . '"0 ba\n' - \ . '"1 b\n' - \ . '"a b\n' + call assert_match('^\nType Name Content\n' + \ . ' c "" a\n' + \ . ' c "0 ba\n' + \ . ' c "1 b\n' + \ . ' c "a b\n' \ . '.*' - \ . '"- a\n' + \ . ' c "- a\n' \ . '.*' - \ . '": ls\n' - \ . '"% file2\n' - \ . '"# file1\n' - \ . '"/ bar\n' - \ . '"= 2\*4', a) + \ . ' c ": ls\n' + \ . ' c "% file2\n' + \ . ' c "# file1\n' + \ . ' c "/ bar\n' + \ . ' c "= 2\*4', a) let a = execute('registers a') - call assert_match('^\n--- Registers ---\n' - \ . '"a b', a) + call assert_match('^\nType Name Content\n' + \ . ' c "a b', a) let a = execute('registers :') - call assert_match('^\n--- Registers ---\n' - \ . '": ls', a) + call assert_match('^\nType Name Content\n' + \ . ' c ": ls', a) bwipe! endfunc +func Test_recording_status_in_ex_line() + norm qx + redraw! + call assert_equal('recording @x', Screenline(&lines)) + set shortmess=q + redraw! + call assert_equal('recording', Screenline(&lines)) + set shortmess& + norm q + redraw! + call assert_equal('', Screenline(&lines)) +endfunc + " Check that replaying a typed sequence does not use an Esc and following " characters as an escape sequence. func Test_recording_esc_sequence() @@ -254,4 +270,15 @@ func Test_ve_blockpaste() bwipe! endfunc +func Test_insert_small_delete() + new + call setline(1, ['foo foobar bar']) + call cursor(1,1) + exe ":norm! ciw'\<C-R>-'" + call assert_equal("'foo' foobar bar", getline(1)) + exe ":norm! w.w." + call assert_equal("'foo' 'foobar' 'bar'", getline(1)) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 9c74c35049..1a274d1fec 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -27,6 +27,19 @@ func Test_rubydo() %bwipe! endfunc +func Test_rubydo_dollar_underscore() + throw 'skipped: TODO: ' + new + call setline(1, ['one', 'two', 'three', 'four']) + 2,3rubydo $_ = '[' + $_ + ']' + call assert_equal(['one', '[two]', '[three]', 'four'], getline(1, '$')) + bwipe! + + call assert_fails('rubydo $_ = 0', 'E265:') + call assert_fails('rubydo (') + bwipe! +endfunc + func Test_rubyfile() " Check :rubyfile does not SEGV with Ruby level exception but just fails let tempfile = tempname() . '.rb' @@ -391,3 +404,14 @@ func Test_ruby_p() let messages = GetMessages() call assert_equal(0, len(messages)) endfunc + +func Test_rubyeval_error() + " On Linux or Windows the error matches: + " "syntax error, unexpected end-of-input" + " whereas on macOS in CI, the error message makes less sense: + " "SyntaxError: array length must be 2" + " Unclear why. The test does not check the error message. + call assert_fails('call rubyeval("(")') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 6824c50112..0703a6b141 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -270,7 +270,7 @@ func Test_search_cmdline2() " nor "/foo\<c-u>\<cr>" works to delete the commandline. " In that case Vim should return "E35 no previous regular expression", " but it looks like Vim still sees /foo and therefore the test fails. - " Therefore, disableing this test + " Therefore, disabling this test "call assert_fails(feedkeys("/foo\<c-w>\<cr>", 'tx'), 'E35') "call assert_equal({'lnum': 1, 'leftcol': 0, 'col': 0, 'topfill': 0, 'topline': 1, 'coladd': 0, 'skipcol': 0, 'curswant': 0}, winsaveview()) @@ -508,7 +508,7 @@ endfunc func Test_search_cmdline7() throw 'skipped: Nvim does not support test_override()' - " Test that an pressing <c-g> in an empty command line + " Test that pressing <c-g> in an empty command line " does not move the cursor if !exists('+incsearch') return @@ -1172,3 +1172,24 @@ func Test_search_special() set t_PE= exe "norm /\x80PS" endfunc + +" Test 'smartcase' with utf-8. +func Test_search_smartcase_utf8() + new + let save_enc = &encoding + set encoding=utf8 ignorecase smartcase + + call setline(1, 'Café cafÉ') + 1s/café/x/g + call assert_equal('x x', getline(1)) + + call setline(1, 'Café cafÉ') + 1s/cafÉ/x/g + call assert_equal('Café x', getline(1)) + + set ignorecase& smartcase& + let &encoding = save_enc + close! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim new file mode 100644 index 0000000000..338c0d79ff --- /dev/null +++ b/src/nvim/testdir/test_signals.vim @@ -0,0 +1,140 @@ +" Test signal handling. + +source check.vim +source term_util.vim + +CheckUnix + +source shared.vim + +" Check whether a signal is available on this system. +func HasSignal(signal) + let signals = system('kill -l') + return signals =~# '\<' .. a:signal .. '\>' +endfunc + +" Test signal WINCH (window resize signal) +func Test_signal_WINCH() + throw 'skipped: Nvim cannot avoid terminal resize' + if has('gui_running') || !HasSignal('WINCH') + return + endif + + " We do not actually want to change the size of the terminal. + let old_WS = '' + if exists('&t_WS') + let old_WS = &t_WS + let &t_WS = '' + endif + + let old_lines = &lines + let old_columns = &columns + let new_lines = &lines - 2 + let new_columns = &columns - 2 + + exe 'set lines=' .. new_lines + exe 'set columns=' .. new_columns + call assert_equal(new_lines, &lines) + call assert_equal(new_columns, &columns) + + " Send signal and wait for signal to be processed. + " 'lines' and 'columns' should have been restored + " after handing signal WINCH. + exe 'silent !kill -s WINCH ' .. getpid() + call WaitForAssert({-> assert_equal(old_lines, &lines)}) + call assert_equal(old_columns, &columns) + + if old_WS != '' + let &t_WS = old_WS + endif +endfunc + +" Test signal PWR, which should update the swap file. +func Test_signal_PWR() + if !HasSignal('PWR') + return + endif + + " Set a very large 'updatetime' and 'updatecount', so that we can be sure + " that swap file is updated as a result of sending PWR signal, and not + " because of exceeding 'updatetime' or 'updatecount' when changing buffer. + set updatetime=100000 updatecount=100000 + new Xtest_signal_PWR + let swap_name = swapname('%') + call setline(1, '123') + preserve + let swap_content = readfile(swap_name, 'b') + + " Update the buffer and check that the swap file is not yet updated, + " since we set 'updatetime' and 'updatecount' to large values. + call setline(1, 'abc') + call assert_equal(swap_content, readfile(swap_name, 'b')) + + " Sending PWR signal should update the swap file. + exe 'silent !kill -s PWR ' .. getpid() + call WaitForAssert({-> assert_notequal(swap_content, readfile(swap_name, 'b'))}) + + bwipe! + set updatetime& updatecount& +endfunc + +" Test a deadly signal. +" +" There are several deadly signals: SISEGV, SIBUS, SIGTERM... +" Test uses signal SIGTERM as it does not create a core +" dump file unlike SIGSEGV, SIGBUS, etc. See "man 7 signals. +" +" Vim should exit with a deadly signal and unsaved changes +" should be recoverable from the swap file preserved as a +" result of the deadly signal handler. +func Test_deadly_signal_TERM() + if !HasSignal('TERM') + throw 'Skipped: TERM signal not supported' + endif + if !CanRunVimInTerminal() + throw 'Skipped: cannot run vim in terminal' + endif + let cmd = GetVimCommand() + if cmd =~ 'valgrind' + throw 'Skipped: cannot test signal TERM with valgrind' + endif + + " If test fails once, it can leave temporary files and trying to rerun + " the test would then fail again if they are not deleted first. + call delete('.Xsig_TERM.swp') + call delete('XsetupAucmd') + call delete('XautoOut') + let lines =<< trim END + au VimLeave * call writefile(["VimLeave triggered"], "XautoOut", "as") + au VimLeavePre * call writefile(["VimLeavePre triggered"], "XautoOut", "as") + END + call writefile(lines, 'XsetupAucmd') + + let buf = RunVimInTerminal('-S XsetupAucmd Xsig_TERM', {'rows': 6}) + let pid_vim = term_getjob(buf)->job_info().process + + call term_sendkeys(buf, ":call setline(1, 'foo')\n") + call WaitForAssert({-> assert_equal('foo', term_getline(buf, 1))}) + + call assert_false(filereadable('Xsig_TERM')) + exe 'silent !kill -s TERM ' .. pid_vim + call WaitForAssert({-> assert_true(filereadable('.Xsig_TERM.swp'))}) + + " Don't call StopVimInTerminal() as it expects job to be still running. + call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))}) + + new + silent recover .Xsig_TERM.swp + call assert_equal(['foo'], getline(1, '$')) + + let result = readfile('XautoOut') + call assert_match('VimLeavePre triggered', result[0]) + call assert_match('VimLeave triggered', result[1]) + + %bwipe! + call delete('.Xsig_TERM.swp') + call delete('XsetupAucmd') + call delete('XautoOut') +endfunc + +" vim: ts=8 sw=2 sts=2 tw=80 fdm=marker diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 2b7672f1af..4bbd722fdb 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -15,14 +15,8 @@ func Test_sign() " icon is ignored when not supported. "(not supported)" is shown after " the icon name when listing signs. sign define Sign1 text=x - try - sign define Sign2 text=xy texthl=Title linehl=Error - \ icon=../../pixmaps/stock_vim_find_help.png - catch /E255:/ - " Ignore error: E255: Couldn't read in sign data! - " This error can happen when running in the GUI. - " Some gui like Motif do not support the png icon format. - endtry + + call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png') " Test listing signs. let a=execute('sign list') @@ -104,12 +98,7 @@ func Test_sign() edit foo call setline(1, ['A', 'B', 'C', 'D']) - try - sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist - \ icon=doesnotexist.xpm - catch /E255:/ - " ignore error: E255: it can happens for guis. - endtry + call Sign_command_ignore_error('sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist icon=doesnotexist.xpm') let fn = expand('%:p') exe 'sign place 43 line=2 name=Sign3 file=' . fn @@ -133,9 +122,9 @@ func Test_sign() call assert_fails("sign define Sign4 text=a\e linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=\ea linehl=Comment", 'E239:') - " Only 1 or 2 character text is allowed + " Only 0, 1 or 2 character text is allowed call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') - call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') + " call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " define sign with whitespace @@ -317,7 +306,7 @@ func Test_sign_invalid_commands() call assert_fails('sign jump 1 name=', 'E474:') call assert_fails('sign jump 1 name=Sign1', 'E474:') call assert_fails('sign jump 1 line=100', '474:') - call assert_fails('sign define Sign2 text=', 'E239:') + " call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr(''), \ 'E474:') @@ -378,6 +367,25 @@ func Test_sign_delete_buffer() sign undefine Sign endfunc +" Ignore error: E255: Couldn't read in sign data! +" This error can happen when running in the GUI. +" Some gui like Motif do not support the png icon format. +func Sign_command_ignore_error(cmd) + try + exe a:cmd + catch /E255:/ + endtry +endfunc + +" ignore error: E255: Couldn't read in sign data! +" This error can happen when running in gui. +func Sign_define_ignore_error(name, attr) + try + call sign_define(a:name, a:attr) + catch /E255:/ + endtry +endfunc + " Test for Vim script functions for managing signs func Test_sign_funcs() " Remove all the signs @@ -394,12 +402,7 @@ func Test_sign_funcs() call sign_define("sign2") let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', \ 'icon' : 'sign2.ico'} - try - call sign_define("sign2", attr) - catch /E255:/ - " ignore error: E255: Couldn't read in sign data! - " This error can happen when running in gui. - endtry + call Sign_define_ignore_error("sign2", attr) call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], \ sign_getdefined("sign2")) @@ -412,7 +415,7 @@ func Test_sign_funcs() " Tests for invalid arguments to sign_define() call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') - call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') + " call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') call assert_fails('call sign_define([])', 'E730:') call assert_fails('call sign_define("sign6", [])', 'E715:') @@ -508,6 +511,16 @@ func Test_sign_funcs() call assert_fails('call sign_undefine("none")', 'E155:') call assert_fails('call sign_undefine([])', 'E730:') + " Test for using '.' as the line number for sign_place() + call Sign_define_ignore_error("sign1", attr) + call cursor(22, 1) + call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', + \ {'lnum' : '.'})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 15, 'group' : '', 'lnum' : 22, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('%', {'lnum' : 22})) + call delete("Xsign") call sign_unplace('*') call sign_undefine() diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 6214975ef5..e7f332bc4c 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -686,3 +686,53 @@ func Test_v_argv() call assert_true(idx > 2) call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:]) endfunc + +" Test the '-T' argument which sets the 'term' option. +func Test_T_arg() + throw 'skipped: Nvim does not support "-T" argument' + CheckNotGui + let after =<< trim [CODE] + call writefile([&term], "Xtest_T_arg") + qall + [CODE] + + for t in ['builtin_dumb', 'builtin_ansi'] + if RunVim([], after, '-T ' .. t) + let lines = readfile('Xtest_T_arg') + call assert_equal([t], lines) + endif + endfor + + call delete('Xtest_T_arg') +endfunc + +" Test the '-x' argument to read/write encrypted files. +func Test_x_arg() + CheckRunVimInTerminal + CheckFeature cryptv + + " Create an encrypted file Xtest_x_arg. + let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0}) + call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match(' All$', term_getline(buf, 10))}) + call term_sendkeys(buf, "itest\<Esc>:w\<Enter>") + call WaitForAssert({-> assert_match('"Xtest_x_arg" \[New\]\[blowfish2\] 1L, 5B written', + \ term_getline(buf, 10))}) + call StopVimInTerminal(buf) + + " Read the encrypted file and check that it contains the expected content "test" + let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0}) + call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))}) + call term_sendkeys(buf, "foo\n") + call WaitForAssert({-> assert_match('^test', term_getline(buf, 1))}) + call StopVimInTerminal(buf) + + call delete('Xtest_x_arg') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 424cb4abd0..9cf8690d57 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -3,7 +3,7 @@ source shared.vim source check.vim -function! Test_System() +func Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return endif diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 55dff3d476..2b6a89647e 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -1,6 +1,7 @@ " Tests for tabpage source screendump.vim +source check.vim function Test_tabpage() bw! @@ -58,7 +59,7 @@ function Test_tabpage() q " " - " Test for ":tab drop multi-opend-file" to keep current tabpage and window. + " Test for ":tab drop multi-opened-file" to keep current tabpage and window. new test1 tabnew new test1 @@ -222,6 +223,34 @@ function Test_tabpage_with_autocmd() 1tabonly! endfunction +" Test autocommands on tab drop +function Test_tabpage_with_autocmd_tab_drop() + augroup TestTabpageGroup + au! + autocmd TabEnter * call add(s:li, 'TabEnter') + autocmd WinEnter * call add(s:li, 'WinEnter') + autocmd BufEnter * call add(s:li, 'BufEnter') + autocmd TabLeave * call add(s:li, 'TabLeave') + autocmd WinLeave * call add(s:li, 'WinLeave') + autocmd BufLeave * call add(s:li, 'BufLeave') + augroup END + + let s:li = [] + tab drop test1 + call assert_equal(['BufLeave', 'BufEnter'], s:li) + + let s:li = [] + tab drop test2 test3 + call assert_equal([ + \ 'TabLeave', 'TabEnter', 'TabLeave', 'TabEnter', + \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter', + \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li) + + autocmd! TestTabpageGroup + augroup! TestTabpageGroup + 1tabonly! +endfunction + function Test_tabpage_with_tab_modifier() for n in range(4) tabedit @@ -579,4 +608,82 @@ func Test_tabpage_cmdheight() call delete('XTest_tabpage_cmdheight') endfunc +" Return the terminal key code for selecting a tab page from the tabline. This +" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0), +" KS_FILLER (0x58) and then the tab page number. +func TabLineSelectPageCode(tabnr) + return "\x9b\xf0\x58" .. nr2char(a:tabnr) +endfunc + +" Return the terminal key code for opening a new tabpage from the tabpage +" menu. This sequence consists of the following codes: a CSI (0x9b), +" KS_TABMENU (0xef), KS_FILLER (0x58), the tab page number and +" TABLINE_MENU_NEW (2). +func TabMenuNewItemCode(tabnr) + return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(2) +endfunc + +" Return the terminal key code for closing a tabpage from the tabpage menu. +" This sequence consists of the following codes: a CSI (0x9b), KS_TABMENU +" (0xef), KS_FILLER (0x58), the tab page number and TABLINE_MENU_CLOSE (1). +func TabMenuCloseItemCode(tabnr) + return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(1) +endfunc + +" Test for using the tabpage menu from the insert and normal modes +func Test_tabline_tabmenu() + " only works in GUI + CheckGui + + %bw! + tabnew + tabnew + call assert_equal(3, tabpagenr()) + + " go to tab page 2 in normal mode + call feedkeys(TabLineSelectPageCode(2), "Lx!") + call assert_equal(2, tabpagenr()) + + " close tab page 3 in normal mode + call feedkeys(TabMenuCloseItemCode(3), "Lx!") + call assert_equal(2, tabpagenr('$')) + call assert_equal(2, tabpagenr()) + + " open new tab page before tab page 1 in normal mode + call feedkeys(TabMenuNewItemCode(1), "Lx!") + call assert_equal(1, tabpagenr()) + call assert_equal(3, tabpagenr('$')) + + " go to tab page 2 in operator-pending mode (should beep) + call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")') + + " open new tab page before tab page 1 in operator-pending mode (should beep) + call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")') + + " open new tab page after tab page 3 in normal mode + call feedkeys(TabMenuNewItemCode(4), "Lx!") + call assert_equal(4, tabpagenr()) + call assert_equal(4, tabpagenr('$')) + + " go to tab page 2 in insert mode + call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!") + call assert_equal(2, tabpagenr()) + + " close tab page 2 in insert mode + call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!") + call assert_equal(3, tabpagenr('$')) + + " open new tab page before tab page 3 in insert mode + call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!") + call assert_equal(3, tabpagenr()) + call assert_equal(4, tabpagenr('$')) + + " open new tab page after tab page 4 in insert mode + call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!") + call assert_equal(5, tabpagenr()) + call assert_equal(5, tabpagenr('$')) + + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 2223be952c..4af52b536c 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -424,6 +424,15 @@ func Test_format_align() \ ], getline(1, '$')) enew! + " align text with 'wrapmargin' + 50vnew + call setline(1, ['Vim']) + setl textwidth=0 + setl wrapmargin=30 + right + call assert_equal("\t\t Vim", getline(1)) + q! + set tw& endfunc @@ -931,4 +940,217 @@ func Test_substitute() call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g')) endfunc +" Test for 'a' and 'w' flags in 'formatoptions' +func Test_fo_a_w() + new + setlocal fo+=aw tw=10 + call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt') + call assert_equal(['abc abcde ', 'a abc'], getline(1, '$')) + + " Test for 'a', 'w' and '1' options. + setlocal textwidth=0 + setlocal fo=1aw + %d + call setline(1, '. foo') + normal 72ig + call feedkeys('a uu uu uu', 'xt') + call assert_equal('g uu uu ', getline(1)[-8:]) + call assert_equal(['uu. foo'], getline(2, '$')) + + " using backspace or "x" triggers reformat + call setline(1, ['1 2 3 4 5 ', '6 7 8 9']) + set tw=10 + set fo=taw + set bs=indent,eol,start + exe "normal 1G4la\<BS>\<BS>\<Esc>" + call assert_equal(['1 2 4 5 6 ', '7 8 9'], getline(1, 2)) + exe "normal f4xx" + call assert_equal(['1 2 5 6 7 ', '8 9'], getline(1, 2)) + + set tw=0 + set fo& + %bw! +endfunc + +" Test for formatting lines using gq in visual mode +func Test_visual_gq_format() + new + call setline(1, ['one two three four', 'five six', 'one two']) + setl textwidth=10 + call feedkeys('ggv$jj', 'xt') + redraw! + normal gq + %d + call setline(1, ['one two three four', 'five six', 'one two']) + normal G$ + call feedkeys('v0kk', 'xt') + redraw! + normal gq + setl textwidth& + close! +endfunc + +" Test for 'n' flag in 'formatoptions' to format numbered lists +func Test_fo_n() + new + setlocal autoindent + setlocal textwidth=12 + setlocal fo=n + call setline(1, [' 1) one two three four', ' 2) two']) + normal gggqG + call assert_equal([' 1) one two', ' three', ' four', ' 2) two'], + \ getline(1, '$')) + close! +endfunc + +" Test for 'formatlistpat' option +func Test_formatlistpat() + new + setlocal autoindent + setlocal textwidth=10 + setlocal fo=n + setlocal formatlistpat=^\\s*-\\s* + call setline(1, [' - one two three', ' - two']) + normal gggqG + call assert_equal([' - one', ' two', ' three', ' - two'], + \ getline(1, '$')) + close! +endfunc + +" Test for the 'b' and 'v' flags in 'formatoptions' +" Text should wrap only if a space character is inserted at or before +" 'textwidth' +func Test_fo_b() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four') + call feedkeys('Amore', 'xt') + call assert_equal(['one two three', 'fourmore'], getline(1, '$')) + + setlocal formatoptions=bt + %d + call setline(1, 'one two three four') + call feedkeys('Amore five', 'xt') + call assert_equal(['one two three fourmore five'], getline(1, '$')) + + setlocal formatoptions=bt + %d + call setline(1, 'one two three four') + call feedkeys('A five', 'xt') + call assert_equal(['one two three four', 'five'], getline(1, '$')) + + setlocal formatoptions=vt + %d + call setline(1, 'one two three four') + call feedkeys('Amore five', 'xt') + call assert_equal(['one two three fourmore', 'five'], getline(1, '$')) + + close! +endfunc + +" Test for the '1' flag in 'formatoptions'. Don't wrap text after a one letter +" word. +func Test_fo_1() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four') + call feedkeys('A a bird', 'xt') + call assert_equal(['one two three four a', 'bird'], getline(1, '$')) + + %d + setlocal formatoptions=t1 + call setline(1, 'one two three four') + call feedkeys('A a bird', 'xt') + call assert_equal(['one two three four', 'a bird'], getline(1, '$')) + + close! +endfunc + +" Test for 'l' flag in 'formatoptions'. When starting insert mode, if a line +" is longer than 'textwidth', then it is not broken. +func Test_fo_l() + new + setlocal textwidth=20 + + setlocal formatoptions=t + call setline(1, 'one two three four five') + call feedkeys('A six', 'xt') + call assert_equal(['one two three four', 'five six'], getline(1, '$')) + + %d + setlocal formatoptions=tl + call setline(1, 'one two three four five') + call feedkeys('A six', 'xt') + call assert_equal(['one two three four five six'], getline(1, '$')) + + close! +endfunc + +" Test for the '2' flag in 'formatoptions' +func Test_fo_2() + new + setlocal autoindent + setlocal formatoptions=t2 + setlocal textwidth=30 + call setline(1, ["\tfirst line of a paragraph.", + \ "second line of the same paragraph.", + \ "third line."]) + normal gggqG + call assert_equal(["\tfirst line of a", + \ "paragraph. second line of the", + \ "same paragraph. third line."], getline(1, '$')) + close! +endfunc + +" Test for formatting lines where only the first line has a comment. +func Test_fo_gq_with_firstline_comment() + new + setlocal formatoptions=tcq + call setline(1, ['- one two', 'three']) + normal gggqG + call assert_equal(['- one two three'], getline(1, '$')) + + %d + call setline(1, ['- one', '- two']) + normal gggqG + call assert_equal(['- one', '- two'], getline(1, '$')) + close! +endfunc + +" Test for trying to join a comment line with a non-comment line +func Test_join_comments() + new + call setline(1, ['one', '/* two */', 'three']) + normal gggqG + call assert_equal(['one', '/* two */', 'three'], getline(1, '$')) + close! +endfunc + +" Test for using 'a' in 'formatoptions' with comments +func Test_autoformat_comments() + new + setlocal formatoptions+=a + call feedkeys("a- one\n- two\n", 'xt') + call assert_equal(['- one', '- two', ''], getline(1, '$')) + + %d + call feedkeys("a\none\n", 'xt') + call assert_equal(['', 'one', ''], getline(1, '$')) + + setlocal formatoptions+=aw + %d + call feedkeys("aone \ntwo\n", 'xt') + call assert_equal(['one two', ''], getline(1, '$')) + + %d + call feedkeys("aone\ntwo\n", 'xt') + call assert_equal(['one', 'two', ''], getline(1, '$')) + + close! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim new file mode 100644 index 0000000000..7e513180a3 --- /dev/null +++ b/src/nvim/testdir/test_trycatch.vim @@ -0,0 +1,1977 @@ +" Test try-catch-finally exception handling +" Most of this was formerly in test49. + +source check.vim +source shared.vim + +"------------------------------------------------------------------------------- +" Test environment {{{1 +"------------------------------------------------------------------------------- + +com! XpathINIT let g:Xpath = '' +com! -nargs=1 -bar Xpath let g:Xpath = g:Xpath . <args> + +" Test 25: Executing :finally clauses on normal control flow {{{1 +" +" Control flow in a :try conditional should always fall through to its +" :finally clause. A :finally clause of a :try conditional inside an +" inactive conditional should never be executed. +"------------------------------------------------------------------------------- + +func T25_F() + let loops = 3 + while loops > 0 + Xpath 'a' . loops + if loops >= 2 + try + Xpath 'b' . loops + if loops == 2 + try + Xpath 'c' . loops + finally + Xpath 'd' . loops + endtry + endif + finally + Xpath 'e' . loops + if loops == 2 + try + Xpath 'f' . loops + finally + Xpath 'g' . loops + endtry + endif + endtry + endif + Xpath 'h' . loops + let loops = loops - 1 + endwhile + Xpath 'i' +endfunc + +func T25_G() + if 1 + try + Xpath 'A' + call T25_F() + Xpath 'B' + finally + Xpath 'C' + endtry + else + try + Xpath 'D' + finally + Xpath 'E' + endtry + endif +endfunc + +func Test_finally() + XpathINIT + call T25_G() + call assert_equal('Aa3b3e3h3a2b2c2d2e2f2g2h2a1h1iBC', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 26: Executing :finally clauses after :continue or :break {{{1 +" +" For a :continue or :break dynamically enclosed in a :try/:endtry +" region inside the next surrounding :while/:endwhile, if the +" :continue/:break is before the :finally, the :finally clause is +" executed first. If the :continue/:break is after the :finally, the +" :finally clause is broken (like an :if/:endif region). +"------------------------------------------------------------------------------- + +func T26_F() + try + let loops = 3 + while loops > 0 + try + try + if loops == 2 + Xpath 'a' . loops + let loops = loops - 1 + continue + elseif loops == 1 + Xpath 'b' . loops + break + finish + endif + Xpath 'c' . loops + endtry + finally + Xpath 'd' . loops + endtry + Xpath 'e' . loops + let loops = loops - 1 + endwhile + Xpath 'f' + finally + Xpath 'g' + let loops = 3 + while loops > 0 + try + finally + try + if loops == 2 + Xpath 'h' . loops + let loops = loops - 1 + continue + elseif loops == 1 + Xpath 'i' . loops + break + finish + endif + endtry + Xpath 'j' . loops + endtry + Xpath 'k' . loops + let loops = loops - 1 + endwhile + Xpath 'l' + endtry + Xpath 'm' +endfunc + +func Test_finally_after_continue() + XpathINIT + call T26_F() + call assert_equal('c3d3e3a2d1b1d1fgj3k3h2i1lm', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 32: Remembering the :return value on :finally {{{1 +" +" If a :finally clause is executed due to a :return specifying +" a value, this is the value visible to the caller if not overwritten +" by a new :return in the :finally clause. A :return without a value +" in the :finally clause overwrites with value 0. +"------------------------------------------------------------------------------- + +func T32_F() + try + Xpath 'a' + try + Xpath 'b' + return "ABCD" + Xpath 'c' + finally + Xpath 'd' + endtry + Xpath 'e' + finally + Xpath 'f' + endtry + Xpath 'g' +endfunc + +func T32_G() + try + Xpath 'h' + return 8 + Xpath 'i' + finally + Xpath 'j' + return 16 + strlen(T32_F()) + Xpath 'k' + endtry + Xpath 'l' +endfunc + +func T32_H() + try + Xpath 'm' + return 32 + Xpath 'n' + finally + Xpath 'o' + return + Xpath 'p' + endtry + Xpath 'q' +endfunc + +func T32_I() + try + Xpath 'r' + finally + Xpath 's' + return T32_G() + T32_H() + 64 + Xpath 't' + endtry + Xpath 'u' +endfunc + +func Test_finally_return() + XpathINIT + call assert_equal(84, T32_I()) + call assert_equal('rshjabdfmo', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 33: :return under :execute or user command and :finally {{{1 +" +" A :return command may be executed under an ":execute" or from +" a user command. Executing of :finally clauses and passing through +" the return code works also then. +"------------------------------------------------------------------------------- + +func T33_F() + try + RETURN 10 + Xpath 'a' + finally + Xpath 'b' + endtry + Xpath 'c' +endfunc + +func T33_G() + try + RETURN 20 + Xpath 'd' + finally + Xpath 'e' + RETURN 30 + Xpath 'f' + endtry + Xpath 'g' +endfunc + +func T33_H() + try + execute "try | return 40 | finally | return 50 | endtry" + Xpath 'h' + finally + Xpath 'i' + endtry + Xpath 'j' +endfunc + +func T33_I() + try + execute "try | return 60 | finally | return 70 | endtry" + Xpath 'k' + finally + Xpath 'l' + execute "try | return 80 | finally | return 90 | endtry" + Xpath 'm' + endtry + Xpath 'n' +endfunc + +func T33_J() + try + RETURN 100 + Xpath 'o' + finally + Xpath 'p' + return + Xpath 'q' + endtry + Xpath 'r' +endfunc + +func T33_K() + try + execute "try | return 110 | finally | return 120 | endtry" + Xpath 's' + finally + Xpath 't' + execute "try | return 130 | finally | return | endtry" + Xpath 'u' + endtry + Xpath 'v' +endfunc + +func T33_L() + try + return + Xpath 'w' + finally + Xpath 'x' + RETURN 140 + Xpath 'y' + endtry + Xpath 'z' +endfunc + +func T33_M() + try + return + Xpath 'A' + finally + Xpath 'B' + execute "try | return 150 | finally | return 160 | endtry" + Xpath 'C' + endtry + Xpath 'D' +endfunc + +func T33_N() + RETURN 170 +endfunc + +func T33_O() + execute "try | return 180 | finally | return 190 | endtry" +endfunc + +func Test_finally_cmd_return() + command! -nargs=? RETURN + \ try | return <args> | finally | return <args> * 2 | endtry + XpathINIT + call assert_equal(20, T33_F()) + call assert_equal(60, T33_G()) + call assert_equal(50, T33_H()) + call assert_equal(90, T33_I()) + call assert_equal(0, T33_J()) + call assert_equal(0, T33_K()) + call assert_equal(280, T33_L()) + call assert_equal(160, T33_M()) + call assert_equal(340, T33_N()) + call assert_equal(190, T33_O()) + call assert_equal('beilptxB', g:Xpath) + delcommand RETURN +endfunc + + +"------------------------------------------------------------------------------- +" Test 41: Skipped :throw finding next command {{{1 +" +" A :throw in an inactive conditional must not hide a following +" command. +"------------------------------------------------------------------------------- + +func T41_F() + Xpath 'a' + if 0 | throw 'never' | endif | Xpath 'b' + Xpath 'c' +endfunc + +func T41_G() + Xpath 'd' + while 0 | throw 'never' | endwhile | Xpath 'e' + Xpath 'f' +endfunc + +func T41_H() + Xpath 'g' + if 0 | try | throw 'never' | endtry | endif | Xpath 'h' + Xpath 'i' +endfunc + +func Test_throw_inactive_cond() + XpathINIT + try + Xpath 'j' + call T41_F() + Xpath 'k' + catch /.*/ + Xpath 'l' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'm' + call T41_G() + Xpath 'n' + catch /.*/ + Xpath 'o' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'p' + call T41_H() + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('jabckmdefnpghiq', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 42: Catching number and string exceptions {{{1 +" +" When a number is thrown, it is converted to a string exception. +" Numbers and strings may be caught by specifying a regular exception +" as argument to the :catch command. +"------------------------------------------------------------------------------- + + +func T42_F() + try + + try + Xpath 'a' + throw 4711 + Xpath 'b' + catch /4711/ + Xpath 'c' + endtry + + try + Xpath 'd' + throw 4711 + Xpath 'e' + catch /^4711$/ + Xpath 'f' + endtry + + try + Xpath 'g' + throw 4711 + Xpath 'h' + catch /\d/ + Xpath 'i' + endtry + + try + Xpath 'j' + throw 4711 + Xpath 'k' + catch /^\d\+$/ + Xpath 'l' + endtry + + try + Xpath 'm' + throw "arrgh" + Xpath 'n' + catch /arrgh/ + Xpath 'o' + endtry + + try + Xpath 'p' + throw "arrgh" + Xpath 'q' + catch /^arrgh$/ + Xpath 'r' + endtry + + try + Xpath 's' + throw "arrgh" + Xpath 't' + catch /\l/ + Xpath 'u' + endtry + + try + Xpath 'v' + throw "arrgh" + Xpath 'w' + catch /^\l\+$/ + Xpath 'x' + endtry + + try + try + Xpath 'y' + throw "ARRGH" + Xpath 'z' + catch /^arrgh$/ + Xpath 'A' + endtry + catch /^\carrgh$/ + Xpath 'B' + endtry + + try + Xpath 'C' + throw "" + Xpath 'D' + catch /^$/ + Xpath 'E' + endtry + + catch /.*/ + Xpath 'F' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry +endfunc + +func Test_catch_number_string() + XpathINIT + call T42_F() + call assert_equal('acdfgijlmoprsuvxyBCE', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 43: Selecting the correct :catch clause {{{1 +" +" When an exception is thrown and there are multiple :catch clauses, +" the first matching one is taken. +"------------------------------------------------------------------------------- + +func T43_F() + let loops = 3 + while loops > 0 + try + if loops == 3 + Xpath 'a' . loops + throw "a" + Xpath 'b' . loops + elseif loops == 2 + Xpath 'c' . loops + throw "ab" + Xpath 'd' . loops + elseif loops == 1 + Xpath 'e' . loops + throw "abc" + Xpath 'f' . loops + endif + catch /abc/ + Xpath 'g' . loops + catch /ab/ + Xpath 'h' . loops + catch /.*/ + Xpath 'i' . loops + catch /a/ + Xpath 'j' . loops + endtry + + let loops = loops - 1 + endwhile + Xpath 'k' +endfunc + +func Test_multi_catch() + XpathINIT + call T43_F() + call assert_equal('a3i3c2h2e1g1k', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 44: Missing or empty :catch patterns {{{1 +" +" A missing or empty :catch pattern means the same as /.*/, that is, +" catches everything. To catch only empty exceptions, /^$/ must be +" used. A :catch with missing, empty, or /.*/ argument also works +" when followed by another command separated by a bar on the same +" line. :catch patterns cannot be specified between ||. But other +" pattern separators can be used instead of //. +"------------------------------------------------------------------------------- + +func T44_F() + try + try + Xpath 'a' + throw "" + catch /^$/ + Xpath 'b' + endtry + + try + Xpath 'c' + throw "" + catch /.*/ + Xpath 'd' + endtry + + try + Xpath 'e' + throw "" + catch // + Xpath 'f' + endtry + + try + Xpath 'g' + throw "" + catch + Xpath 'h' + endtry + + try + Xpath 'i' + throw "oops" + catch /^$/ + Xpath 'j' + catch /.*/ + Xpath 'k' + endtry + + try + Xpath 'l' + throw "arrgh" + catch /^$/ + Xpath 'm' + catch // + Xpath 'n' + endtry + + try + Xpath 'o' + throw "brrr" + catch /^$/ + Xpath 'p' + catch + Xpath 'q' + endtry + + try | Xpath 'r' | throw "x" | catch /.*/ | Xpath 's' | endtry + + try | Xpath 't' | throw "y" | catch // | Xpath 'u' | endtry + + while 1 + try + let caught = 0 + let v:errmsg = "" + " Extra try level: if ":catch" without arguments below raises + " a syntax error because it misinterprets the "Xpath" as a pattern, + " let it be caught by the ":catch /.*/" below. + try + try | Xpath 'v' | throw "z" | catch | Xpath 'w' | : + endtry + endtry + catch /.*/ + let caught = 1 + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + finally + if $VIMNOERRTHROW && v:errmsg != "" + call assert_report(v:errmsg) + endif + if caught || $VIMNOERRTHROW && v:errmsg != "" + Xpath 'x' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + let cologne = 4711 + try + try + Xpath 'y' + throw "throw cologne" + " Next lines catches all and throws 4711: + catch |throw cologne| + Xpath 'z' + endtry + catch /4711/ + Xpath 'A' + endtry + + try + Xpath 'B' + throw "plus" + catch +plus+ + Xpath 'C' + endtry + + Xpath 'D' + catch /.*/ + Xpath 'E' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry +endfunc + +func Test_empty_catch() + XpathINIT + call T44_F() + call assert_equal('abcdefghiklnoqrstuvwyABCD', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 45: Catching exceptions from nested :try blocks {{{1 +" +" When :try blocks are nested, an exception is caught by the innermost +" try conditional that has a matching :catch clause. +"------------------------------------------------------------------------------- + +func T45_F() + let loops = 3 + while loops > 0 + try + try + try + try + if loops == 3 + Xpath 'a' . loops + throw "a" + Xpath 'b' . loops + elseif loops == 2 + Xpath 'c' . loops + throw "ab" + Xpath 'd' . loops + elseif loops == 1 + Xpath 'e' . loops + throw "abc" + Xpath 'f' . loops + endif + catch /abc/ + Xpath 'g' . loops + endtry + catch /ab/ + Xpath 'h' . loops + endtry + catch /.*/ + Xpath 'i' . loops + endtry + catch /a/ + Xpath 'j' . loops + endtry + + let loops = loops - 1 + endwhile + Xpath 'k' +endfunc + +func Test_catch_from_nested_try() + XpathINIT + call T45_F() + call assert_equal('a3i3c2h2e1g1k', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 46: Executing :finally after a :throw in nested :try {{{1 +" +" When an exception is thrown from within nested :try blocks, the +" :finally clauses of the non-catching try conditionals should be +" executed before the matching :catch of the next surrounding :try +" gets the control. If this also has a :finally clause, it is +" executed afterwards. +"------------------------------------------------------------------------------- + +func T46_F() + let sum = 0 + + try + Xpath 'a' + try + Xpath 'b' + try + Xpath 'c' + try + Xpath 'd' + throw "ABC" + Xpath 'e' + catch /xyz/ + Xpath 'f' + finally + Xpath 'g' + if sum != 0 + Xpath 'h' + endif + let sum = sum + 1 + endtry + Xpath 'i' + catch /123/ + Xpath 'j' + catch /321/ + Xpath 'k' + finally + Xpath 'l' + if sum != 1 + Xpath 'm' + endif + let sum = sum + 2 + endtry + Xpath 'n' + finally + Xpath 'o' + if sum != 3 + Xpath 'p' + endif + let sum = sum + 4 + endtry + Xpath 'q' + catch /ABC/ + Xpath 'r' + if sum != 7 + Xpath 's' + endif + let sum = sum + 8 + finally + Xpath 't' + if sum != 15 + Xpath 'u' + endif + let sum = sum + 16 + endtry + Xpath 'v' + if sum != 31 + Xpath 'w' + endif +endfunc + +func Test_finally_after_throw() + XpathINIT + call T46_F() + call assert_equal('abcdglortv', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 47: Throwing exceptions from a :catch clause {{{1 +" +" When an exception is thrown from a :catch clause, it should not be +" caught by a :catch of the same :try conditional. After executing +" the :finally clause (if present), surrounding try conditionals +" should be checked for a matching :catch. +"------------------------------------------------------------------------------- + +func T47_F() + Xpath 'a' + try + Xpath 'b' + try + Xpath 'c' + try + Xpath 'd' + throw "x1" + Xpath 'e' + catch /x1/ + Xpath 'f' + try + Xpath 'g' + throw "x2" + Xpath 'h' + catch /x1/ + Xpath 'i' + catch /x2/ + Xpath 'j' + try + Xpath 'k' + throw "x3" + Xpath 'l' + catch /x1/ + Xpath 'm' + catch /x2/ + Xpath 'n' + finally + Xpath 'o' + endtry + Xpath 'p' + catch /x3/ + Xpath 'q' + endtry + Xpath 'r' + catch /x1/ + Xpath 's' + catch /x2/ + Xpath 't' + catch /x3/ + Xpath 'u' + finally + Xpath 'v' + endtry + Xpath 'w' + catch /x1/ + Xpath 'x' + catch /x2/ + Xpath 'y' + catch /x3/ + Xpath 'z' + endtry + Xpath 'A' + catch /.*/ + Xpath 'B' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + Xpath 'C' +endfunc + +func Test_throw_from_catch() + XpathINIT + call T47_F() + call assert_equal('abcdfgjkovzAC', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 48: Throwing exceptions from a :finally clause {{{1 +" +" When an exception is thrown from a :finally clause, it should not be +" caught by a :catch of the same :try conditional. Surrounding try +" conditionals should be checked for a matching :catch. A previously +" thrown exception is discarded. +"------------------------------------------------------------------------------- + +func T48_F() + try + + try + try + Xpath 'a' + catch /x1/ + Xpath 'b' + finally + Xpath 'c' + throw "x1" + Xpath 'd' + endtry + Xpath 'e' + catch /x1/ + Xpath 'f' + endtry + Xpath 'g' + + try + try + Xpath 'h' + throw "x2" + Xpath 'i' + catch /x2/ + Xpath 'j' + catch /x3/ + Xpath 'k' + finally + Xpath 'l' + throw "x3" + Xpath 'm' + endtry + Xpath 'n' + catch /x2/ + Xpath 'o' + catch /x3/ + Xpath 'p' + endtry + Xpath 'q' + + try + try + try + Xpath 'r' + throw "x4" + Xpath 's' + catch /x5/ + Xpath 't' + finally + Xpath 'u' + throw "x5" " discards 'x4' + Xpath 'v' + endtry + Xpath 'w' + catch /x4/ + Xpath 'x' + finally + Xpath 'y' + endtry + Xpath 'z' + catch /x5/ + Xpath 'A' + endtry + Xpath 'B' + + catch /.*/ + Xpath 'C' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + Xpath 'D' +endfunc + +func Test_throw_from_finally() + XpathINIT + call T48_F() + call assert_equal('acfghjlpqruyABD', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 51: Throwing exceptions across :execute and user commands {{{1 +" +" A :throw command may be executed under an ":execute" or from +" a user command. +"------------------------------------------------------------------------------- + +func T51_F() + command! -nargs=? THROW1 throw <args> | throw 1 + command! -nargs=? THROW2 try | throw <args> | endtry | throw 2 + command! -nargs=? THROW3 try | throw 3 | catch /3/ | throw <args> | endtry + command! -nargs=? THROW4 try | throw 4 | finally | throw <args> | endtry + + try + + try + try + Xpath 'a' + THROW1 "A" + catch /A/ + Xpath 'b' + endtry + catch /1/ + Xpath 'c' + endtry + + try + try + Xpath 'd' + THROW2 "B" + catch /B/ + Xpath 'e' + endtry + catch /2/ + Xpath 'f' + endtry + + try + try + Xpath 'g' + THROW3 "C" + catch /C/ + Xpath 'h' + endtry + catch /3/ + Xpath 'i' + endtry + + try + try + Xpath 'j' + THROW4 "D" + catch /D/ + Xpath 'k' + endtry + catch /4/ + Xpath 'l' + endtry + + try + try + Xpath 'm' + execute 'throw "E" | throw 5' + catch /E/ + Xpath 'n' + endtry + catch /5/ + Xpath 'o' + endtry + + try + try + Xpath 'p' + execute 'try | throw "F" | endtry | throw 6' + catch /F/ + Xpath 'q' + endtry + catch /6/ + Xpath 'r' + endtry + + try + try + Xpath 's' + execute'try | throw 7 | catch /7/ | throw "G" | endtry' + catch /G/ + Xpath 't' + endtry + catch /7/ + Xpath 'u' + endtry + + try + try + Xpath 'v' + execute 'try | throw 8 | finally | throw "H" | endtry' + catch /H/ + Xpath 'w' + endtry + catch /8/ + Xpath 'x' + endtry + + catch /.*/ + Xpath 'y' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + Xpath 'z' + + delcommand THROW1 + delcommand THROW2 + delcommand THROW3 + delcommand THROW4 +endfunc + +func Test_throw_across_commands() + XpathINIT + call T51_F() + call assert_equal('abdeghjkmnpqstvwz', g:Xpath) +endfunc + + + +"------------------------------------------------------------------------------- +" Test 69: :throw across :if, :elseif, :while {{{1 +" +" On an :if, :elseif, or :while command, an exception might be thrown +" during evaluation of the expression to test. The exception can be +" caught by the script. +"------------------------------------------------------------------------------- + +func T69_throw(x) + Xpath 'x' + throw a:x +endfunc + +func Test_throw_ifelsewhile() + XpathINIT + + try + try + Xpath 'a' + if 111 == T69_throw("if") + 111 + Xpath 'b' + else + Xpath 'c' + endif + Xpath 'd' + catch /^if$/ + Xpath 'e' + catch /.*/ + Xpath 'f' + call assert_report("if: " . v:exception . " in " . v:throwpoint) + endtry + + try + Xpath 'g' + if v:false + Xpath 'h' + elseif 222 == T69_throw("elseif") + 222 + Xpath 'i' + else + Xpath 'j' + endif + Xpath 'k' + catch /^elseif$/ + Xpath 'l' + catch /.*/ + Xpath 'm' + call assert_report("elseif: " . v:exception . " in " . v:throwpoint) + endtry + + try + Xpath 'n' + while 333 == T69_throw("while") + 333 + Xpath 'o' + break + endwhile + Xpath 'p' + catch /^while$/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report("while: " .. v:exception .. " in " .. v:throwpoint) + endtry + catch /^0$/ " default return value + Xpath 's' + call assert_report(v:throwpoint) + catch /.*/ + call assert_report(v:exception .. " in " .. v:throwpoint) + Xpath 't' + endtry + + call assert_equal('axegxlnxq', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 70: :throw across :return or :throw {{{1 +" +" On a :return or :throw command, an exception might be thrown during +" evaluation of the expression to return or throw, respectively. The +" exception can be caught by the script. +"------------------------------------------------------------------------------- + +let T70_taken = "" + +func T70_throw(x, n) + let g:T70_taken = g:T70_taken . "T" . a:n + throw a:x +endfunc + +func T70_F(x, y, n) + let g:T70_taken = g:T70_taken . "F" . a:n + return a:x + T70_throw(a:y, a:n) +endfunc + +func T70_G(x, y, n) + let g:T70_taken = g:T70_taken . "G" . a:n + throw a:x . T70_throw(a:y, a:n) + return a:x +endfunc + +func Test_throwreturn() + XpathINIT + + try + try + Xpath 'a' + call T70_F(4711, "return", 1) + Xpath 'b' + catch /^return$/ + Xpath 'c' + catch /.*/ + Xpath 'd' + call assert_report("return: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'e' + let var = T70_F(4712, "return-var", 2) + Xpath 'f' + catch /^return-var$/ + Xpath 'g' + catch /.*/ + Xpath 'h' + call assert_report("return-var: " . v:exception . " in " . v:throwpoint) + finally + unlet! var + endtry + + try + Xpath 'i' + throw "except1" . T70_throw("throw1", 3) + Xpath 'j' + catch /^except1/ + Xpath 'k' + catch /^throw1$/ + Xpath 'l' + catch /.*/ + Xpath 'm' + call assert_report("throw1: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'n' + call T70_G("except2", "throw2", 4) + Xpath 'o' + catch /^except2/ + Xpath 'p' + catch /^throw2$/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report("throw2: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 's' + let var = T70_G("except3", "throw3", 5) + Xpath 't' + catch /^except3/ + Xpath 'u' + catch /^throw3$/ + Xpath 'v' + catch /.*/ + Xpath 'w' + call assert_report("throw3: " .. v:exception .. " in " .. v:throwpoint) + finally + unlet! var + endtry + + call assert_equal('F1T1F2T2T3G4T4G5T5', g:T70_taken) + Xpath 'x' + catch /^0$/ " default return value + Xpath 'y' + call assert_report(v:throwpoint) + catch /.*/ + Xpath 'z' + call assert_report('Caught' .. v:exception .. ' in ' .. v:throwpoint) + endtry + + call assert_equal('acegilnqsvx', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 71: :throw across :echo variants and :execute {{{1 +" +" On an :echo, :echon, :echomsg, :echoerr, or :execute command, an +" exception might be thrown during evaluation of the arguments to +" be displayed or executed as a command, respectively. Any following +" arguments are not evaluated, then. The exception can be caught by +" the script. +"------------------------------------------------------------------------------- + +let T71_taken = "" + +func T71_throw(x, n) + let g:T71_taken = g:T71_taken . "T" . a:n + throw a:x +endfunc + +func T71_F(n) + let g:T71_taken = g:T71_taken . "F" . a:n + return "F" . a:n +endfunc + +func Test_throw_echo() + XpathINIT + + try + try + Xpath 'a' + echo 'echo ' . T71_throw("echo-except", 1) . T71_F(1) + Xpath 'b' + catch /^echo-except$/ + Xpath 'c' + catch /.*/ + Xpath 'd' + call assert_report("echo: " .. v:exception .. " in " .. v:throwpoint) + endtry + + try + Xpath 'e' + echon "echon " . T71_throw("echon-except", 2) . T71_F(2) + Xpath 'f' + catch /^echon-except$/ + Xpath 'g' + catch /.*/ + Xpath 'h' + call assert_report('echon: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'i' + echomsg "echomsg " . T71_throw("echomsg-except", 3) . T71_F(3) + Xpath 'j' + catch /^echomsg-except$/ + Xpath 'k' + catch /.*/ + Xpath 'l' + call assert_report('echomsg: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 'm' + echoerr "echoerr " . T71_throw("echoerr-except", 4) . T71_F(4) + Xpath 'n' + catch /^echoerr-except$/ + Xpath 'o' + catch /Vim/ + Xpath 'p' + catch /echoerr/ + Xpath 'q' + catch /.*/ + Xpath 'r' + call assert_report('echoerr: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + Xpath 's' + execute "echo 'execute " . T71_throw("execute-except", 5) . T71_F(5) "'" + Xpath 't' + catch /^execute-except$/ + Xpath 'u' + catch /.*/ + Xpath 'v' + call assert_report('execute: ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('T1T2T3T4T5', g:T71_taken) + Xpath 'w' + catch /^0$/ " default return value + Xpath 'x' + call assert_report(v:throwpoint) + catch /.*/ + Xpath 'y' + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acegikmosuw', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 72: :throw across :let or :unlet {{{1 +" +" On a :let command, an exception might be thrown during evaluation +" of the expression to assign. On an :let or :unlet command, the +" evaluation of the name of the variable to be assigned or list or +" deleted, respectively, may throw an exception. Any following +" arguments are not evaluated, then. The exception can be caught by +" the script. +"------------------------------------------------------------------------------- + +let throwcount = 0 + +func T72_throw(x) + let g:throwcount = g:throwcount + 1 + throw a:x +endfunc + +let T72_addpath = '' + +func T72_addpath(p) + let g:T72_addpath = g:T72_addpath . a:p +endfunc + +func Test_throw_let() + XpathINIT + + try + try + let $VAR = 'old_value' + Xpath 'a' + let $VAR = 'let(' . T72_throw('var') . ')' + Xpath 'b' + catch /^var$/ + Xpath 'c' + finally + call assert_equal('old_value', $VAR) + endtry + + try + let @a = 'old_value' + Xpath 'd' + let @a = 'let(' . T72_throw('reg') . ')' + Xpath 'e' + catch /^reg$/ + try + Xpath 'f' + let @A = 'let(' . T72_throw('REG') . ')' + Xpath 'g' + catch /^REG$/ + Xpath 'h' + endtry + finally + call assert_equal('old_value', @a) + call assert_equal('old_value', @A) + endtry + + try + let saved_gpath = &g:path + let saved_lpath = &l:path + Xpath 'i' + let &path = 'let(' . T72_throw('opt') . ')' + Xpath 'j' + catch /^opt$/ + try + Xpath 'k' + let &g:path = 'let(' . T72_throw('gopt') . ')' + Xpath 'l' + catch /^gopt$/ + try + Xpath 'm' + let &l:path = 'let(' . T72_throw('lopt') . ')' + Xpath 'n' + catch /^lopt$/ + Xpath 'o' + endtry + endtry + finally + call assert_equal(saved_gpath, &g:path) + call assert_equal(saved_lpath, &l:path) + let &g:path = saved_gpath + let &l:path = saved_lpath + endtry + + unlet! var1 var2 var3 + + try + Xpath 'p' + let var1 = 'let(' . T72_throw('var1') . ')' + Xpath 'q' + catch /^var1$/ + Xpath 'r' + finally + call assert_true(!exists('var1')) + endtry + + try + let var2 = 'old_value' + Xpath 's' + let var2 = 'let(' . T72_throw('var2'). ')' + Xpath 't' + catch /^var2$/ + Xpath 'u' + finally + call assert_equal('old_value', var2) + endtry + + try + Xpath 'v' + let var{T72_throw('var3')} = 4711 + Xpath 'w' + catch /^var3$/ + Xpath 'x' + endtry + + try + call T72_addpath('T1') + let var{T72_throw('var4')} var{T72_addpath('T2')} | call T72_addpath('T3') + call T72_addpath('T4') + catch /^var4$/ + call T72_addpath('T5') + endtry + + try + call T72_addpath('T6') + unlet var{T72_throw('var5')} var{T72_addpath('T7')} + \ | call T72_addpath('T8') + call T72_addpath('T9') + catch /^var5$/ + call T72_addpath('T10') + endtry + + call assert_equal('T1T5T6T10', g:T72_addpath) + call assert_equal(11, g:throwcount) + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acdfhikmoprsuvx', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 73: :throw across :function, :delfunction {{{1 +" +" The :function and :delfunction commands may cause an expression +" specified in braces to be evaluated. During evaluation, an +" exception might be thrown. The exception can be caught by the +" script. +"------------------------------------------------------------------------------- + +let T73_taken = '' + +func T73_throw(x, n) + let g:T73_taken = g:T73_taken . 'T' . a:n + throw a:x +endfunc + +func T73_expr(x, n) + let g:T73_taken = g:T73_taken . 'E' . a:n + if a:n % 2 == 0 + call T73_throw(a:x, a:n) + endif + return 2 - a:n % 2 +endfunc + +func Test_throw_func() + XpathINIT + + try + try + " Define function. + Xpath 'a' + function! F0() + endfunction + Xpath 'b' + function! F{T73_expr('function-def-ok', 1)}() + endfunction + Xpath 'c' + function! F{T73_expr('function-def', 2)}() + endfunction + Xpath 'd' + catch /^function-def-ok$/ + Xpath 'e' + catch /^function-def$/ + Xpath 'f' + catch /.*/ + call assert_report('def: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + " List function. + Xpath 'g' + function F0 + Xpath 'h' + function F{T73_expr('function-lst-ok', 3)} + Xpath 'i' + function F{T73_expr('function-lst', 4)} + Xpath 'j' + catch /^function-lst-ok$/ + Xpath 'k' + catch /^function-lst$/ + Xpath 'l' + catch /.*/ + call assert_report('lst: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + " Delete function + Xpath 'm' + delfunction F0 + Xpath 'n' + delfunction F{T73_expr('function-del-ok', 5)} + Xpath 'o' + delfunction F{T73_expr('function-del', 6)} + Xpath 'p' + catch /^function-del-ok$/ + Xpath 'q' + catch /^function-del$/ + Xpath 'r' + catch /.*/ + call assert_report('del: ' . v:exception . ' in ' . v:throwpoint) + endtry + call assert_equal('E1E2T2E3E4T4E5E6T6', g:T73_taken) + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('abcfghilmnor', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 74: :throw across builtin functions and commands {{{1 +" +" Some functions like exists(), searchpair() take expression +" arguments, other functions or commands like substitute() or +" :substitute cause an expression (specified in the regular +" expression) to be evaluated. During evaluation an exception +" might be thrown. The exception can be caught by the script. +"------------------------------------------------------------------------------- + +let T74_taken = "" + +func T74_throw(x, n) + let g:T74_taken = g:T74_taken . "T" . a:n + throw a:x +endfunc + +func T74_expr(x, n) + let g:T74_taken = g:T74_taken . "E" . a:n + call T74_throw(a:x . a:n, a:n) + return "EXPR" +endfunc + +func T74_skip(x, n) + let g:T74_taken = g:T74_taken . "S" . a:n . "(" . line(".") + let theline = getline(".") + if theline =~ "skip" + let g:T74_taken = g:T74_taken . "s)" + return 1 + elseif theline =~ "throw" + let g:T74_taken = g:T74_taken . "t)" + call T74_throw(a:x . a:n, a:n) + else + let g:T74_taken = g:T74_taken . ")" + return 0 + endif +endfunc + +func T74_subst(x, n) + let g:T74_taken = g:T74_taken . "U" . a:n . "(" . line(".") + let theline = getline(".") + if theline =~ "not" " T74_subst() should not be called for this line + let g:T74_taken = g:T74_taken . "n)" + call T74_throw(a:x . a:n, a:n) + elseif theline =~ "throw" + let g:T74_taken = g:T74_taken . "t)" + call T74_throw(a:x . a:n, a:n) + else + let g:T74_taken = g:T74_taken . ")" + return "replaced" + endif +endfunc + +func Test_throw_builtin_func() + XpathINIT + + try + try + Xpath 'a' + let result = exists('*{T74_expr("exists", 1)}') + Xpath 'b' + catch /^exists1$/ + Xpath 'c' + try + let result = exists('{T74_expr("exists", 2)}') + Xpath 'd' + catch /^exists2$/ + Xpath 'e' + catch /.*/ + call assert_report('exists2: ' . v:exception . ' in ' . v:throwpoint) + endtry + catch /.*/ + call assert_report('exists1: ' . v:exception . ' in ' . v:throwpoint) + endtry + + try + let file = tempname() + exec "edit" file + call append(0, [ + \ 'begin', + \ 'xx', + \ 'middle 3', + \ 'xx', + \ 'middle 5 skip', + \ 'xx', + \ 'middle 7 throw', + \ 'xx', + \ 'end']) + normal! gg + Xpath 'f' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 3)') + Xpath 'g' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 4)') + Xpath 'h' + let result = searchpair("begin", "middle", "end", '', + \ 'T74_skip("searchpair", 5)') + Xpath 'i' + catch /^searchpair[35]$/ + Xpath 'j' + catch /^searchpair4$/ + Xpath 'k' + catch /.*/ + call assert_report('searchpair: ' . v:exception . ' in ' . v:throwpoint) + finally + bwipeout! + call delete(file) + endtry + + try + let file = tempname() + exec "edit" file + call append(0, [ + \ 'subst 1', + \ 'subst 2', + \ 'not', + \ 'subst 4', + \ 'subst throw', + \ 'subst 6']) + normal! gg + Xpath 'l' + 1,2substitute/subst/\=T74_subst("substitute", 6)/ + try + Xpath 'm' + try + let v:errmsg = "" + 3substitute/subst/\=T74_subst("substitute", 7)/ + finally + if v:errmsg != "" + " If exceptions are not thrown on errors, fake the error + " exception in order to get the same execution path. + throw "faked Vim(substitute)" + endif + endtry + catch /Vim(substitute)/ " Pattern not found ('e' flag missing) + Xpath 'n' + 3substitute/subst/\=T74_subst("substitute", 8)/e + Xpath 'o' + endtry + Xpath 'p' + 4,6substitute/subst/\=T74_subst("substitute", 9)/ + Xpath 'q' + catch /^substitute[678]/ + Xpath 'r' + catch /^substitute9/ + Xpath 's' + finally + bwipeout! + call delete(file) + endtry + + try + Xpath 't' + let var = substitute("sub", "sub", '\=T74_throw("substitute()y", 10)', '') + Xpath 'u' + catch /substitute()y/ + Xpath 'v' + catch /.*/ + call assert_report('substitute()y: ' . v:exception . ' in ' + \ . v:throwpoint) + endtry + + try + Xpath 'w' + let var = substitute("not", "sub", '\=T74_throw("substitute()n", 11)', '') + Xpath 'x' + catch /substitute()n/ + Xpath 'y' + catch /.*/ + call assert_report('substitute()n: ' . v:exception . ' in ' + \ . v:throwpoint) + endtry + + call assert_equal('E1T1E2T2S3(3)S4(5s)S4(7t)T4U6(1)U6(2)U9(4)U9(5t)T9T10', + \ g:T74_taken) + + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + endtry + + call assert_equal('acefgklmnopstvwx', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 75: Errors in builtin functions. {{{1 +" +" On an error in a builtin function called inside a :try/:endtry +" region, the evaluation of the expression calling that function and +" the command containing that expression are abandoned. The error can +" be caught as an exception. +" +" A simple :call of the builtin function is a trivial case. If the +" builtin function is called in the argument list of another function, +" no further arguments are evaluated, and the other function is not +" executed. If the builtin function is called from the argument of +" a :return command, the :return command is not executed. If the +" builtin function is called from the argument of a :throw command, +" the :throw command is not executed. The evaluation of the +" expression calling the builtin function is abandoned. +"------------------------------------------------------------------------------- + +func T75_F1(arg1) + Xpath 'a' +endfunc + +func T75_F2(arg1, arg2) + Xpath 'b' +endfunc + +func T75_G() + Xpath 'c' +endfunc + +func T75_H() + Xpath 'd' +endfunc + +func T75_R() + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'e' + return append(1, "s") + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'f' + finally + Xpath 'g' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'h' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + Xpath 'i' +endfunc + +func Test_builtin_func_error() + XpathINIT + + try + set noma " let append() fail with "E21" + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'j' + call append(1, "s") + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'k' + finally + Xpath 'l' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'm' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'n' + call T75_F1('x' . append(1, "s")) + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'o' + finally + Xpath 'p' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'q' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'r' + call T75_F2('x' . append(1, "s"), T75_G()) + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 's' + finally + Xpath 't' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'u' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + call T75_R() + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'v' + throw "T" . append(1, "s") + catch /E21/ + let caught = 1 + catch /^T.*/ + Xpath 'w' + catch /.*/ + Xpath 'x' + finally + Xpath 'y' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'z' + endif + break " discard error for $VIMNOERRTHROW + endtry + endwhile + + while 1 + try + let caught = 0 + let v:errmsg = "" + Xpath 'A' + let x = "a" + let x = x . "b" . append(1, "s") . T75_H() + catch /E21/ + let caught = 1 + catch /.*/ + Xpath 'B' + finally + Xpath 'C' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + Xpath 'D' + endif + call assert_equal('a', x) + break " discard error for $VIMNOERRTHROW + endtry + endwhile + catch /.*/ + call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint) + finally + set ma& + endtry + + call assert_equal('jlmnpqrtueghivyzACD', g:Xpath) +endfunc + +" Modelines {{{1 +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker +"------------------------------------------------------------------------------- diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 0a89066a2b..4621207d19 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -393,9 +393,13 @@ func Test_addr_all() call assert_equal(len(gettabinfo()), g:a2) bwipe - command! -addr=other DoSomething echo 'nothing' + command! -addr=other DoSomething let g:a1 = <line1> | let g:a2 = <line2> DoSomething - call assert_fails('%DoSomething') + call assert_equal(line('.'), g:a1) + call assert_equal(line('.'), g:a2) + %DoSomething + call assert_equal(1, g:a1) + call assert_equal(line('$'), g:a2) delcommand DoSomething endfunc @@ -421,7 +425,7 @@ func Test_command_list() \ execute('command DoCmd')) command! -count=2 DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 2c :", + \ .. "\n DoCmd 0 2c ? :", \ execute('command DoCmd')) " Test with various -addr= argument values. diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 8302ccb67f..e8161f8fcb 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -103,3 +103,14 @@ func Test_list2str_str2list_latin1() call assert_equal(l, lres) call assert_equal(s, sres) endfunc + +func Test_print_overlong() + " Text with more composing characters than MB_MAXBYTES. + new + call setline(1, 'axxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') + s/x/\=nr2char(1629)/g + print + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 4edf8308e7..43907de1d9 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -919,9 +919,203 @@ func Test_if_bar_fail() call assert_equal('acdfh-acfh', g:test15_result) endfunc +"------------------------------------------------------------------------------- +" Test 16: Double :else or :elseif after :else {{{1 +" +" Multiple :elses or an :elseif after an :else are forbidden. +"------------------------------------------------------------------------------- + +func T16_F() abort + if 0 + Xpath 'a' + else + Xpath 'b' + else " aborts function + Xpath 'c' + endif + Xpath 'd' +endfunc + +func T16_G() abort + if 0 + Xpath 'a' + else + Xpath 'b' + elseif 1 " aborts function + Xpath 'c' + else + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_H() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + else " aborts function + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_I() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + elseif 1 " aborts function + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' +endfunc + +func Test_Multi_Else() + XpathINIT + try + call T16_F() + catch /E583:/ + Xpath 'e' + endtry + call assert_equal('be', g:Xpath) + + XpathINIT + try + call T16_G() + catch /E584:/ + Xpath 'f' + endtry + call assert_equal('bf', g:Xpath) + + XpathINIT + try + call T16_H() + catch /E583:/ + Xpath 'f' + endtry + call assert_equal('cf', g:Xpath) + + XpathINIT + try + call T16_I() + catch /E584:/ + Xpath 'g' + endtry + call assert_equal('cg', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 +" +" The :while/:endwhile takes precedence in nesting over an unclosed +" :if or an unopened :endif. +"------------------------------------------------------------------------------- + +" While loops inside a function are continued on error. +func T17_F() + let loops = 3 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if (loops == 1) + Xpath 'b' . loops + continue + elseif (loops == 0) + Xpath 'c' . loops + break + elseif 1 + Xpath 'd' . loops + " endif missing! + endwhile " :endwhile after :if 1 + Xpath 'e' +endfunc + +func T17_G() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing + endwhile " :endwhile after :if 0 +endfunc + +func T17_H() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + " if missing! + endif " :endif without :if in while + Xpath 'b' . loops + endwhile +endfunc + +" Error continuation outside a function is at the outermost :endwhile or :endif. +XpathINIT +let v:errmsg = '' +let loops = 2 +while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing! Following :endwhile fails. +endwhile | Xpath 'c' +Xpath 'd' +call assert_match('E171:', v:errmsg) +call assert_equal('a1d', g:Xpath) + +func Test_unmatched_if_in_while() + XpathINIT + call assert_fails('call T17_F()', 'E171:') + call assert_equal('a2d2a1b1a0c0e', g:Xpath) + + XpathINIT + call assert_fails('call T17_G()', 'E171:') + call assert_equal('a1a0', g:Xpath) + + XpathINIT + call assert_fails('call T17_H()', 'E580:') + call assert_equal('a1b1a0b0', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +"------------------------------------------------------------------------------- +"------------------------------------------------------------------------------- +" Test 87 using (expr) ? funcref : funcref {{{1 +" +" Vim needs to correctly parse the funcref and even when it does +" not execute the funcref, it needs to consume the trailing () +"------------------------------------------------------------------------------- + +func Add2(x1, x2) + return a:x1 + a:x2 +endfu + +func GetStr() + return "abcdefghijklmnopqrstuvwxyp" +endfu + +func Test_funcref_with_condexpr() + call assert_equal(5, function('Add2')(2,3)) + + call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3)) + call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3)) + " Make sure, GetStr() still works. + call assert_equal('abcdefghijk', GetStr()[0:10]) +endfunc "------------------------------------------------------------------------------- -" Test 16: Recognizing {} in variable name. {{{1 +" Test 90: Recognizing {} in variable name. {{{1 "------------------------------------------------------------------------------- func Test_curlies() @@ -1490,5 +1684,5 @@ endfunc "------------------------------------------------------------------------------- " Modelines {{{1 -" vim: ts=8 sw=4 tw=80 fdm=marker +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker "------------------------------------------------------------------------------- diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index c630e678fd..687b1cb989 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -850,6 +850,14 @@ func Test_window_resize() exe other_winnr .. 'resize +1' call assert_equal(12, winheight(other_winnr)) call assert_equal(&lines - 10 - 3 -2, winheight(0)) + close + + vsplit + wincmd l + let other_winnr = winnr('h') + call assert_notequal(winnr(), other_winnr) + exe 'vert ' .. other_winnr .. 'resize -' .. &columns + call assert_equal(0, winwidth(other_winnr)) %bwipe! endfunc diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 685da77b39..c6c09c80d7 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -61,6 +61,9 @@ static bool pending_mode_info_update = false; static bool pending_mode_update = false; static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; +static bool has_mouse = false; +static int pending_has_mouse = -1; + #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL # define UI_LOG(funname) #else @@ -220,6 +223,7 @@ void ui_refresh(void) ui_mode_info_set(); pending_mode_update = true; ui_cursor_shape(); + pending_has_mouse = -1; } int ui_pum_get_height(void) @@ -459,10 +463,69 @@ void ui_flush(void) ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx); pending_mode_update = false; } + if (pending_has_mouse != has_mouse) { + (has_mouse ? ui_call_mouse_on : ui_call_mouse_off)(); + pending_has_mouse = has_mouse; + } ui_call_flush(); } + +/// Check if 'mouse' is active for the current mode +/// +/// TODO(bfredl): precompute the State -> active mapping when 'mouse' changes, +/// then this can be checked directly in ui_flush() +void ui_check_mouse(void) +{ + has_mouse = false; + // Be quick when mouse is off. + if (*p_mouse == NUL) { + return; + } + + int checkfor = MOUSE_NORMAL; // assume normal mode + if (VIsual_active) { + checkfor = MOUSE_VISUAL; + } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) { + checkfor = MOUSE_RETURN; + } else if (State & INSERT) { + checkfor = MOUSE_INSERT; + } else if (State & CMDLINE) { + checkfor = MOUSE_COMMAND; + } else if (State == CONFIRM || State == EXTERNCMD) { + checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd" + } + + // mouse should be active if at least one of the following is true: + // - "c" is in 'mouse', or + // - 'a' is in 'mouse' and "c" is in MOUSE_A, or + // - the current buffer is a help file and 'h' is in 'mouse' and we are in a + // normal editing mode (not at hit-return message). + for (char_u *p = p_mouse; *p; p++) { + switch (*p) { + case 'a': + if (vim_strchr((char_u *)MOUSE_A, checkfor) != NULL) { + has_mouse = true; + return; + } + break; + case MOUSE_HELP: + if (checkfor != MOUSE_RETURN && curbuf->b_help) { + has_mouse = true; + return; + } + break; + default: + if (checkfor == *p) { + has_mouse = true; + return; + } + } + } +} + /// Check if current mode has changed. +/// /// May update the shape of the cursor. void ui_cursor_shape(void) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index a8b8f7aa50..da464c56dc 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,8 @@ #include <string.h> #include <fcntl.h> +#include "auto/config.h" + #include "nvim/buffer.h" #include "nvim/ascii.h" #include "nvim/change.h" @@ -1255,7 +1257,8 @@ theend: /// a bit more verbose. /// Otherwise use curbuf->b_ffname to generate the undo file name. /// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text. -void u_read_undo(char *name, char_u *hash, char_u *orig_name) +void u_read_undo(char *name, const char_u *hash, + const char_u *orig_name FUNC_ATTR_UNUSED) FUNC_ATTR_NONNULL_ARG(2) { u_header_T **uhp_table = NULL; @@ -1273,7 +1276,7 @@ void u_read_undo(char *name, char_u *hash, char_u *orig_name) // owner of the text file or equal to the current user. FileInfo file_info_orig; FileInfo file_info_undo; - if (os_fileinfo((char *)orig_name, &file_info_orig) + if (os_fileinfo((const char *)orig_name, &file_info_orig) && os_fileinfo((char *)file_name, &file_info_undo) && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid && file_info_undo.stat.st_uid != getuid()) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 6be2a61c6a..834d27cc84 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -16,7 +16,6 @@ #include "nvim/iconv.h" #include "nvim/version.h" #include "nvim/charset.h" -#include "nvim/macros.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" diff --git a/src/nvim/version.h b/src/nvim/version.h index c10f6fa534..4af038dd6c 100644 --- a/src/nvim/version.h +++ b/src/nvim/version.h @@ -2,6 +2,7 @@ #define NVIM_VERSION_H #include "nvim/ex_cmds_defs.h" +#include "nvim/macros.h" // defined in version.c extern char* Version; @@ -10,14 +11,20 @@ extern char* longVersion; // // Vim version number, name, etc. Patchlevel is defined in version.c. // + +// Values that change for a new release #define VIM_VERSION_MAJOR 8 #define VIM_VERSION_MINOR 0 + +// Values based on the above +#define VIM_VERSION_MAJOR_STR STR(VIM_VERSION_MAJOR) +#define VIM_VERSION_MINOR_STR STR(VIM_VERSION_MINOR) #define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR) // used for the runtime directory name -#define VIM_VERSION_NODOT "vim80" +#define VIM_VERSION_NODOT "vim" VIM_VERSION_MAJOR_STR VIM_VERSION_MINOR_STR // swap file compatibility (max. length is 6 chars) -#define VIM_VERSION_SHORT "8.0" +#define VIM_VERSION_SHORT VIM_VERSION_MAJOR_STR "." VIM_VERSION_MINOR_STR #ifdef INCLUDE_GENERATED_DECLARATIONS # include "version.h.generated.h" diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 900f2acd81..01f20cf29a 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -208,6 +208,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // Size in bytes of the hash used in the undo file. #define UNDO_HASH_SIZE 32 +#define CLEAR_POINTER(ptr) memset((ptr), 0, sizeof(*(ptr))) // defines to avoid typecasts from (char_u *) to (char *) and back // (vim_strchr() is now in strings.c) diff --git a/src/nvim/window.c b/src/nvim/window.c index 72ee400e40..00f49724b6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -641,7 +641,7 @@ void win_set_minimal_style(win_T *wp) wp->w_p_scl = (char_u *)xstrdup("auto"); } - // foldcolumn: use 'auto' + // foldcolumn: use '0' if (wp->w_p_fdc[0] != '0') { xfree(wp->w_p_fdc); wp->w_p_fdc = (char_u *)xstrdup("0"); @@ -700,9 +700,10 @@ int win_fdccol_count(win_T *wp) const char *fdc = (const char *)wp->w_p_fdc; // auto:<NUM> - if (strncmp(fdc, "auto:", 5) == 0) { + if (strncmp(fdc, "auto", 4) == 0) { + const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1; int needed_fdccols = getDeepestNesting(wp); - return MIN(fdc[5] - '0', needed_fdccols); + return MIN(fdccol, needed_fdccols); } else { return fdc[0] - '0'; } @@ -1636,6 +1637,19 @@ bool win_valid(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +// Find window "handle" in the current tab page. +// Return NULL if not found. +win_T *win_find_by_handle(handle_T handle) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->handle == handle) { + return wp; + } + } + return NULL; +} + /// Check if "win" is a pointer to an existing window in any tabpage. /// /// @param win window to check @@ -3787,32 +3801,35 @@ void free_tabpage(tabpage_T *tp) /// @return Was the new tabpage created successfully? FAIL or OK. int win_new_tabpage(int after, char_u *filename) { - tabpage_T *tp = curtab; + tabpage_T *old_curtab = curtab; tabpage_T *newtp; int n; newtp = alloc_tabpage(); - /* Remember the current windows in this Tab page. */ - if (leave_tabpage(curbuf, TRUE) == FAIL) { + // Remember the current windows in this Tab page. + if (leave_tabpage(curbuf, true) == FAIL) { xfree(newtp); return FAIL; } - newtp->tp_localdir = tp->tp_localdir ? vim_strsave(tp->tp_localdir) : NULL; + newtp->tp_localdir = old_curtab->tp_localdir + ? vim_strsave(old_curtab->tp_localdir) : NULL; curtab = newtp; - /* Create a new empty window. */ - if (win_alloc_firstwin(tp->tp_curwin) == OK) { - /* Make the new Tab page the new topframe. */ + // Create a new empty window. + if (win_alloc_firstwin(old_curtab->tp_curwin) == OK) { + // Make the new Tab page the new topframe. if (after == 1) { - /* New tab page becomes the first one. */ + // New tab page becomes the first one. newtp->tp_next = first_tabpage; first_tabpage = newtp; } else { + tabpage_T *tp = old_curtab; + if (after > 0) { - /* Put new tab page before tab page "after". */ + // Put new tab page before tab page "after". n = 2; for (tp = first_tabpage; tp->tp_next != NULL && n < after; tp = tp->tp_next) @@ -3826,13 +3843,13 @@ int win_new_tabpage(int after, char_u *filename) win_comp_scroll(curwin); newtp->tp_topframe = topframe; - last_status(FALSE); + last_status(false); redraw_all_later(NOT_VALID); - tabpage_check_windows(tp); + tabpage_check_windows(old_curtab); - lastused_tabpage = tp; + lastused_tabpage = old_curtab; apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); @@ -3842,8 +3859,8 @@ int win_new_tabpage(int after, char_u *filename) return OK; } - /* Failed, get back the previous Tab page */ - enter_tabpage(curtab, curbuf, TRUE, TRUE); + // Failed, get back the previous Tab page + enter_tabpage(curtab, curbuf, true, true); return FAIL; } @@ -4523,7 +4540,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, // Might need to scroll the old window before switching, e.g., when the // cursor was moved. - update_topline(); + update_topline(curwin); // may have to copy the buffer options when 'cpo' contains 'S' if (wp->w_buffer != curbuf) { @@ -4996,7 +5013,10 @@ void win_size_save(garray_T *gap) { ga_init(gap, (int)sizeof(int), 1); - ga_grow(gap, win_count() * 2); + ga_grow(gap, win_count() * 2 + 1); + // first entry is value of 'lines' + ((int *)gap->ga_data)[gap->ga_len++] = Rows; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { ((int *)gap->ga_data)[gap->ga_len++] = wp->w_width + wp->w_vsep_width; @@ -5004,18 +5024,18 @@ void win_size_save(garray_T *gap) } } -/* - * Restore window sizes, but only if the number of windows is still the same. - * Does not free the growarray. - */ +// Restore window sizes, but only if the number of windows is still the same +// and 'lines' didn't change. +// Does not free the growarray. void win_size_restore(garray_T *gap) + FUNC_ATTR_NONNULL_ALL { - if (win_count() * 2 == gap->ga_len) { - /* The order matters, because frames contain other frames, but it's - * difficult to get right. The easy way out is to do it twice. */ - for (int j = 0; j < 2; ++j) - { - int i = 0; + if (win_count() * 2 + 1 == gap->ga_len + && ((int *)gap->ga_data)[0] == Rows) { + // The order matters, because frames contain other frames, but it's + // difficult to get right. The easy way out is to do it twice. + for (int j = 0; j < 2; j++) { + int i = 1; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { int width = ((int *)gap->ga_data)[i++]; int height = ((int *)gap->ga_data)[i++]; @@ -5329,6 +5349,8 @@ void win_setwidth_win(int width, win_T *wp) width = p_wmw; if (width == 0) width = 1; + } else if (width < 0) { + width = 0; } if (wp->w_floating) { wp->w_float_config.width = width; @@ -5856,10 +5878,10 @@ void scroll_to_fraction(win_T *wp, int prev_height) } if (wp == curwin) { - if (get_scrolloff_value()) { - update_topline(); + if (get_scrolloff_value(wp)) { + update_topline(wp); } - curs_columns(false); // validate w_wrow + curs_columns(wp, false); // validate w_wrow } if (prev_height > 0) { wp->w_prev_fraction_row = wp->w_wrow; @@ -5915,8 +5937,8 @@ void win_set_inner_size(win_T *wp) changed_line_abv_curs_win(wp); invalidate_botline_win(wp); if (wp == curwin) { - update_topline(); - curs_columns(true); // validate w_wrow + update_topline(wp); + curs_columns(wp, true); // validate w_wrow } redraw_later(wp, NOT_VALID); } |