aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Doxyfile6
-rwxr-xr-xsrc/clint.py2
-rw-r--r--src/nvim/api/buffer.c116
-rw-r--r--src/nvim/api/private/defs.h11
-rw-r--r--src/nvim/api/private/helpers.c226
-rw-r--r--src/nvim/api/private/helpers.h1
-rw-r--r--src/nvim/api/ui.c79
-rw-r--r--src/nvim/api/ui_events.in.h6
-rw-r--r--src/nvim/api/vim.c358
-rw-r--r--src/nvim/api/window.c71
-rw-r--r--src/nvim/auevents.lua4
-rw-r--r--src/nvim/buffer.c1640
-rw-r--r--src/nvim/buffer.h9
-rw-r--r--src/nvim/buffer_defs.h140
-rw-r--r--src/nvim/charset.c7
-rw-r--r--src/nvim/edit.c274
-rw-r--r--src/nvim/eval.c551
-rw-r--r--src/nvim/eval.lua8
-rw-r--r--src/nvim/eval/typval.c28
-rw-r--r--src/nvim/event/rstream.c10
-rw-r--r--src/nvim/event/stream.c10
-rw-r--r--src/nvim/event/stream.h7
-rw-r--r--src/nvim/ex_cmds.c49
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_cmds2.c45
-rw-r--r--src/nvim/ex_docmd.c80
-rw-r--r--src/nvim/ex_eval.c7
-rw-r--r--src/nvim/ex_getln.c85
-rw-r--r--src/nvim/file_search.c11
-rw-r--r--src/nvim/fileio.c283
-rw-r--r--src/nvim/fileio.h1
-rw-r--r--src/nvim/func_attr.h5
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua73
-rw-r--r--src/nvim/generators/gen_unicode_tables.lua2
-rw-r--r--src/nvim/getchar.c847
-rw-r--r--src/nvim/getchar.h34
-rw-r--r--src/nvim/gettext.h5
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/highlight.c7
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/if_cscope.c4
-rw-r--r--src/nvim/keymap.c27
-rw-r--r--src/nvim/keymap.h56
-rw-r--r--src/nvim/lib/kbtree.h20
-rw-r--r--src/nvim/log.c21
-rw-r--r--src/nvim/lua/executor.c27
-rw-r--r--src/nvim/lua/vim.lua124
-rw-r--r--src/nvim/main.c31
-rw-r--r--src/nvim/map.c12
-rw-r--r--src/nvim/mark.c103
-rw-r--r--src/nvim/mbyte.c159
-rw-r--r--src/nvim/memline.c268
-rw-r--r--src/nvim/menu.c31
-rw-r--r--src/nvim/message.c38
-rw-r--r--src/nvim/misc1.c28
-rw-r--r--src/nvim/move.c50
-rw-r--r--src/nvim/msgpack_rpc/channel.c67
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h7
-rw-r--r--src/nvim/msgpack_rpc/helpers.c39
-rw-r--r--src/nvim/normal.c18
-rw-r--r--src/nvim/ops.c57
-rw-r--r--src/nvim/option.c2317
-rw-r--r--src/nvim/option_defs.h24
-rw-r--r--src/nvim/options.lua35
-rw-r--r--src/nvim/os/env.c43
-rw-r--r--src/nvim/os/fs.c115
-rw-r--r--src/nvim/os/process.c6
-rw-r--r--src/nvim/os/stdpaths.c10
-rw-r--r--src/nvim/os/stdpaths_defs.h1
-rw-r--r--src/nvim/path.c9
-rw-r--r--src/nvim/popupmnu.c57
-rw-r--r--src/nvim/popupmnu.h1
-rw-r--r--src/nvim/quickfix.c2400
-rw-r--r--src/nvim/regexp.c16
-rw-r--r--src/nvim/regexp_defs.h6
-rw-r--r--src/nvim/regexp_nfa.c23
-rw-r--r--src/nvim/screen.c296
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/shada.c12
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--src/nvim/spell.c17
-rw-r--r--src/nvim/spellfile.c10
-rw-r--r--src/nvim/strings.c16
-rw-r--r--src/nvim/syntax.c43
-rw-r--r--src/nvim/tag.c331
-rw-r--r--src/nvim/terminal.c153
-rw-r--r--src/nvim/testdir/Makefile10
-rw-r--r--src/nvim/testdir/load.vim6
-rw-r--r--src/nvim/testdir/test14.in94
-rw-r--r--src/nvim/testdir/test14.ok26
-rw-r--r--src/nvim/testdir/test16.in15
-rw-r--r--src/nvim/testdir/test16.ok2
-rw-r--r--src/nvim/testdir/test17.in126
-rw-r--r--src/nvim/testdir/test17.ok33
-rw-r--r--src/nvim/testdir/test17a.in3
-rw-r--r--src/nvim/testdir/test37.in116
-rw-r--r--src/nvim/testdir/test37.ok33
-rw-r--r--src/nvim/testdir/test50.in89
-rw-r--r--src/nvim/testdir/test50.ok14
-rw-r--r--src/nvim/testdir/test_arglist.vim54
-rw-r--r--src/nvim/testdir/test_autocmd.vim379
-rw-r--r--src/nvim/testdir/test_cd.vim3
-rw-r--r--src/nvim/testdir/test_checkpath.vim113
-rw-r--r--src/nvim/testdir/test_clientserver.vim3
-rw-r--r--src/nvim/testdir/test_command_count.vim1
-rw-r--r--src/nvim/testdir/test_debugger.vim232
-rw-r--r--src/nvim/testdir/test_diffmode.vim34
-rw-r--r--src/nvim/testdir/test_edit.vim48
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim28
-rw-r--r--src/nvim/testdir/test_filetype.vim5
-rw-r--r--src/nvim/testdir/test_findfile.vim1
-rw-r--r--src/nvim/testdir/test_functions.vim31
-rw-r--r--src/nvim/testdir/test_gf.vim35
-rw-r--r--src/nvim/testdir/test_highlight.vim46
-rw-r--r--src/nvim/testdir/test_jumplist.vim66
-rw-r--r--src/nvim/testdir/test_lambda.vim6
-rw-r--r--src/nvim/testdir/test_listchars.vim39
-rw-r--r--src/nvim/testdir/test_mapping.vim40
-rw-r--r--src/nvim/testdir/test_match.vim22
-rw-r--r--src/nvim/testdir/test_mksession.vim11
-rw-r--r--src/nvim/testdir/test_modeline.vim84
-rw-r--r--src/nvim/testdir/test_normal.vim95
-rw-r--r--src/nvim/testdir/test_popup.vim181
-rw-r--r--src/nvim/testdir/test_put.vim57
-rw-r--r--src/nvim/testdir/test_python2.vim89
-rw-r--r--src/nvim/testdir/test_python3.vim89
-rw-r--r--src/nvim/testdir/test_quickfix.vim541
-rw-r--r--src/nvim/testdir/test_regex_char_classes.vim240
-rw-r--r--src/nvim/testdir/test_scrollbind.vim240
-rw-r--r--src/nvim/testdir/test_search.vim24
-rw-r--r--src/nvim/testdir/test_substitute.vim116
-rw-r--r--src/nvim/testdir/test_swap.vim163
-rw-r--r--src/nvim/testdir/test_syntax.vim34
-rw-r--r--src/nvim/testdir/test_tabpage.vim32
-rw-r--r--src/nvim/testdir/test_tagjump.vim205
-rw-r--r--src/nvim/testdir/test_taglist.vim38
-rw-r--r--src/nvim/testdir/test_textformat.vim323
-rw-r--r--src/nvim/testdir/test_timers.vim34
-rw-r--r--src/nvim/testdir/test_true_false.vim4
-rw-r--r--src/nvim/testdir/test_undo.vim10
-rw-r--r--src/nvim/testdir/test_visual.vim24
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim26
-rw-r--r--src/nvim/testdir/test_window_cmd.vim50
-rw-r--r--src/nvim/testdir/test_writefile.vim25
-rw-r--r--src/nvim/tui/input.c81
-rw-r--r--src/nvim/tui/input.h3
-rw-r--r--src/nvim/tui/tui.c16
-rw-r--r--src/nvim/ui.c19
-rw-r--r--src/nvim/ui.h6
-rw-r--r--src/nvim/ui_compositor.c6
-rw-r--r--src/nvim/ui_compositor.h2
-rw-r--r--src/nvim/undo.c40
-rw-r--r--src/nvim/version.c52
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c428
155 files changed, 11770 insertions, 5610 deletions
diff --git a/src/Doxyfile b/src/Doxyfile
index de31c8355f..461fafe99d 100644
--- a/src/Doxyfile
+++ b/src/Doxyfile
@@ -243,7 +243,7 @@ OPTIMIZE_OUTPUT_VHDL = NO
# that for custom extensions you also need to set FILE_PATTERNS otherwise the
# files are not read by doxygen.
-EXTENSION_MAPPING =
+EXTENSION_MAPPING = lua=C
# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
# comments according to the Markdown format, which allows for more readable
@@ -672,7 +672,7 @@ INPUT_ENCODING = UTF-8
# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
# *.f90 *.f *.for *.vhd *.vhdl
-FILE_PATTERNS = *.h *.c
+FILE_PATTERNS = *.h *.c *.lua
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -758,7 +758,7 @@ INPUT_FILTER =
# info on how filters are used. If FILTER_PATTERNS is empty or if
# non of the patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+FILTER_PATTERNS = *.lua=scripts/lua2dox_filter
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
diff --git a/src/clint.py b/src/clint.py
index 1ef31820ee..6643d8956a 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -3246,7 +3246,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
r'|li_(?:next|prev|tv))\b', line)
if match:
error(filename, linenum, 'runtime/deprecated', 4,
- 'Accessing list_T internals directly is prohibited')
+ 'Accessing list_T internals directly is prohibited (hint: see commit d46e37cb4c71)')
# Check for suspicious usage of "if" like
# } if (a == b) {
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 3613a8f8bc..9a5ffecad4 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -15,6 +15,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/getchar.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/misc1.h"
@@ -49,7 +50,7 @@
/// Gets the buffer line count
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Line count, or 0 for unloaded buffer. |api-buffer|
Integer nvim_buf_line_count(Buffer buffer, Error *err)
@@ -97,15 +98,16 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
return rv;
}
-/// Activate updates from this buffer to the current channel.
+/// Activates buffer-update events on the channel.
///
-/// @param buffer The buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param send_buffer Set to true if the initial notification should contain
/// the whole buffer. If so, the first notification will be a
/// `nvim_buf_lines_event`. Otherwise, the first notification will be
/// a `nvim_buf_changedtick_event`
/// @param opts Optional parameters. Reserved for future use.
-/// @param[out] err Details of an error that may have occurred
+/// @param[out] err Error details, if any
/// @return False when updates couldn't be enabled because the buffer isn't
/// loaded or `opts` contained an invalid key; otherwise True.
Boolean nvim_buf_attach(uint64_t channel_id,
@@ -128,11 +130,12 @@ Boolean nvim_buf_attach(uint64_t channel_id,
return buf_updates_register(buf, channel_id, send_buffer);
}
-//
-/// Deactivate updates from this buffer to the current channel.
+
+/// Deactivates buffer-update events on the channel.
///
-/// @param buffer The buffer handle
-/// @param[out] err Details of an error that may have occurred
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param[out] err Error details, if any
/// @return False when updates couldn't be disabled because the buffer
/// isn't loaded; otherwise True.
Boolean nvim_buf_detach(uint64_t channel_id,
@@ -221,7 +224,8 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
/// @param end Last line index (exclusive)
/// @param strict_indexing Whether out-of-bounds should be an error.
@@ -290,7 +294,7 @@ end:
/// newend = end + int(include_end) + int(end < 0)
/// int(bool) = 1 if bool is true else 0
///
-/// @param buffer Buffer handle
+/// @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
@@ -324,7 +328,8 @@ void buffer_set_line_slice(Buffer buffer,
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
/// @param end Last line index (exclusive)
/// @param strict_indexing Whether out-of-bounds should be an error.
@@ -470,6 +475,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
false);
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
+ fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
end:
for (size_t i = 0; i < new_len; i++) {
@@ -491,7 +497,7 @@ end:
/// Unlike |line2byte()|, throws error for out-of-bounds indexing.
/// Returns -1 for unloaded buffer.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param index Line index
/// @param[out] err Error details, if any
/// @return Integer byte offset, or -1 for unloaded buffer.
@@ -518,7 +524,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
/// Gets a buffer-scoped (b:) variable.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Variable value
@@ -536,7 +542,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
/// Gets a changed tick of a buffer
///
-/// @param[in] buffer Buffer handle.
+/// @param[in] buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
///
/// @return `b:changedtick` value.
@@ -555,7 +561,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// Gets a list of buffer-local |mapping| definitions.
///
/// @param mode Mode short-name ("n", "i", "v", ...)
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
@@ -571,9 +577,34 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
return keymap_array(mode, buf);
}
+/// Sets a buffer-local |mapping| for the given mode.
+///
+/// @see |nvim_set_keymap()|
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(6)
+{
+ modify_keymap(buffer, false, mode, lhs, rhs, opts, err);
+}
+
+/// Unmaps a buffer-local |mapping| for the given mode.
+///
+/// @see |nvim_del_keymap()|
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
+ FUNC_API_SINCE(6)
+{
+ String rhs = { .data = "", .size = 0 };
+ Dictionary opts = ARRAY_DICT_INIT;
+ modify_keymap(buffer, true, mode, lhs, rhs, opts, err);
+}
+
/// Gets a map of buffer-local |user-commands|.
///
-/// @param buffer Buffer handle.
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param opts Optional parameters. Currently not used.
/// @param[out] err Error details, if any.
///
@@ -613,7 +644,7 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dictionary opts, Error *err)
/// Sets a buffer-scoped (b:) variable
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
@@ -631,7 +662,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
/// Removes a buffer-scoped (b:) variable
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
void nvim_buf_del_var(Buffer buffer, String name, Error *err)
@@ -650,7 +681,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
///
/// @deprecated
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
@@ -673,7 +704,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Old value
@@ -691,7 +722,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
/// Gets a buffer option value
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
@@ -710,7 +741,8 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
/// works if there's a global fallback)
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
@@ -732,7 +764,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer,
/// @deprecated The buffer number now is equal to the object id,
/// so there is no need to use this function.
///
-/// @param buffer Buffer handle
+/// @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)
@@ -751,7 +783,7 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
/// Gets the full file name for the buffer
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
String nvim_buf_get_name(Buffer buffer, Error *err)
@@ -769,7 +801,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
/// Sets the full file name for a buffer
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Buffer name
/// @param[out] err Error details, if any
void nvim_buf_set_name(Buffer buffer, String name, Error *err)
@@ -801,7 +833,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
/// Checks if a buffer is valid and loaded. See |api-buffer| for more info
/// about unloaded buffers.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @return true if the buffer is valid and loaded, false otherwise.
Boolean nvim_buf_is_loaded(Buffer buffer)
FUNC_API_SINCE(5)
@@ -817,7 +849,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer)
/// @note Even if a buffer is valid it may have been unloaded. See |api-buffer|
/// for more info about unloaded buffers.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @return true if the buffer is valid, false otherwise.
Boolean nvim_buf_is_valid(Buffer buffer)
FUNC_API_SINCE(1)
@@ -849,7 +881,7 @@ void buffer_insert(Buffer buffer,
/// Return a tuple (row,col) representing the position of the named mark
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Mark name
/// @param[out] err Error details, if any
/// @return (row, col) tuple
@@ -914,7 +946,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// supported for backwards compatibility, new code should use
/// |nvim_create_namespace| to create a new empty namespace.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id namespace to use or -1 for ungrouped highlight
/// @param hl_group Name of the highlight group to use
/// @param line Line to highlight (zero-indexed)
@@ -964,7 +996,7 @@ Integer nvim_buf_add_highlight(Buffer buffer,
/// To clear the namespace in the entire buffer, pass in 0 and -1 to
/// line_start and line_end respectively.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace to clear, or -1 to clear all namespaces.
/// @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
@@ -997,7 +1029,7 @@ void nvim_buf_clear_namespace(Buffer buffer,
///
/// @deprecated use |nvim_buf_clear_namespace|.
///
-/// @param buffer Buffer handle
+/// @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
@@ -1031,7 +1063,7 @@ void nvim_buf_clear_highlight(Buffer buffer,
/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
/// virtual text, the allocated id is then returned.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace to use or 0 to create a namespace,
/// or -1 for a ungrouped annotation
/// @param line Line to annotate with virtual text (zero-indexed)
@@ -1101,6 +1133,26 @@ free_exit:
return 0;
}
+// Check if deleting lines made the cursor position invalid.
+// Changed lines from `lo` to `hi`; added `extra` lines (negative if deleted).
+static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
+{
+ if (curwin->w_cursor.lnum >= lo) {
+ // Adjust cursor position if it's in/after the changed lines.
+ if (curwin->w_cursor.lnum >= hi) {
+ curwin->w_cursor.lnum += extra;
+ check_cursor_col();
+ } else if (extra < 0) {
+ curwin->w_cursor.lnum = lo;
+ check_cursor();
+ } else {
+ check_cursor_col();
+ }
+ changed_cline_bef_curs();
+ }
+ invalidate_botline();
+}
+
// Normalizes 0-based indexes to buffer line numbers
static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
{
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index feca140547..978c55691b 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -29,14 +29,13 @@ typedef enum {
} ErrorType;
typedef enum {
- kMessageTypeRequest,
- kMessageTypeResponse,
- kMessageTypeNotification
+ kMessageTypeUnknown = -1,
+ // Per msgpack-rpc spec.
+ kMessageTypeRequest = 0,
+ kMessageTypeResponse = 1,
+ kMessageTypeNotification = 2,
} MessageType;
-/// Used as the message ID of notifications.
-#define NO_RESPONSE UINT64_MAX
-
/// Mask for all internal calls
#define INTERNAL_CALL_MASK (((uint64_t)1) << (sizeof(uint64_t) * 8 - 1))
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index c2b382804d..521ec11906 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -744,6 +744,232 @@ String ga_take_string(garray_T *ga)
return str;
}
+/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
+/// functions like @ref nvim_buf_set_keymap.
+///
+/// Arguments are handled like @ref nvim_set_keymap unless noted.
+/// @param buffer Buffer handle for a specific buffer, or 0 for the current
+/// buffer, or -1 to signify global behavior ("all buffers")
+/// @param is_unmap When true, removes the mapping that matches {lhs}.
+void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs,
+ String rhs, Dictionary opts, Error *err)
+{
+ char *err_msg = NULL; // the error message to report, if any
+ char *err_arg = NULL; // argument for the error message format string
+ ErrorType err_type = kErrorTypeNone;
+
+ char_u *lhs_buf = NULL;
+ char_u *rhs_buf = NULL;
+
+ bool global = (buffer == -1);
+ if (global) {
+ buffer = 0;
+ }
+ buf_T *target_buf = find_buffer_by_handle(buffer, err);
+
+ MapArguments parsed_args;
+ memset(&parsed_args, 0, sizeof(parsed_args));
+ if (parse_keymap_opts(opts, &parsed_args, err)) {
+ goto fail_and_free;
+ }
+ parsed_args.buffer = !global;
+
+ set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size,
+ (char_u *)rhs.data, rhs.size,
+ CPO_TO_CPO_FLAGS, &parsed_args);
+
+ if (parsed_args.lhs_len > MAXMAPLEN) {
+ err_msg = "LHS exceeds maximum map length: %s";
+ err_arg = lhs.data;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ if (mode.size > 1) {
+ err_msg = "Shortname is too long: %s";
+ err_arg = mode.data;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ int mode_val; // integer value of the mapping mode, to be passed to do_map()
+ char_u *p = (char_u *)((mode.size) ? mode.data : "m");
+ if (STRNCMP(p, "!", 2) == 0) {
+ mode_val = get_map_mode(&p, true); // mapmode-ic
+ } else {
+ mode_val = get_map_mode(&p, false);
+ if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING)
+ && mode.size > 0) {
+ // get_map_mode() treats unrecognized mode shortnames as ":map".
+ // This is an error unless the given shortname was empty string "".
+ err_msg = "Invalid mode shortname: \"%s\"";
+ err_arg = (char *)p;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ }
+
+ if (parsed_args.lhs_len == 0) {
+ err_msg = "Invalid (empty) LHS";
+ err_arg = "";
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ bool is_noremap = parsed_args.noremap;
+ assert(!(is_unmap && is_noremap));
+
+ if (!is_unmap && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
+ if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
+ parsed_args.rhs_is_noop = true;
+ } else {
+ // the given RHS was nonempty and not a <Nop>, but was parsed as if it
+ // were empty?
+ assert(false && "Failed to parse nonempty RHS!");
+ err_msg = "Parsing of nonempty RHS failed: %s";
+ err_arg = rhs.data;
+ err_type = kErrorTypeException;
+ goto fail_with_message;
+ }
+ } else if (is_unmap && parsed_args.rhs_len) {
+ err_msg = "Gave nonempty RHS in unmap command: %s";
+ err_arg = (char *)parsed_args.rhs;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ // buf_do_map() reads noremap/unmap as its own argument.
+ int maptype_val = 0;
+ if (is_unmap) {
+ maptype_val = 1;
+ } else if (is_noremap) {
+ maptype_val = 2;
+ }
+
+ switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
+ case 0:
+ break;
+ case 1:
+ api_set_error(err, kErrorTypeException, (char *)e_invarg, 0);
+ goto fail_and_free;
+ case 2:
+ api_set_error(err, kErrorTypeException, (char *)e_nomap, 0);
+ goto fail_and_free;
+ case 5:
+ api_set_error(err, kErrorTypeException,
+ "E227: mapping already exists for %s", parsed_args.lhs);
+ goto fail_and_free;
+ default:
+ assert(false && "Unrecognized return code!");
+ goto fail_and_free;
+ } // switch
+
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+
+ return;
+
+fail_with_message:
+ api_set_error(err, err_type, err_msg, err_arg);
+
+fail_and_free:
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ return;
+}
+
+/// Read in the given opts, setting corresponding flags in `out`.
+///
+/// @param opts A dictionary passed to @ref nvim_set_keymap or
+/// @ref nvim_buf_set_keymap.
+/// @param[out] out MapArguments object in which to set parsed
+/// |:map-arguments| flags.
+/// @param[out] err Error details, if any.
+///
+/// @returns Zero on success, nonzero on failure.
+Integer parse_keymap_opts(Dictionary opts, MapArguments *out, Error *err)
+{
+ char *err_msg = NULL; // the error message to report, if any
+ char *err_arg = NULL; // argument for the error message format string
+ ErrorType err_type = kErrorTypeNone;
+
+ out->buffer = false;
+ out->nowait = false;
+ out->silent = false;
+ out->script = false;
+ out->expr = false;
+ out->unique = false;
+
+ for (size_t i = 0; i < opts.size; i++) {
+ KeyValuePair *key_and_val = &opts.items[i];
+ char *optname = key_and_val->key.data;
+
+ if (key_and_val->value.type != kObjectTypeBoolean) {
+ err_msg = "Gave non-boolean value for an opt: %s";
+ err_arg = optname;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ bool was_valid_opt = false;
+ switch (optname[0]) {
+ // note: strncmp up to and including the null terminator, so that
+ // "nowaitFoobar" won't match against "nowait"
+
+ // don't recognize 'buffer' as a key; user shouldn't provide <buffer>
+ // when calling nvim_set_keymap or nvim_buf_set_keymap, since it can be
+ // inferred from which function they called
+ case 'n':
+ if (STRNCMP(optname, "noremap", 8) == 0) {
+ was_valid_opt = true;
+ out->noremap = key_and_val->value.data.boolean;
+ } else if (STRNCMP(optname, "nowait", 7) == 0) {
+ was_valid_opt = true;
+ out->nowait = key_and_val->value.data.boolean;
+ }
+ break;
+ case 's':
+ if (STRNCMP(optname, "silent", 7) == 0) {
+ was_valid_opt = true;
+ out->silent = key_and_val->value.data.boolean;
+ } else if (STRNCMP(optname, "script", 7) == 0) {
+ was_valid_opt = true;
+ out->script = key_and_val->value.data.boolean;
+ }
+ break;
+ case 'e':
+ if (STRNCMP(optname, "expr", 5) == 0) {
+ was_valid_opt = true;
+ out->expr = key_and_val->value.data.boolean;
+ }
+ break;
+ case 'u':
+ if (STRNCMP(optname, "unique", 7) == 0) {
+ was_valid_opt = true;
+ out->unique = key_and_val->value.data.boolean;
+ }
+ break;
+ default:
+ break;
+ } // switch
+ if (!was_valid_opt) {
+ err_msg = "Invalid key: %s";
+ err_arg = optname;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ } // for
+
+ return 0;
+
+fail_with_message:
+ api_set_error(err, err_type, err_msg, err_arg);
+ return 1;
+}
+
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 0634764f13..cc74824402 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -5,6 +5,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/vim.h"
+#include "nvim/getchar.h"
#include "nvim/memory.h"
#include "nvim/ex_eval.h"
#include "nvim/lib/kvec.h"
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index d3cbb46dad..4f28ea5af3 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -29,11 +29,12 @@ typedef struct {
uint64_t channel_id;
Array buffer;
- int hl_id; // current higlight for legacy put event
- Integer cursor_row, cursor_col; // Intended visibule cursor position
+ int hl_id; // Current highlight for legacy put event.
+ Integer cursor_row, cursor_col; // Intended visible cursor position.
// Position of legacy cursor, used both for drawing and visible user cursor.
Integer client_row, client_col;
+ bool wildmenu_active;
} UIData;
static PMap(uint64_t) *connected_uis = NULL;
@@ -75,6 +76,21 @@ void remote_ui_wait_for_attach(void)
pmap_has(uint64_t)(connected_uis, CHAN_STDIO));
}
+/// Activates UI events on the channel.
+///
+/// Entry point of all UI clients. Allows |\-\-embed| to continue startup.
+/// Implies that the client is ready to show the UI. Adds the client to the
+/// list of UIs. |nvim_list_uis()|
+///
+/// @note If multiple UI clients are attached, the global screen dimensions
+/// degrade to the smallest client. E.g. if client A requests 80x40 but
+/// client B requests 200x100, the global screen has size 80x40.
+///
+/// @param channel_id
+/// @param width Requested screen columns
+/// @param height Requested screen rows
+/// @param options |ui-option| map
+/// @param[out] err Error details, if any
void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
Dictionary options, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
@@ -94,6 +110,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->width = (int)width;
ui->height = (int)height;
ui->rgb = true;
+ ui->override = false;
ui->grid_resize = remote_ui_grid_resize;
ui->grid_clear = remote_ui_grid_clear;
ui->grid_cursor_goto = remote_ui_grid_cursor_goto;
@@ -146,6 +163,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
data->buffer = (Array)ARRAY_DICT_INIT;
data->hl_id = 0;
data->client_col = -1;
+ data->wildmenu_active = false;
ui->data = data;
pmap_put(uint64_t)(connected_uis, channel_id, ui);
@@ -162,6 +180,12 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height,
api_free_dictionary(opts);
}
+/// Deactivates UI events on the channel.
+///
+/// Removes the client from the list of UIs. |nvim_list_uis()|
+///
+/// @param channel_id
+/// @param[out] err Error details, if any
void nvim_ui_detach(uint64_t channel_id, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
@@ -213,6 +237,15 @@ void nvim_ui_set_option(uint64_t channel_id, String name,
static void ui_set_option(UI *ui, bool init, String name, Object value,
Error *error)
{
+ if (strequal(name.data, "override")) {
+ if (value.type != kObjectTypeBoolean) {
+ api_set_error(error, kErrorTypeValidation, "override must be a Boolean");
+ return;
+ }
+ ui->override = value.data.boolean;
+ return;
+ }
+
if (strequal(name.data, "rgb")) {
if (value.type != kObjectTypeBoolean) {
api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean");
@@ -262,20 +295,22 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
///
/// On invalid grid handle, fails with error.
///
+/// @param channel_id
/// @param grid The handle of the grid to be changed.
/// @param width The new requested width.
/// @param height The new requested height.
+/// @param[out] err Error details, if any
void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width,
- Integer height, Error *error)
+ Integer height, Error *err)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, kErrorTypeException,
+ api_set_error(err, kErrorTypeException,
"UI not attached to channel: %" PRId64, channel_id);
return;
}
- ui_grid_resize((handle_T)grid, (int)width, (int)height, error);
+ ui_grid_resize((handle_T)grid, (int)width, (int)height, err);
}
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
@@ -586,6 +621,7 @@ static Array translate_firstarg(UI *ui, Array args)
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{
+ UIData *data = ui->data;
if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back
// never consumes args
@@ -611,6 +647,39 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
}
}
+ // Back-compat: translate popupmenu_xx to legacy wildmenu_xx.
+ if (ui->ui_ext[kUIWildmenu]) {
+ if (strequal(name, "popupmenu_show")) {
+ data->wildmenu_active = (args.items[4].data.integer == -1)
+ || !ui->ui_ext[kUIPopupmenu];
+ if (data->wildmenu_active) {
+ Array new_args = ARRAY_DICT_INIT;
+ Array items = args.items[0].data.array;
+ Array new_items = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < items.size; i++) {
+ ADD(new_items, copy_object(items.items[i].data.array.items[0]));
+ }
+ ADD(new_args, ARRAY_OBJ(new_items));
+ push_call(ui, "wildmenu_show", new_args);
+ if (args.items[1].data.integer != -1) {
+ Array new_args2 = ARRAY_DICT_INIT;
+ ADD(new_args2, args.items[1]);
+ push_call(ui, "wildmenu_select", new_args);
+ }
+ return;
+ }
+ } else if (strequal(name, "popupmenu_select")) {
+ if (data->wildmenu_active) {
+ name = "wildmenu_select";
+ }
+ } else if (strequal(name, "popupmenu_hide")) {
+ if (data->wildmenu_active) {
+ name = "wildmenu_hide";
+ }
+ }
+ }
+
+
Array my_args = ARRAY_DICT_INIT;
// Objects are currently single-reference
// make a copy, but only if necessary
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index b89c5b6014..a1d25766fe 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -143,11 +143,11 @@ void cmdline_block_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_show(Array items)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void wildmenu_select(Integer selected)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void wildmenu_hide(void)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void msg_show(String kind, Array content, Boolean replace_last)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index cb5ed5ecda..b8c863704a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -15,6 +15,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/buffer.h"
+#include "nvim/api/window.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/lua/executor.h"
@@ -213,8 +214,8 @@ Integer nvim_input(String keys)
/// Send mouse event from GUI.
///
-/// The call is non-blocking. It doesn't wait on any resulting action, but
-/// queues the event to be processed soon by the event loop.
+/// Non-blocking: does not wait on any result, but queues the event to be
+/// processed soon by the event loop.
///
/// @note Currently this doesn't support "scripting" multiple mouse events
/// by calling it multiple times in a loop: the intermediate mouse
@@ -232,6 +233,7 @@ Integer nvim_input(String keys)
/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
/// @param row Mouse row-position (zero-based, like redraw events)
/// @param col Mouse column-position (zero-based, like redraw events)
+/// @param[out] err Error details, if any
void nvim_input_mouse(String button, String action, String modifier,
Integer grid, Integer row, Integer col, Error *err)
FUNC_API_SINCE(6) FUNC_API_ASYNC
@@ -681,14 +683,14 @@ void nvim_set_current_dir(String dir, Error *err)
try_start();
- if (vim_chdir((char_u *)string, kCdScopeGlobal)) {
+ if (vim_chdir((char_u *)string)) {
if (!try_end(err)) {
api_set_error(err, kErrorTypeException, "Failed to change directory");
}
return;
}
- post_chdir(kCdScopeGlobal);
+ post_chdir(kCdScopeGlobal, true);
try_end(err);
}
@@ -755,9 +757,9 @@ void nvim_del_var(String name, Error *err)
/// @deprecated
/// @see nvim_set_var
-/// @return Old value or nil if there was no previous value.
/// @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);
@@ -805,6 +807,7 @@ Object nvim_get_option(String name, Error *err)
/// Sets an option value.
///
+/// @param channel_id
/// @param name Option name
/// @param value New option value
/// @param[out] err Error details, if any
@@ -937,6 +940,7 @@ Window nvim_get_current_win(void)
/// Sets the current window.
///
/// @param window Window handle
+/// @param[out] err Error details, if any
void nvim_set_current_win(Window window, Error *err)
FUNC_API_SINCE(1)
{
@@ -958,7 +962,7 @@ void nvim_set_current_win(Window window, Error *err)
/// Creates a new, empty, unnamed buffer.
///
-/// @param listed Controls 'buflisted'
+/// @param listed Sets 'buflisted'
/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
/// (always 'nomodified')
/// @param[out] err Error details, if any
@@ -997,38 +1001,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// GUI with the |ui-multigrid| extension. External windows are only supported
/// with multigrid GUIs, and are displayed as separate top-level windows.
///
-/// Exactly one of `external` and `relative` must be specified.
+/// For a general overview of floats, see |api-floatwin|.
///
-/// @param buffer handle of buffer to be displayed in the window
-/// @param enter whether the window should be entered (made the current window)
-/// @param width width of window (in character cells)
-/// @param height height of window (in character cells)
-/// @param options dict of options for configuring window positioning
-/// accepts the following keys:
-/// `relative`: If set, the window becomes a floating window. The window
-/// will be placed with row,col coordinates relative one of the
-/// following:
-/// "editor" the global editor grid
-/// "win" a window. Use 'win' option below to specify window id,
-/// or current window will be used by default.
-/// "cursor" the cursor position in current window.
-/// `anchor`: the corner of the float that the row,col position defines
-/// "NW" north-west (default)
-/// "NE" north-east
-/// "SW" south-west
-/// "SE" south-east
-/// `focusable`: Whether window can be focused by wincmds and
-/// mouse events. Defaults to true. Even if set to false, the window
-/// can still be entered using |nvim_set_current_win()| API call.
-/// `row`: row position. Screen cell height are used as unit. Can be
-/// floating point.
-/// `col`: column position. Screen cell width is used as unit. Can be
-/// floating point.
-/// `win`: when using relative='win', window id of the window where the
-/// position is defined.
-/// `external` GUI should display the window as an external
-/// top-level window. Currently accepts no other positioning options
-/// together with this.
+/// Exactly one of `external` and `relative` must be specified. The `width` and
+/// `height` of the new window must be specified.
///
/// With editor positioning row=0, col=0 refers to the top-left corner of the
/// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner.
@@ -1042,27 +1018,55 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// could let floats hover outside of the main window like a tooltip, but
/// this should not be used to specify arbitrary WM screen positions.
///
+/// @param buffer handle of buffer to be displayed in the window
+/// @param enter whether the window should be entered (made the current window)
+/// @param config Dictionary for the window configuration accepts these keys:
+/// - `relative`: If set, the window becomes a floating window. The window
+/// will be placed with row,col coordinates relative to one of the
+/// following:
+/// - "editor" the global editor grid
+/// - "win" a window. Use `win` to specify a window id,
+/// or the current window will be used by default.
+/// "cursor" the cursor position in current window.
+/// - `win`: When using relative='win', window id of the window where the
+/// position is defined.
+/// - `anchor`: The corner of the float that the row,col position defines:
+/// - "NW" north-west (default)
+/// - "NE" north-east
+/// - "SW" south-west
+/// - "SE" south-east
+/// - `height`: window height (in character cells). Minimum of 1.
+/// - `width`: window width (in character cells). Minimum of 1.
+/// - `row`: row position. Screen cell height are used as unit. Can be
+/// floating point.
+/// - `col`: column position. Screen cell width is used as unit. Can be
+/// floating point.
+/// - `focusable`: Whether window can be focused by wincmds and
+/// mouse events. Defaults to true. Even if set to false, the window
+/// can still be entered using |nvim_set_current_win()| API call.
+/// - `external`: GUI should display the window as an external
+/// top-level window. Currently accepts no other positioning
+/// configuration together with this.
/// @param[out] err Error details, if any
-/// @return the window handle or 0 when error
-Window nvim_open_win(Buffer buffer, Boolean enter,
- Integer width, Integer height,
- Dictionary options, Error *err)
+///
+/// @return Window handle, or 0 on error
+Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
+ Error *err)
FUNC_API_SINCE(6)
{
- win_T *old = curwin;
- FloatConfig config = FLOAT_CONFIG_INIT;
- if (!parse_float_config(options, &config, false, err)) {
+ FloatConfig fconfig = FLOAT_CONFIG_INIT;
+ if (!parse_float_config(config, &fconfig, false, err)) {
return 0;
}
- win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err);
+ win_T *wp = win_new_float(NULL, fconfig, err);
if (!wp) {
return 0;
}
- if (buffer > 0) {
- nvim_set_current_buf(buffer, err);
+ if (enter) {
+ win_enter(wp, false);
}
- if (!enter) {
- win_enter(old, false);
+ if (buffer > 0) {
+ nvim_win_set_buf(wp->handle, buffer, err);
}
return wp->handle;
}
@@ -1194,12 +1198,29 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
rpc_unsubscribe(channel_id, e);
}
+/// Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or
+/// "#rrggbb" hexadecimal string.
+///
+/// Example:
+/// <pre>
+/// :echo nvim_get_color_by_name("Pink")
+/// :echo nvim_get_color_by_name("#cbcbcb")
+/// </pre>
+///
+/// @param name Color name or "#rrggbb" string
+/// @return 24-bit RGB value, or -1 for invalid argument.
Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1)
{
return name_to_color((char_u *)name.data);
}
+/// Returns a map of color names and RGB values.
+///
+/// Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values
+/// (e.g. 65535).
+///
+/// @return Map of color names and RGB values.
Dictionary nvim_get_color_map(void)
FUNC_API_SINCE(1)
{
@@ -1241,6 +1262,49 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
return keymap_array(mode, NULL);
}
+/// Sets a global |mapping| for the given mode.
+///
+/// To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
+///
+/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs}
+/// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
+///
+/// Example:
+/// <pre>
+/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+/// </pre>
+///
+/// is equivalent to:
+/// <pre>
+/// nmap <nowait> <Space><NL> <Nop>
+/// </pre>
+///
+/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
+/// or "!" for |:map!|, or empty string for |:map|.
+/// @param lhs Left-hand-side |{lhs}| of the mapping.
+/// @param rhs Right-hand-side |{rhs}| of the mapping.
+/// @param opts Optional parameters map. Accepts all |:map-arguments|
+/// as keys excluding |<buffer>| but including |noremap|.
+/// Values are Booleans. Unknown key is an error.
+/// @param[out] err Error details, if any.
+void nvim_set_keymap(String mode, String lhs, String rhs,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(6)
+{
+ modify_keymap(-1, false, mode, lhs, rhs, opts, err);
+}
+
+/// Unmaps a global |mapping| for the given mode.
+///
+/// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
+///
+/// @see |nvim_set_keymap()|
+void nvim_del_keymap(String mode, String lhs, Error *err)
+ FUNC_API_SINCE(6)
+{
+ nvim_buf_del_keymap(-1, mode, lhs, err);
+}
+
/// Gets a map of global (non-buffer-local) Ex commands.
///
/// Currently only |user-commands| are supported, not builtin Ex commands.
@@ -1272,47 +1336,48 @@ Array nvim_get_api_info(uint64_t channel_id)
return rv;
}
-/// Identify the client for nvim. Can be called more than once, but subsequent
-/// calls will remove earlier info, which should be resent if it is still
-/// valid. (This could happen if a library first identifies the channel, and a
+/// Identifies the client. Can be called more than once; subsequent calls
+/// remove earlier info, which should be included by the caller if it is
+/// still valid. (E.g. if a library first identifies the channel, then a
/// plugin using that library later overrides that info)
///
-/// @param name short name for the connected client
-/// @param version Dictionary describing the version, with the following
-/// possible keys (all optional)
+/// @param channel_id
+/// @param name Short name for the connected client
+/// @param version Dictionary describing the version, with these
+/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
/// - "minor" minor version
/// - "patch" patch number
/// - "prerelease" string describing a prerelease, like "dev" or "beta1"
/// - "commit" hash or similar identifier of commit
-/// @param type Must be one of the following values. A client library should
-/// use "remote" if the library user hasn't specified other value.
-/// - "remote" remote client that connected to nvim.
+/// @param type Must be one of the following values. Client libraries should
+/// default to "remote" unless overridden by the user.
+/// - "remote" remote client connected to Nvim.
/// - "ui" gui frontend
-/// - "embedder" application using nvim as a component, for instance
-/// IDE/editor implementing a vim mode.
+/// - "embedder" application using Nvim as a component (for example,
+/// IDE/editor implementing a vim mode).
/// - "host" plugin host, typically started by nvim
/// - "plugin" single plugin, started by nvim
/// @param methods Builtin methods in the client. For a host, this does not
/// include plugin methods which will be discovered later.
/// The key should be the method name, the values are dicts with
-/// the following (optional) keys:
+/// these (optional) keys (more keys may be added in future
+/// versions of Nvim, thus unknown keys are ignored. Clients
+/// must only use keys defined in this or later versions of
+/// Nvim):
/// - "async" if true, send as a notification. If false or unspecified,
/// use a blocking request
/// - "nargs" Number of arguments. Could be a single integer or an array
-/// two integers, minimum and maximum inclusive.
-/// Further keys might be added in later versions of nvim and unknown keys
-/// are thus ignored. Clients must only use keys defined in this or later
-/// versions of nvim!
-///
-/// @param attributes Informal attributes describing the client. Clients might
-/// define their own keys, but the following are suggested:
-/// - "website" Website of client (for instance github repository)
-/// - "license" Informal description of the license, such as "Apache 2",
-/// "GPLv3" or "MIT"
-/// - "logo" URI or path to image, preferably small logo or icon.
-/// .png or .svg format is preferred.
+/// of two integers, minimum and maximum inclusive.
+///
+/// @param attributes Arbitrary string:string map of informal client properties.
+/// Suggested keys:
+/// - "website": Client homepage URL (e.g. GitHub repository)
+/// - "license": License description ("Apache 2", "GPLv3", "MIT", …)
+/// - "logo": URI or path to image, preferably small logo or icon.
+/// .png or .svg format is preferred.
///
+/// @param[out] err Error details, if any
void nvim_set_client_info(uint64_t channel_id, String name,
Dictionary version, String type,
Dictionary methods, Dictionary attributes,
@@ -1344,15 +1409,14 @@ void nvim_set_client_info(uint64_t channel_id, String name,
/// Get information about a channel.
///
-/// @returns a Dictionary, describing a channel with the
-/// following keys:
-/// - "stream" the stream underlying the channel
+/// @returns Dictionary describing a channel, with these keys:
+/// - "stream" the stream underlying the channel
/// - "stdio" stdin and stdout of this Nvim instance
/// - "stderr" stderr of this Nvim instance
/// - "socket" TCP/IP socket or named pipe
/// - "job" job with communication over its stdio
/// - "mode" how data received on the channel is interpreted
-/// - "bytes" send and recieve raw bytes
+/// - "bytes" send and receive raw bytes
/// - "terminal" a |terminal| instance interprets ASCII sequences
/// - "rpc" |RPC| communication on the channel is active
/// - "pty" Name of pseudoterminal, if one is used (optional).
@@ -1393,13 +1457,13 @@ Array nvim_list_chans(void)
/// processing which have such side-effects, e.g. |:sleep| may wake timers).
/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
///
+/// @param channel_id
/// @param calls an array of calls, where each call is described by an array
-/// with two elements: the request name, and an array of arguments.
-/// @param[out] err Details of a validation error of the nvim_multi_request call
-/// itself, i.e. malformed `calls` parameter. Errors from called methods will
-/// be indicated in the return value, see below.
+/// with two elements: the request name, and an array of arguments.
+/// @param[out] err Validation error details (malformed `calls` parameter),
+/// if any. Errors from batched calls are given in the return value.
///
-/// @return an array with two elements. The first is an array of return
+/// @return Array of two elements. The first is an array of return
/// values. The second is NIL if all calls succeeded. If a call resulted in
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
@@ -1490,9 +1554,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// Parse a VimL expression.
///
-/// @param[in] expr Expression to parse. Is always treated as a single line.
-/// @param[in] flags Flags:
-///
+/// @param[in] expr Expression to parse. Always treated as a single line.
+/// @param[in] flags Flags:
/// - "m" if multiple expressions in a row are allowed (only
/// the first one will be parsed),
/// - "E" if EOC tokens are not allowed (determines whether
@@ -1500,7 +1563,6 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// operator/space, though also yielding an error).
/// - "l" when needing to start parsing with lvalues for
/// ":let" or ":for".
-///
/// Common flag sets:
/// - "m" to parse like for ":echo".
/// - "E" to parse like for "<C-r>=".
@@ -1513,63 +1575,57 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// starting column and ending column (latter exclusive:
/// one should highlight region [start_col, end_col)).
///
-/// @return AST: top-level dictionary with these keys:
-///
-/// "error": Dictionary with error, present only if parser saw some
-/// error. Contains the following keys:
-///
-/// "message": String, error message in printf format, translated.
-/// Must contain exactly one "%.*s".
-/// "arg": String, error message argument.
-///
-/// "len": Amount of bytes successfully parsed. With flags equal to ""
-/// that should be equal to the length of expr string.
-///
-/// @note: “Sucessfully parsed†here means “participated in AST
-/// creationâ€, not “till the first errorâ€.
-///
-/// "ast": AST, either nil or a dictionary with these keys:
-///
-/// "type": node type, one of the value names from ExprASTNodeType
-/// stringified without "kExprNode" prefix.
-/// "start": a pair [line, column] describing where node is “startedâ€
-/// where "line" is always 0 (will not be 0 if you will be
-/// using nvim_parse_viml() on e.g. ":let", but that is not
-/// present yet). Both elements are Integers.
-/// "len": “length†of the node. This and "start" are there for
-/// debugging purposes primary (debugging parser and providing
-/// debug information).
-/// "children": a list of nodes described in top/"ast". There always
-/// is zero, one or two children, key will not be present
-/// if node has no children. Maximum number of children
-/// may be found in node_maxchildren array.
-///
-/// Local values (present only for certain nodes):
-///
-/// "scope": a single Integer, specifies scope for "Option" and
-/// "PlainIdentifier" nodes. For "Option" it is one of
-/// ExprOptScope values, for "PlainIdentifier" it is one of
-/// ExprVarScope values.
-/// "ident": identifier (without scope, if any), present for "Option",
-/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
-/// "name": Integer, register name (one character) or -1. Only present
-/// for "Register" nodes.
-/// "cmp_type": String, comparison type, one of the value names from
-/// ExprComparisonType, stringified without "kExprCmp"
-/// prefix. Only present for "Comparison" nodes.
-/// "ccs_strategy": String, case comparison strategy, one of the
-/// value names from ExprCaseCompareStrategy,
-/// stringified without "kCCStrategy" prefix. Only
-/// present for "Comparison" nodes.
-/// "augmentation": String, augmentation type for "Assignment" nodes.
-/// Is either an empty string, "Add", "Subtract" or
-/// "Concat" for "=", "+=", "-=" or ".=" respectively.
-/// "invert": Boolean, true if result of comparison needs to be
-/// inverted. Only present for "Comparison" nodes.
-/// "ivalue": Integer, integer value for "Integer" nodes.
-/// "fvalue": Float, floating-point value for "Float" nodes.
-/// "svalue": String, value for "SingleQuotedString" and
-/// "DoubleQuotedString" nodes.
+/// @return
+/// - AST: top-level dictionary with these keys:
+/// - "error": Dictionary with error, present only if parser saw some
+/// error. Contains the following keys:
+/// - "message": String, error message in printf format, translated.
+/// Must contain exactly one "%.*s".
+/// - "arg": String, error message argument.
+/// - "len": Amount of bytes successfully parsed. With flags equal to ""
+/// that should be equal to the length of expr string.
+/// (“Sucessfully parsed†here means “participated in AST
+/// creationâ€, not “till the first errorâ€.)
+/// - "ast": AST, either nil or a dictionary with these keys:
+/// - "type": node type, one of the value names from ExprASTNodeType
+/// stringified without "kExprNode" prefix.
+/// - "start": a pair [line, column] describing where node is "started"
+/// where "line" is always 0 (will not be 0 if you will be
+/// using nvim_parse_viml() on e.g. ":let", but that is not
+/// present yet). Both elements are Integers.
+/// - "len": “length†of the node. This and "start" are there for
+/// debugging purposes primary (debugging parser and providing
+/// debug information).
+/// - "children": a list of nodes described in top/"ast". There always
+/// is zero, one or two children, key will not be present
+/// if node has no children. Maximum number of children
+/// may be found in node_maxchildren array.
+/// - Local values (present only for certain nodes):
+/// - "scope": a single Integer, specifies scope for "Option" and
+/// "PlainIdentifier" nodes. For "Option" it is one of
+/// ExprOptScope values, for "PlainIdentifier" it is one of
+/// ExprVarScope values.
+/// - "ident": identifier (without scope, if any), present for "Option",
+/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
+/// - "name": Integer, register name (one character) or -1. Only present
+/// for "Register" nodes.
+/// - "cmp_type": String, comparison type, one of the value names from
+/// ExprComparisonType, stringified without "kExprCmp"
+/// prefix. Only present for "Comparison" nodes.
+/// - "ccs_strategy": String, case comparison strategy, one of the
+/// value names from ExprCaseCompareStrategy,
+/// stringified without "kCCStrategy" prefix. Only
+/// present for "Comparison" nodes.
+/// - "augmentation": String, augmentation type for "Assignment" nodes.
+/// Is either an empty string, "Add", "Subtract" or
+/// "Concat" for "=", "+=", "-=" or ".=" respectively.
+/// - "invert": Boolean, true if result of comparison needs to be
+/// inverted. Only present for "Comparison" nodes.
+/// - "ivalue": Integer, integer value for "Integer" nodes.
+/// - "fvalue": Float, floating-point value for "Float" nodes.
+/// - "svalue": String, value for "SingleQuotedString" and
+/// "DoubleQuotedString" nodes.
+/// @param[out] err Error details, if any
Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
Error *err)
FUNC_API_SINCE(4) FUNC_API_ASYNC
@@ -2034,15 +2090,12 @@ Dictionary nvim__stats(void)
/// Gets a list of dictionaries representing attached UIs.
///
-/// @return Array of UI dictionaries
-///
-/// Each dictionary has the following keys:
-/// - "height" requested height of the UI
-/// - "width" requested width of the UI
-/// - "rgb" whether the UI uses rgb colors (false implies cterm colors)
-/// - "ext_..." Requested UI extensions, see |ui-options|
-/// - "chan" Channel id of remote UI (not present for TUI)
-///
+/// @return Array of UI dictionaries, each with these keys:
+/// - "height" Requested height of the UI
+/// - "width" Requested width of the UI
+/// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
+/// - "ext_..." Requested UI extensions, see |ui-option|
+/// - "chan" Channel id of remote UI (not present for TUI)
Array nvim_list_uis(void)
FUNC_API_SINCE(4)
{
@@ -2145,6 +2198,7 @@ Object nvim_get_proc(Integer pid, Error *err)
/// @param finish Finish the completion and dismiss the popupmenu. Implies
/// `insert`.
/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any
void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish,
Dictionary opts, Error *err)
FUNC_API_SINCE(6)
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 157f73c9fa..e1c50cb89d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -67,6 +67,10 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
buffer);
}
+ // If window is not current, state logic will not validate its cursor.
+ // So do it now.
+ validate_cursor();
+
restore_win(save_curwin, save_curtab, false);
}
@@ -345,6 +349,7 @@ Object nvim_win_get_option(Window window, String name, Error *err)
/// Sets a window option value. Passing 'nil' as value deletes the option(only
/// works if there's a global fallback)
///
+/// @param channel_id
/// @param window Window handle
/// @param name Option name
/// @param value Option value
@@ -438,14 +443,16 @@ Boolean nvim_win_is_valid(Window window)
/// floating and external windows (including changing a split window to these
/// types).
///
-/// See documentation at |nvim_open_win()|, for the meaning of parameters. Pass
-/// in -1 for 'witdh' and 'height' to keep exiting size.
+/// See documentation at |nvim_open_win()|, for the meaning of parameters.
///
/// When reconfiguring a floating window, absent option keys will not be
/// changed. The following restriction apply: `row`, `col` and `relative`
/// must be reconfigured together. Only changing a subset of these is an error.
-void nvim_win_config(Window window, Integer width, Integer height,
- Dictionary options, Error *err)
+///
+/// @param window Window handle
+/// @param config Dictionary of window configuration
+/// @param[out] err Error details, if any
+void nvim_win_set_config(Window window, Dictionary config, Error *err)
FUNC_API_SINCE(6)
{
win_T *win = find_window_by_handle(window, err);
@@ -453,25 +460,67 @@ void nvim_win_config(Window window, Integer width, Integer height,
return;
}
bool new_float = !win->w_floating;
- width = width > 0 ? width: win->w_width;
- height = height > 0 ? height : win->w_height;
// reuse old values, if not overriden
- FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
+ FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
- if (!parse_float_config(options, &config, !new_float, err)) {
+ if (!parse_float_config(config, &fconfig, !new_float, err)) {
return;
}
if (new_float) {
- if (!win_new_float(win, (int)width, (int)height, config, err)) {
+ if (!win_new_float(win, fconfig, err)) {
return;
}
redraw_later(NOT_VALID);
} else {
- win_config_float(win, (int)width, (int)height, config);
+ win_config_float(win, fconfig);
win->w_pos_changed = true;
}
}
+/// Return window configuration.
+///
+/// Return a dictionary containing the same config that can be given to
+/// |nvim_open_win()|.
+///
+/// `relative` will be an empty string for normal windows.
+///
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Window configuration
+Dictionary nvim_win_get_config(Window window, Error *err)
+ FUNC_API_SINCE(6)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+
+ win_T *wp = find_window_by_handle(window, err);
+ if (!wp) {
+ return rv;
+ }
+
+ PUT(rv, "focusable", BOOLEAN_OBJ(wp->w_float_config.focusable));
+ PUT(rv, "external", BOOLEAN_OBJ(wp->w_float_config.external));
+
+ if (wp->w_floating) {
+ PUT(rv, "width", INTEGER_OBJ(wp->w_float_config.width));
+ PUT(rv, "height", INTEGER_OBJ(wp->w_float_config.height));
+ if (!wp->w_float_config.external) {
+ if (wp->w_float_config.relative == kFloatRelativeWindow) {
+ PUT(rv, "win", INTEGER_OBJ(wp->w_float_config.window));
+ }
+ PUT(rv, "anchor", STRING_OBJ(cstr_to_string(
+ float_anchor_str[wp->w_float_config.anchor])));
+ PUT(rv, "row", FLOAT_OBJ(wp->w_float_config.row));
+ PUT(rv, "col", FLOAT_OBJ(wp->w_float_config.col));
+ }
+ }
+
+ const char *rel = (wp->w_floating && !wp->w_float_config.external
+ ? float_relative_str[wp->w_float_config.relative] : "");
+ PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel)));
+
+ return rv;
+}
+
/// Close a window.
///
/// This is equivalent to |:close| with count except that it takes a window id.
@@ -480,9 +529,7 @@ void nvim_win_config(Window window, Integer width, Integer height,
/// @param force Behave like `:close!` The last window of a buffer with
/// unwritten changes can be closed. The buffer will become
/// hidden, even if 'hidden' is not set.
-///
/// @param[out] err Error details, if any
-/// @return Window number
void nvim_win_close(Window window, Boolean force, Error *err)
FUNC_API_SINCE(6)
{
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index cc0ed0f587..ef528f72b8 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -29,6 +29,7 @@ return {
'CmdWinLeave', -- before leaving the cmdline window
'ColorScheme', -- after loading a colorscheme
'ColorSchemePre', -- before loading a colorscheme
+ 'CompleteChanged', -- after popup menu changed
'CompleteDone', -- after finishing insert complete
'CursorHold', -- cursor in same position for a while
'CursorHoldI', -- idem, in Insert mode
@@ -64,7 +65,6 @@ return {
'InsertCharPre', -- before inserting a char
'InsertEnter', -- when entering Insert mode
'InsertLeave', -- when leaving Insert mode
- 'JobActivity', -- when job sent some data
'MenuPopup', -- just before popup menu is displayed
'OptionSet', -- after setting any option
'QuickFixCmdPost', -- after :make, :grep etc.
@@ -88,7 +88,7 @@ return {
'TabNew', -- when creating a new tab
'TabNewEntered', -- after entering a new tab
'TermChanged', -- after changing 'term'
- 'TermClose', -- after the processs exits
+ 'TermClose', -- after the process exits
'TermOpen', -- after opening a terminal buffer
'TermResponse', -- after setting "v:termresponse"
'TextChanged', -- text was modified
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8cb4e32815..cdb226b94d 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1,23 +1,23 @@
// 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
-/*
- * buffer.c: functions for dealing with the buffer structure
- */
+//
+// buffer.c: functions for dealing with the buffer structure
+//
-/*
- * The buffer list is a double linked list of all buffers.
- * Each buffer can be in one of these states:
- * never loaded: BF_NEVERLOADED is set, only the file name is valid
- * not loaded: b_ml.ml_mfp == NULL, no memfile allocated
- * hidden: b_nwindows == 0, loaded but not displayed in a window
- * normal: loaded and displayed in a window
- *
- * Instead of storing file names all over the place, each file name is
- * stored in the buffer list. It can be referenced by a number.
- *
- * The current implementation remembers all file names ever used.
- */
+//
+// The buffer list is a double linked list of all buffers.
+// Each buffer can be in one of these states:
+// never loaded: BF_NEVERLOADED is set, only the file name is valid
+// not loaded: b_ml.ml_mfp == NULL, no memfile allocated
+// hidden: b_nwindows == 0, loaded but not displayed in a window
+// normal: loaded and displayed in a window
+//
+// Instead of storing file names all over the place, each file name is
+// stored in the buffer list. It can be referenced by a number.
+//
+// The current implementation remembers all file names ever used.
+//
#include <stdbool.h>
#include <string.h>
@@ -145,16 +145,13 @@ read_buffer(
return retval;
}
-/*
- * Open current buffer, that is: open the memfile and read the file into
- * memory.
- * Return FAIL for failure, OK otherwise.
- */
-int
-open_buffer (
- int read_stdin, /* read file from stdin */
- exarg_T *eap, /* for forced 'ff' and 'fenc' or NULL */
- int flags /* extra flags for readfile() */
+// Open current buffer, that is: open the memfile and read the file into
+// memory.
+// Return FAIL for failure, OK otherwise.
+int open_buffer(
+ int read_stdin, // read file from stdin
+ exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
+ int flags // extra flags for readfile()
)
{
int retval = OK;
@@ -169,14 +166,14 @@ open_buffer (
*/
if (readonlymode && curbuf->b_ffname != NULL
&& (curbuf->b_flags & BF_NEVERLOADED))
- curbuf->b_p_ro = TRUE;
+ curbuf->b_p_ro = true;
if (ml_open(curbuf) == FAIL) {
/*
* There MUST be a memfile, otherwise we can't do anything
* If we can't create one for the current buffer, take another buffer
*/
- close_buffer(NULL, curbuf, 0, FALSE);
+ close_buffer(NULL, curbuf, 0, false);
curbuf = NULL;
FOR_ALL_BUFFERS(buf) {
@@ -196,8 +193,9 @@ open_buffer (
}
EMSG(_("E83: Cannot allocate buffer, using other one..."));
enter_buffer(curbuf);
- if (old_tw != curbuf->b_p_tw)
+ if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
+ }
return FAIL;
}
@@ -206,7 +204,7 @@ open_buffer (
set_bufref(&old_curbuf, curbuf);
modified_was_set = false;
- /* mark cursor position as being invalid */
+ // mark cursor position as being invalid
curwin->w_valid = 0;
if (curbuf->b_ffname != NULL) {
@@ -265,7 +263,7 @@ open_buffer (
* it possible to retry when 'fileformat' or 'fileencoding' was
* guessed wrong.
*/
- curbuf->b_p_bin = TRUE;
+ curbuf->b_p_bin = true;
retval = readfile(NULL, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL,
flags | (READ_NEW + READ_STDIN));
@@ -275,7 +273,7 @@ open_buffer (
}
}
- /* if first time loading this buffer, init b_chartab[] */
+ // if first time loading this buffer, init b_chartab[]
if (curbuf->b_flags & BF_NEVERLOADED) {
(void)buf_init_chartab(curbuf, false);
parse_cino(curbuf);
@@ -302,20 +300,21 @@ open_buffer (
curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
- /* require "!" to overwrite the file, because it wasn't read completely */
- if (aborting())
+ // require "!" to overwrite the file, because it wasn't read completely
+ if (aborting()) {
curbuf->b_flags |= BF_READERR;
+ }
/* Need to update automatic folding. Do this before the autocommands,
* they may use the fold info. */
foldUpdateAll(curwin);
- /* need to set w_topline, unless some autocommand already did that. */
+ // need to set w_topline, unless some autocommand already did that.
if (!(curwin->w_valid & VALID_TOPLINE)) {
curwin->w_topline = 1;
curwin->w_topfill = 0;
}
- apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
+ apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf, &retval);
if (retval == FAIL) {
return FAIL;
@@ -333,10 +332,10 @@ open_buffer (
do_modelines(0);
curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
- apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
- &retval);
+ apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, false, curbuf,
+ &retval);
- /* restore curwin/curbuf and a few other things */
+ // restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
}
@@ -457,14 +456,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
set_last_cursor(win);
}
buflist_setfpos(buf, win,
- win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
- win->w_cursor.col, TRUE);
+ win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
+ win->w_cursor.col, true);
}
bufref_T bufref;
set_bufref(&bufref, buf);
- /* When the buffer is no longer in a window, trigger BufWinLeave */
+ // When the buffer is no longer in a window, trigger BufWinLeave
if (buf->b_nwindows == 1) {
buf->b_locked++;
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
@@ -497,8 +496,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
return;
}
}
- if (aborting()) /* autocmds may abort script processing */
+ if (aborting()) { // autocmds may abort script processing
return;
+ }
}
// If the buffer was in curwin and the window has changed, go back to that
@@ -531,9 +531,10 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
terminal_close(buf->terminal, NULL);
}
- /* Always remove the buffer when there is no file name. */
- if (buf->b_ffname == NULL)
- del_buf = TRUE;
+ // Always remove the buffer when there is no file name.
+ if (buf->b_ffname == NULL) {
+ del_buf = true;
+ }
/*
* Free all things allocated for this buffer.
@@ -573,8 +574,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
* obtained anyway. Therefore only return if curbuf changed to the
* deleted buffer.
*/
- if (buf == curbuf && !is_curbuf)
+ if (buf == curbuf && !is_curbuf) {
return;
+ }
if (win != NULL // Avoid bogus clang warning.
&& win_valid_any_tab(win)
@@ -588,7 +590,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
buf->b_nwindows--;
}
- /* Change directories when the 'acd' option is set. */
+ // Change directories when the 'acd' option is set.
do_autochdir();
// Disable buffer-updates for the current buffer.
@@ -601,30 +603,33 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
if (wipe_buf) {
xfree(buf->b_ffname);
xfree(buf->b_sfname);
- if (buf->b_prev == NULL)
+ if (buf->b_prev == NULL) {
firstbuf = buf->b_next;
- else
+ } else {
buf->b_prev->b_next = buf->b_next;
- if (buf->b_next == NULL)
+ }
+ if (buf->b_next == NULL) {
lastbuf = buf->b_prev;
- else
+ } else {
buf->b_next->b_prev = buf->b_prev;
+ }
free_buffer(buf);
} else {
if (del_buf) {
/* Free all internal variables and reset option values, to make
* ":bdel" compatible with Vim 5.7. */
- free_buffer_stuff(buf, TRUE);
+ free_buffer_stuff(buf, true);
- /* Make it look like a new buffer. */
+ // Make it look like a new buffer.
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- /* Init the options when loaded again. */
+ // Init the options when loaded again.
buf->b_p_initialized = false;
}
buf_clear_file(buf);
- if (del_buf)
- buf->b_p_bl = FALSE;
+ if (del_buf) {
+ buf->b_p_bl = false;
+ }
}
}
@@ -634,13 +639,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
void buf_clear_file(buf_T *buf)
{
buf->b_ml.ml_line_count = 1;
- unchanged(buf, TRUE);
- buf->b_p_eol = TRUE;
- buf->b_start_eol = TRUE;
- buf->b_p_bomb = FALSE;
- buf->b_start_bomb = FALSE;
+ unchanged(buf, true);
+ buf->b_p_eol = true;
+ buf->b_start_eol = true;
+ buf->b_p_bomb = false;
+ buf->b_start_bomb = false;
buf->b_ml.ml_mfp = NULL;
- buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
+ buf->b_ml.ml_flags = ML_EMPTY; // empty buffer
}
/// Clears the current buffer contents.
@@ -714,29 +719,30 @@ void buf_freeall(buf_T *buf, int flags)
* it's OK to delete the curbuf, because a new one is obtained anyway.
* Therefore only return if curbuf changed to the deleted buffer.
*/
- if (buf == curbuf && !is_curbuf)
+ if (buf == curbuf && !is_curbuf) {
return;
+ }
diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer.
// Remove any ownsyntax, unless exiting.
if (curwin != NULL && curwin->w_buffer == buf) {
reset_synblock(curwin);
}
- /* No folds in an empty buffer. */
+ // No folds in an empty buffer.
FOR_ALL_TAB_WINDOWS(tp, win) {
if (win->w_buffer == buf) {
clearFolding(win);
}
}
- ml_close(buf, TRUE); /* close and delete the memline/memfile */
- buf->b_ml.ml_line_count = 0; /* no lines in buffer */
+ ml_close(buf, true); // close and delete the memline/memfile
+ buf->b_ml.ml_line_count = 0; // no lines in buffer
if ((flags & BFA_KEEP_UNDO) == 0) {
- u_blockfree(buf); /* free the memory allocated for undo */
- u_clearall(buf); /* reset all undo information */
+ u_blockfree(buf); // free the memory allocated for undo
+ u_clearall(buf); // reset all undo information
}
- syntax_clear(&buf->b_s); /* reset syntax info */
- buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */
+ syntax_clear(&buf->b_s); // reset syntax info
+ buf->b_flags &= ~BF_READERR; // a read error is no longer relevant
}
/*
@@ -775,10 +781,10 @@ static void free_buffer(buf_T *buf)
/*
* Free stuff in the buffer for ":bdel" and when wiping out the buffer.
*/
-static void
-free_buffer_stuff (
+static void
+free_buffer_stuff(
buf_T *buf,
- int free_options /* free options as well */
+ int free_options // free options as well
)
{
if (free_options) {
@@ -842,14 +848,14 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count)
if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') {
cleanup_T cs;
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a window. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a window.
enter_cleanup(&cs);
// Quitting means closing the split window, nothing else.
win_close(curwin, true);
swap_exists_action = SEA_NONE;
- swap_exists_did_quit = TRUE;
+ swap_exists_did_quit = true;
/* Restore the error/interrupt/exception state if not discarded by a
* new aborting error, interrupt, or uncaught exception. */
@@ -871,8 +877,8 @@ void handle_swap_exists(bufref_T *old_curbuf)
buf_T *buf;
if (swap_exists_action == SEA_QUIT) {
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a buffer.
enter_cleanup(&cs);
// User selected Quit at ATTENTION prompt. Go back to previous
@@ -902,20 +908,20 @@ void handle_swap_exists(bufref_T *old_curbuf)
check_colorcolumn(curwin);
}
}
- /* If "old_curbuf" is NULL we are in big trouble here... */
+ // If "old_curbuf" is NULL we are in big trouble here...
/* Restore the error/interrupt/exception state if not discarded by a
* new aborting error, interrupt, or uncaught exception. */
leave_cleanup(&cs);
} else if (swap_exists_action == SEA_RECOVER) {
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a buffer.
enter_cleanup(&cs);
- /* User selected Recover at ATTENTION prompt. */
- msg_scroll = TRUE;
+ // User selected Recover at ATTENTION prompt.
+ msg_scroll = true;
ml_recover();
- MSG_PUTS("\n"); /* don't overwrite the last message */
+ MSG_PUTS("\n"); // don't overwrite the last message
cmdline_row = msg_row;
do_modelines(0);
@@ -940,30 +946,32 @@ void handle_swap_exists(bufref_T *old_curbuf)
* Returns error message or NULL
*/
char_u *
-do_bufdel (
+do_bufdel(
int command,
- char_u *arg, /* pointer to extra arguments */
+ char_u *arg, // pointer to extra arguments
int addr_count,
- int start_bnr, /* first buffer number in a range */
- int end_bnr, /* buffer nr or last buffer nr in a range */
+ int start_bnr, // first buffer number in a range
+ int end_bnr, // buffer nr or last buffer nr in a range
int forceit
)
{
- int do_current = 0; /* delete current buffer? */
- int deleted = 0; /* number of buffers deleted */
- char_u *errormsg = NULL; /* return value */
- int bnr; /* buffer number */
+ int do_current = 0; // delete current buffer?
+ int deleted = 0; // number of buffers deleted
+ char_u *errormsg = NULL; // return value
+ int bnr; // buffer number
char_u *p;
if (addr_count == 0) {
(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
} else {
if (addr_count == 2) {
- if (*arg) /* both range and argument is not allowed */
+ if (*arg) { // both range and argument is not allowed
return (char_u *)_(e_trailing);
+ }
bnr = start_bnr;
- } else /* addr_count == 1 */
+ } else { // addr_count == 1
bnr = end_bnr;
+ }
for (; !got_int; os_breakcheck()) {
/*
@@ -972,61 +980,71 @@ do_bufdel (
* the current one and will be loaded, which may then
* also be deleted, etc.
*/
- if (bnr == curbuf->b_fnum)
+ if (bnr == curbuf->b_fnum) {
do_current = bnr;
- else if (do_buffer(command, DOBUF_FIRST, FORWARD, bnr,
- forceit) == OK)
- ++deleted;
+ } else if (do_buffer(command, DOBUF_FIRST, FORWARD, bnr,
+ forceit) == OK) {
+ deleted++;
+ }
/*
* find next buffer number to delete/unload
*/
if (addr_count == 2) {
- if (++bnr > end_bnr)
+ if (++bnr > end_bnr) {
break;
- } else { /* addr_count == 1 */
+ }
+ } else { // addr_count == 1
arg = skipwhite(arg);
- if (*arg == NUL)
+ if (*arg == NUL) {
break;
+ }
if (!ascii_isdigit(*arg)) {
p = skiptowhite_esc(arg);
bnr = buflist_findpat(arg, p, command == DOBUF_WIPE,
- FALSE, FALSE);
- if (bnr < 0) /* failed */
+ false, false);
+ if (bnr < 0) { // failed
break;
+ }
arg = p;
} else
bnr = getdigits_int(&arg);
}
}
- if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
- FORWARD, do_current, forceit) == OK)
- ++deleted;
+ if (!got_int && do_current
+ && do_buffer(command, DOBUF_FIRST,
+ FORWARD, do_current, forceit) == OK) {
+ deleted++;
+ }
if (deleted == 0) {
- if (command == DOBUF_UNLOAD)
+ if (command == DOBUF_UNLOAD) {
STRCPY(IObuff, _("E515: No buffers were unloaded"));
- else if (command == DOBUF_DEL)
+ } else if (command == DOBUF_DEL) {
STRCPY(IObuff, _("E516: No buffers were deleted"));
- else
+ } else {
STRCPY(IObuff, _("E517: No buffers were wiped out"));
+ }
errormsg = IObuff;
} else if (deleted >= p_report) {
if (command == DOBUF_UNLOAD) {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer unloaded"));
- else
+ } else {
smsg(_("%d buffers unloaded"), deleted);
+ }
} else if (command == DOBUF_DEL) {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer deleted"));
- else
+ } else {
smsg(_("%d buffers deleted"), deleted);
+ }
} else {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer wiped out"));
- else
+ } else {
smsg(_("%d buffers wiped out"), deleted);
+ }
}
}
}
@@ -1055,8 +1073,8 @@ static int empty_curbuf(int close_others, int forceit, int action)
set_bufref(&bufref, buf);
if (close_others) {
- /* Close any other windows on this buffer, then make it empty. */
- close_windows(buf, TRUE);
+ // Close any other windows on this buffer, then make it empty.
+ close_windows(buf, true);
}
setpcmark();
@@ -1092,13 +1110,13 @@ static int empty_curbuf(int close_others, int forceit, int action)
*
* Return FAIL or OK.
*/
-int
-do_buffer (
+int
+do_buffer(
int action,
int start,
- int dir, /* FORWARD or BACKWARD */
- int count, /* buffer number or number of buffers */
- int forceit /* TRUE for :...! */
+ int dir, // FORWARD or BACKWARD
+ int count, // buffer number or number of buffers
+ int forceit // true for :...!
)
{
buf_T *buf;
@@ -1111,51 +1129,56 @@ do_buffer (
case DOBUF_LAST: buf = lastbuf; break;
default: buf = curbuf; break;
}
- if (start == DOBUF_MOD) { /* find next modified buffer */
+ if (start == DOBUF_MOD) { // find next modified buffer
while (count-- > 0) {
do {
buf = buf->b_next;
- if (buf == NULL)
+ if (buf == NULL) {
buf = firstbuf;
+ }
} while (buf != curbuf && !bufIsChanged(buf));
}
if (!bufIsChanged(buf)) {
EMSG(_("E84: No modified buffer found"));
return FAIL;
}
- } else if (start == DOBUF_FIRST && count) { /* find specified buffer number */
- while (buf != NULL && buf->b_fnum != count)
+ } else if (start == DOBUF_FIRST && count) { // find specified buffer number
+ while (buf != NULL && buf->b_fnum != count) {
buf = buf->b_next;
+ }
} else {
bp = NULL;
while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) {
/* remember the buffer where we start, we come back there when all
* buffers are unlisted. */
- if (bp == NULL)
+ if (bp == NULL) {
bp = buf;
+ }
if (dir == FORWARD) {
buf = buf->b_next;
- if (buf == NULL)
+ if (buf == NULL) {
buf = firstbuf;
+ }
} else {
buf = buf->b_prev;
- if (buf == NULL)
+ if (buf == NULL) {
buf = lastbuf;
+ }
}
- /* don't count unlisted buffers */
+ // don't count unlisted buffers
if (unload || buf->b_p_bl) {
- --count;
- bp = NULL; /* use this buffer as new starting point */
+ count--;
+ bp = NULL; // use this buffer as new starting point
}
if (bp == buf) {
- /* back where we started, didn't find anything. */
+ // back where we started, didn't find anything.
EMSG(_("E85: There is no listed buffer"));
return FAIL;
}
}
}
- if (buf == NULL) { /* could not find it */
+ if (buf == NULL) { // could not find it
if (start == DOBUF_FIRST) {
// don't warn when deleting
if (!unload) {
@@ -1180,8 +1203,9 @@ do_buffer (
/* When unloading or deleting a buffer that's already unloaded and
* unlisted: fail silently. */
- if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
+ if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) {
return FAIL;
+ }
if (!forceit && (buf->terminal || bufIsChanged(buf))) {
if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) {
@@ -1231,8 +1255,9 @@ do_buffer (
break;
}
}
- if (bp == NULL && buf == curbuf)
- return empty_curbuf(TRUE, forceit, action);
+ if (bp == NULL && buf == curbuf) {
+ return empty_curbuf(true, forceit, action);
+ }
/*
* If the deleted buffer is the current one, close the current window
@@ -1242,8 +1267,9 @@ do_buffer (
while (buf == curbuf
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, false) == FAIL)
+ if (win_close(curwin, false) == FAIL) {
break;
+ }
}
/*
@@ -1273,61 +1299,72 @@ do_buffer (
int jumpidx;
jumpidx = curwin->w_jumplistidx - 1;
- if (jumpidx < 0)
+ if (jumpidx < 0) {
jumpidx = curwin->w_jumplistlen - 1;
+ }
forward = jumpidx;
while (jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
if (buf != NULL) {
- if (buf == curbuf || !buf->b_p_bl)
- buf = NULL; /* skip current and unlisted bufs */
- else if (buf->b_ml.ml_mfp == NULL) {
- /* skip unloaded buf, but may keep it for later */
- if (bp == NULL)
+ if (buf == curbuf || !buf->b_p_bl) {
+ buf = NULL; // skip current and unlisted bufs
+ } else if (buf->b_ml.ml_mfp == NULL) {
+ // skip unloaded buf, but may keep it for later
+ if (bp == NULL) {
bp = buf;
+ }
buf = NULL;
}
}
- if (buf != NULL) /* found a valid buffer: stop searching */
+ if (buf != NULL) { // found a valid buffer: stop searching
break;
- /* advance to older entry in jump list */
- if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
+ }
+ // advance to older entry in jump list
+ if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) {
break;
- if (--jumpidx < 0)
+ }
+ if (--jumpidx < 0) {
jumpidx = curwin->w_jumplistlen - 1;
- if (jumpidx == forward) /* List exhausted for sure */
+ }
+ if (jumpidx == forward) { // List exhausted for sure
break;
+ }
}
}
- if (buf == NULL) { /* No previous buffer, Try 2'nd approach */
- forward = TRUE;
+ if (buf == NULL) { // No previous buffer, Try 2'nd approach
+ forward = true;
buf = curbuf->b_next;
for (;; ) {
if (buf == NULL) {
- if (!forward) /* tried both directions */
+ if (!forward) { // tried both directions
break;
+ }
buf = curbuf->b_prev;
- forward = FALSE;
+ forward = false;
continue;
}
- /* in non-help buffer, try to skip help buffers, and vv */
+ // in non-help buffer, try to skip help buffers, and vv
if (buf->b_help == curbuf->b_help && buf->b_p_bl) {
- if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */
+ if (buf->b_ml.ml_mfp != NULL) { // found loaded buffer
break;
- if (bp == NULL) /* remember unloaded buf for later */
+ }
+ if (bp == NULL) { // remember unloaded buf for later
bp = buf;
+ }
}
- if (forward)
+ if (forward) {
buf = buf->b_next;
- else
+ } else {
buf = buf->b_prev;
+ }
}
}
- if (buf == NULL) /* No loaded buffer, use unloaded one */
+ if (buf == NULL) { // No loaded buffer, use unloaded one
buf = bp;
- if (buf == NULL) { /* No loaded buffer, find listed one */
+ }
+ if (buf == NULL) { // No loaded buffer, find listed one
FOR_ALL_BUFFERS(buf2) {
if (buf2->b_p_bl && buf2 != curbuf) {
buf = buf2;
@@ -1335,39 +1372,44 @@ do_buffer (
}
}
}
- if (buf == NULL) { /* Still no buffer, just take one */
- if (curbuf->b_next != NULL)
+ if (buf == NULL) { // Still no buffer, just take one
+ if (curbuf->b_next != NULL) {
buf = curbuf->b_next;
- else
+ } else {
buf = curbuf->b_prev;
+ }
}
}
if (buf == NULL) {
/* Autocommands must have wiped out all other buffers. Only option
* now is to make the current buffer empty. */
- return empty_curbuf(FALSE, forceit, action);
+ return empty_curbuf(false, forceit, action);
}
/*
* make buf current buffer
*/
- if (action == DOBUF_SPLIT) { /* split window first */
- /* If 'switchbuf' contains "useopen": jump to first window containing
- * "buf" if one exists */
- if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
+ if (action == DOBUF_SPLIT) { // split window first
+ // If 'switchbuf' contains "useopen": jump to first window containing
+ // "buf" if one exists
+ if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) {
return OK;
- /* If 'switchbuf' contains "usetab": jump to first window in any tab
- * page containing "buf" if one exists */
- if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
+ }
+ // If 'switchbuf' contains "usetab": jump to first window in any tab
+ // page containing "buf" if one exists
+ if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) {
return OK;
- if (win_split(0, 0) == FAIL)
+ }
+ if (win_split(0, 0) == FAIL) {
return FAIL;
+ }
}
- /* go to current buffer - nothing to do */
- if (buf == curbuf)
+ // go to current buffer - nothing to do
+ if (buf == curbuf) {
return OK;
+ }
/*
* Check if the current buffer may be abandoned.
@@ -1388,15 +1430,16 @@ do_buffer (
}
}
- /* Go to the other buffer. */
+ // Go to the other buffer.
set_curbuf(buf, action);
if (action == DOBUF_SPLIT) {
- RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */
+ RESET_BINDING(curwin); // reset 'scrollbind' and 'cursorbind'
}
- if (aborting()) /* autocmds may abort script processing */
+ if (aborting()) { // autocmds may abort script processing
return FAIL;
+ }
return OK;
}
@@ -1419,12 +1462,13 @@ void set_curbuf(buf_T *buf, int action)
long old_tw = curbuf->b_p_tw;
setpcmark();
- if (!cmdmod.keepalt)
- curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */
- buflist_altfpos(curwin); /* remember curpos */
+ if (!cmdmod.keepalt) {
+ curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file
+ }
+ buflist_altfpos(curwin); // remember curpos
- /* Don't restart Select mode after switching to another buffer. */
- VIsual_reselect = FALSE;
+ // Don't restart Select mode after switching to another buffer.
+ VIsual_reselect = false;
// close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
prevbuf = curbuf;
@@ -1470,8 +1514,9 @@ void set_curbuf(buf_T *buf, int action)
) || curwin->w_buffer == NULL
) {
enter_buffer(buf);
- if (old_tw != curbuf->b_p_tw)
+ if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
+ }
}
if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
@@ -1486,75 +1531,83 @@ void set_curbuf(buf_T *buf, int action)
*/
void enter_buffer(buf_T *buf)
{
- /* Copy buffer and window local option values. Not for a help buffer. */
+ // Copy buffer and window local option values. Not for a help buffer.
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
- if (!buf->b_help)
+ if (!buf->b_help) {
get_winopts(buf);
- else
- /* Remove all folds in the window. */
+ } else {
+ // Remove all folds in the window.
clearFolding(curwin);
- foldUpdateAll(curwin); /* update folds (later). */
+ }
+ foldUpdateAll(curwin); // update folds (later).
- /* Get the buffer in the current window. */
+ // Get the buffer in the current window.
curwin->w_buffer = buf;
curbuf = buf;
- ++curbuf->b_nwindows;
+ curbuf->b_nwindows++;
- if (curwin->w_p_diff)
+ if (curwin->w_p_diff) {
diff_buf_add(curbuf);
+ }
- curwin->w_s = &(buf->b_s);
+ curwin->w_s = &(curbuf->b_s);
- /* Cursor on first line by default. */
+ // Cursor on first line by default.
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
- curwin->w_topline_was_set = FALSE;
+ curwin->w_set_curswant = true;
+ curwin->w_topline_was_set = false;
- /* mark cursor position as being invalid */
+ // mark cursor position as being invalid
curwin->w_valid = 0;
- /* Make sure the buffer is loaded. */
- if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */
- /* If there is no filetype, allow for detecting one. Esp. useful for
- * ":ball" used in an autocommand. If there already is a filetype we
- * might prefer to keep it. */
- if (*curbuf->b_p_ft == NUL)
- did_filetype = FALSE;
+ // Make sure the buffer is loaded.
+ if (curbuf->b_ml.ml_mfp == NULL) { // need to load the file
+ // If there is no filetype, allow for detecting one. Esp. useful for
+ // ":ball" used in an autocommand. If there already is a filetype we
+ // might prefer to keep it.
+ if (*curbuf->b_p_ft == NUL) {
+ did_filetype = false;
+ }
- open_buffer(FALSE, NULL, 0);
+ open_buffer(false, NULL, 0);
} else {
- if (!msg_silent)
- need_fileinfo = TRUE; /* display file info after redraw */
- (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */
+ if (!msg_silent) {
+ need_fileinfo = true; // display file info after redraw
+ }
+ (void)buf_check_timestamp(curbuf, false); // check if file changed
curwin->w_topline = 1;
curwin->w_topfill = 0;
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, false, curbuf);
}
/* If autocommands did not change the cursor position, restore cursor lnum
* and possibly cursor col. */
- if (curwin->w_cursor.lnum == 1 && inindent(0))
+ if (curwin->w_cursor.lnum == 1 && inindent(0)) {
buflist_getfpos();
+ }
- check_arg_idx(curwin); /* check for valid arg_idx */
+ check_arg_idx(curwin); // check for valid arg_idx
maketitle();
- /* when autocmds didn't change it */
- if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
- scroll_cursor_halfway(FALSE); /* redisplay at correct position */
+ // when autocmds didn't change it
+ if (curwin->w_topline == 1 && !curwin->w_topline_was_set) {
+ scroll_cursor_halfway(false); // redisplay at correct position
+ }
- /* Change directories when the 'acd' option is set. */
+ // Change directories when the 'acd' option is set.
do_autochdir();
- if (curbuf->b_kmap_state & KEYMAP_INIT)
+ if (curbuf->b_kmap_state & KEYMAP_INIT) {
(void)keymap_init();
- /* May need to set the spell language. Can only do this after the buffer
- * has been properly setup. */
- if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
+ }
+ // May need to set the spell language. Can only do this after the buffer
+ // has been properly setup.
+ if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
(void)did_set_spelllang(curwin);
+ }
redraw_later(NOT_VALID);
}
@@ -1567,6 +1620,7 @@ void do_autochdir(void)
if (starting == 0
&& curbuf->b_ffname != NULL
&& vim_chdirfile(curbuf->b_ffname) == OK) {
+ post_chdir(kCdScopeGlobal, false);
shorten_fnames(true);
}
}
@@ -1601,11 +1655,11 @@ static inline void buf_init_changedtick(buf_T *const buf)
/// Add a file name to the buffer list.
/// If the same file name already exists return a pointer to that buffer.
/// If it does not exist, or if fname == NULL, a new entry is created.
-/// If (flags & BLN_CURBUF) is TRUE, may use current buffer.
-/// If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
-/// If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
-/// If (flags & BLN_NEW) is TRUE, don't use an existing buffer.
-/// If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer
+/// If (flags & BLN_CURBUF) is true, may use current buffer.
+/// If (flags & BLN_LISTED) is true, add new buffer to buffer list.
+/// If (flags & BLN_DUMMY) is true, don't count it as a real buffer.
+/// If (flags & BLN_NEW) is true, don't use an existing buffer.
+/// If (flags & BLN_NOOPT) is true, don't copy options from the current buffer
/// if the buffer already exists.
/// This is the ONLY way to create a new buffer.
///
@@ -1664,22 +1718,22 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
* buffer.)
*/
buf = NULL;
- if ((flags & BLN_CURBUF)
- && curbuf != NULL
- && curbuf->b_ffname == NULL
- && curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) {
+ if ((flags & BLN_CURBUF) && curbuf_reusable()) {
+ assert(curbuf != NULL);
buf = curbuf;
/* It's like this buffer is deleted. Watch out for autocommands that
* change curbuf! If that happens, allocate a new buffer anyway. */
- if (curbuf->b_p_bl)
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
- if (buf == curbuf)
- apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
- if (aborting()) /* autocmds may abort script processing */
+ if (curbuf->b_p_bl) {
+ apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
+ }
+ if (buf == curbuf) {
+ apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, false, curbuf);
+ }
+ if (aborting()) { // autocmds may abort script processing
return NULL;
+ }
if (buf == curbuf) {
- /* Make sure 'bufhidden' and 'buftype' are empty */
+ // Make sure 'bufhidden' and 'buftype' are empty
clear_string_option(&buf->b_p_bh);
clear_string_option(&buf->b_p_bt);
}
@@ -1688,6 +1742,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
+ buf->b_signcols_max = -1;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -1705,35 +1760,38 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf->b_ffname = NULL;
xfree(buf->b_sfname);
buf->b_sfname = NULL;
- if (buf != curbuf)
+ if (buf != curbuf) {
free_buffer(buf);
+ }
return NULL;
}
if (buf == curbuf) {
- /* free all things allocated for this buffer */
+ // free all things allocated for this buffer
buf_freeall(buf, 0);
- if (buf != curbuf) /* autocommands deleted the buffer! */
+ if (buf != curbuf) { // autocommands deleted the buffer!
return NULL;
- if (aborting()) /* autocmds may abort script processing */
+ }
+ if (aborting()) { // autocmds may abort script processing
return NULL;
- free_buffer_stuff(buf, FALSE); /* delete local variables et al. */
+ }
+ free_buffer_stuff(buf, false); // delete local variables et al.
- /* Init the options. */
+ // Init the options.
buf->b_p_initialized = false;
buf_copy_options(buf, BCO_ENTER);
- /* need to reload lmaps and set b:keymap_name */
+ // need to reload lmaps and set b:keymap_name
curbuf->b_kmap_state |= KEYMAP_INIT;
} else {
/*
* put new buffer at the end of the buffer list
*/
buf->b_next = NULL;
- if (firstbuf == NULL) { /* buffer list is empty */
+ if (firstbuf == NULL) { // buffer list is empty
buf->b_prev = NULL;
firstbuf = buf;
- } else { /* append new buffer at end of list */
+ } else { // append new buffer at end of list
lastbuf->b_next = buf;
buf->b_prev = lastbuf;
}
@@ -1771,8 +1829,9 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
}
buf->b_u_synced = true;
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- if (flags & BLN_DUMMY)
+ if (flags & BLN_DUMMY) {
buf->b_flags |= BF_DUMMY;
+ }
buf_clear_file(buf);
clrallmarks(buf); // clear marks
fmarks_check_names(buf); // check file marks for this file
@@ -1803,9 +1862,21 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
return buf;
}
+/// Return true if the current buffer is empty, unnamed, unmodified and used in
+/// only one window. That means it can be reused.
+bool curbuf_reusable(void)
+{
+ return (curbuf != NULL
+ && curbuf->b_ffname == NULL
+ && curbuf->b_nwindows <= 1
+ && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())
+ && !bt_quickfix(curbuf)
+ && !curbufIsChanged());
+}
+
/*
* Free the memory for the options of a buffer.
- * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
+ * If "free_p_ff" is true also free 'fileformat', 'buftype' and
* 'fileencoding'.
*/
void free_buf_options(buf_T *buf, int free_p_ff)
@@ -1884,25 +1955,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
buf = buflist_findnr(n);
if (buf == NULL) {
- if ((options & GETF_ALT) && n == 0)
+ if ((options & GETF_ALT) && n == 0) {
EMSG(_(e_noalt));
- else
+ } else {
EMSGN(_("E92: Buffer %" PRId64 " not found"), n);
+ }
return FAIL;
}
- /* if alternate file is the current buffer, nothing to do */
- if (buf == curbuf)
+ // if alternate file is the current buffer, nothing to do
+ if (buf == curbuf) {
return OK;
+ }
if (text_locked()) {
text_locked_msg();
return FAIL;
}
- if (curbuf_locked())
+ if (curbuf_locked()) {
return FAIL;
+ }
- /* altfpos may be changed by getfile(), get it now */
+ // altfpos may be changed by getfile(), get it now
if (lnum == 0) {
fpos = buflist_findfpos(buf);
lnum = fpos->lnum;
@@ -1942,12 +2016,12 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
(options & GETF_SETMARK), lnum, forceit))) {
RedrawingDisabled--;
- /* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */
+ // cursor is at to BOL and w_cursor.lnum is checked due to getfile()
if (!p_sol && col != 0) {
curwin->w_cursor.col = col;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
return OK;
}
@@ -1965,13 +2039,13 @@ void buflist_getfpos(void)
curwin->w_cursor.lnum = fpos->lnum;
check_cursor_lnum();
- if (p_sol)
+ if (p_sol) {
curwin->w_cursor.col = 0;
- else {
+ } else {
curwin->w_cursor.col = fpos->col;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
}
@@ -1984,12 +2058,13 @@ buf_T *buflist_findname_exp(char_u *fname)
char_u *ffname;
buf_T *buf = NULL;
- /* First make the name into a full path name */
+ // First make the name into a full path name
ffname = (char_u *)FullName_save((char *)fname,
#ifdef UNIX
- TRUE /* force expansion, get rid of symbolic links */
+ // force expansion, get rid of symbolic links
+ true
#else
- FALSE
+ false
#endif
);
if (ffname != NULL) {
@@ -2059,34 +2134,37 @@ int buflist_findpat(
if (diffmode && !(found_buf && diff_mode_buf(found_buf))) {
match = -1;
}
- }
- /*
- * Try four ways of matching a listed buffer:
- * attempt == 0: without '^' or '$' (at any position)
- * attempt == 1: with '^' at start (only at position 0)
- * attempt == 2: with '$' at end (only match at end)
- * attempt == 3: with '^' at start and '$' at end (only full match)
- * Repeat this for finding an unlisted buffer if there was no matching
- * listed buffer.
- */
- else {
- pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE);
- if (pat == NULL)
+ } else {
+ //
+ // Try four ways of matching a listed buffer:
+ // attempt == 0: without '^' or '$' (at any position)
+ // attempt == 1: with '^' at start (only at position 0)
+ // attempt == 2: with '$' at end (only match at end)
+ // attempt == 3: with '^' at start and '$' at end (only full match)
+ // Repeat this for finding an unlisted buffer if there was no matching
+ // listed buffer.
+ //
+
+ pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, false);
+ if (pat == NULL) {
return -1;
+ }
patend = pat + STRLEN(pat) - 1;
toggledollar = (patend > pat && *patend == '$');
- /* First try finding a listed buffer. If not found and "unlisted"
- * is TRUE, try finding an unlisted buffer. */
- find_listed = TRUE;
+ // First try finding a listed buffer. If not found and "unlisted"
+ // is true, try finding an unlisted buffer.
+ find_listed = true;
for (;; ) {
- for (attempt = 0; attempt <= 3; ++attempt) {
- /* may add '^' and '$' */
- if (toggledollar)
- *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */
+ for (attempt = 0; attempt <= 3; attempt++) {
+ // may add '^' and '$'
+ if (toggledollar) {
+ *patend = (attempt < 2) ? NUL : '$'; // add/remove '$'
+ }
p = pat;
- if (*p == '^' && !(attempt & 1)) /* add/remove '^' */
- ++p;
+ if (*p == '^' && !(attempt & 1)) { // add/remove '^'
+ p++;
+ }
regmatch_T regmatch;
regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
@@ -2113,33 +2191,36 @@ int buflist_findpat(
continue;
}
}
- if (match >= 0) { /* already found a match */
+ if (match >= 0) { // already found a match
match = -2;
break;
}
- match = buf->b_fnum; /* remember first match */
+ match = buf->b_fnum; // remember first match
}
}
vim_regfree(regmatch.regprog);
- if (match >= 0) /* found one match */
+ if (match >= 0) { // found one match
break;
+ }
}
/* Only search for unlisted buffers if there was no match with
* a listed buffer. */
- if (!unlisted || !find_listed || match != -1)
+ if (!unlisted || !find_listed || match != -1) {
break;
- find_listed = FALSE;
+ }
+ find_listed = false;
}
xfree(pat);
}
- if (match == -2)
+ if (match == -2) {
EMSG2(_("E93: More than one match for %s"), pattern);
- else if (match < 0)
+ } else if (match < 0) {
EMSG2(_("E94: No matching buffer for %s"), pattern);
+ }
return match;
}
@@ -2157,10 +2238,10 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
int attempt;
char_u *patc;
- *num_file = 0; /* return values in case of FAIL */
+ *num_file = 0; // return values in case of FAIL
*file = NULL;
- /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */
+ // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
if (*pat == '^') {
patc = xmalloc(STRLEN(pat) + 11);
STRCPY(patc, "\\(^\\|[\\/]\\)");
@@ -2172,15 +2253,17 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
* attempt == 0: try match with '\<', match at start of word
* attempt == 1: try match without '\<', match anywhere
*/
- for (attempt = 0; attempt <= 1; ++attempt) {
- if (attempt > 0 && patc == pat)
- break; /* there was no anchor, no need to try again */
+ for (attempt = 0; attempt <= 1; attempt++) {
+ if (attempt > 0 && patc == pat) {
+ break; // there was no anchor, no need to try again
+ }
regmatch_T regmatch;
regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
if (regmatch.regprog == NULL) {
- if (patc != pat)
+ if (patc != pat) {
xfree(patc);
+ }
return FAIL;
}
@@ -2188,37 +2271,42 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
* round == 1: Count the matches.
* round == 2: Build the array to keep the matches.
*/
- for (round = 1; round <= 2; ++round) {
+ for (round = 1; round <= 2; round++) {
count = 0;
FOR_ALL_BUFFERS(buf) {
- if (!buf->b_p_bl) /* skip unlisted buffers */
+ if (!buf->b_p_bl) { // skip unlisted buffers
continue;
+ }
p = buflist_match(&regmatch, buf, p_wic);
if (p != NULL) {
- if (round == 1)
- ++count;
- else {
- if (options & WILD_HOME_REPLACE)
+ if (round == 1) {
+ count++;
+ } else {
+ if (options & WILD_HOME_REPLACE) {
p = home_replace_save(buf, p);
- else
+ } else {
p = vim_strsave(p);
+ }
(*file)[count++] = p;
}
}
}
- if (count == 0) /* no match found, break here */
+ if (count == 0) { // no match found, break here
break;
+ }
if (round == 1) {
*file = xmalloc((size_t)count * sizeof(**file));
}
}
vim_regfree(regmatch.regprog);
- if (count) /* match(es) found, break here */
+ if (count) { // match(es) found, break here
break;
+ }
}
- if (patc != pat)
+ if (patc != pat) {
xfree(patc);
+ }
*num_file = count;
return count == 0 ? FAIL : OK;
@@ -2227,7 +2315,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
/// Check for a match on the file name for buffer "buf" with regprog "prog".
///
-/// @param ignore_case When TRUE, ignore case. Use 'fic' otherwise.
+/// @param ignore_case When true, ignore case. Use 'fic' otherwise.
static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
{
// First try the short file name, then the long file name.
@@ -2240,7 +2328,7 @@ static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
/// Try matching the regexp in "prog" with file name "name".
///
-/// @param ignore_case When TRUE, ignore case. Use 'fileignorecase' otherwise.
+/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise.
/// @return "name" when there is a match, NULL when not.
static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
{
@@ -2250,13 +2338,14 @@ static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
if (name != NULL) {
// Ignore case when 'fileignorecase' or the argument is set.
rmp->rm_ic = p_fic || ignore_case;
- if (vim_regexec(rmp, name, (colnr_T)0))
+ if (vim_regexec(rmp, name, (colnr_T)0)) {
match = name;
- else {
- /* Replace $(HOME) with '~' and try matching again. */
+ } else {
+ // Replace $(HOME) with '~' and try matching again.
p = home_replace_save(NULL, name);
- if (vim_regexec(rmp, p, (colnr_T)0))
+ if (vim_regexec(rmp, p, (colnr_T)0)) {
match = name;
+ }
xfree(p);
}
}
@@ -2281,17 +2370,18 @@ buf_T *buflist_findnr(int nr)
* Returns a pointer to allocated memory, of NULL when failed.
*/
char_u *
-buflist_nr2name (
+buflist_nr2name(
int n,
int fullname,
- int helptail /* for help buffers return tail only */
+ int helptail // for help buffers return tail only
)
{
buf_T *buf;
buf = buflist_findnr(n);
- if (buf == NULL)
+ if (buf == NULL) {
return NULL;
+ }
return home_replace_save(helptail ? buf : NULL,
fullname ? buf->b_ffname : buf->b_fname);
}
@@ -2311,23 +2401,28 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
{
wininfo_T *wip;
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (wip->wi_win == win)
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (wip->wi_win == win) {
break;
+ }
+ }
if (wip == NULL) {
- /* allocate a new entry */
+ // allocate a new entry
wip = xcalloc(1, sizeof(wininfo_T));
wip->wi_win = win;
- if (lnum == 0) /* set lnum even when it's 0 */
+ if (lnum == 0) { // set lnum even when it's 0
lnum = 1;
+ }
} else {
- /* remove the entry from the list */
- if (wip->wi_prev)
+ // remove the entry from the list
+ if (wip->wi_prev) {
wip->wi_prev->wi_next = wip->wi_next;
- else
+ } else {
buf->b_wininfo = wip->wi_next;
- if (wip->wi_next)
+ }
+ if (wip->wi_next) {
wip->wi_next->wi_prev = wip->wi_prev;
+ }
if (copy_options && wip->wi_optset) {
clear_winopt(&wip->wi_opt);
deleteFoldRecurse(&wip->wi_folds);
@@ -2338,19 +2433,20 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
wip->wi_fpos.col = col;
}
if (copy_options) {
- /* Save the window-specific option values. */
+ // Save the window-specific option values.
copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
wip->wi_fold_manual = win->w_fold_manual;
cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
wip->wi_optset = true;
}
- /* insert the entry in front of the list */
+ // insert the entry in front of the list
wip->wi_next = buf->b_wininfo;
buf->b_wininfo = wip;
wip->wi_prev = NULL;
- if (wip->wi_next)
+ if (wip->wi_next) {
wip->wi_next->wi_prev = wip;
+ }
return;
}
@@ -2377,7 +2473,7 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
/*
* Find info for the current window in buffer "buf".
* If not found, return the info for the most recently used window.
- * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in
+ * When "skip_diff_buffer" is true avoid windows with 'diff' set that is in
* another tab page.
* Returns NULL when there isn't any info.
*/
@@ -2395,11 +2491,14 @@ static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer)
* 'diff' set and is in another tab page). */
if (wip == NULL) {
if (skip_diff_buffer) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (!wininfo_other_tab_diff(wip))
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (!wininfo_other_tab_diff(wip)) {
break;
- } else
+ }
+ }
+ } else {
wip = buf->b_wininfo;
+ }
}
return wip;
}
@@ -2433,9 +2532,10 @@ void get_winopts(buf_T *buf)
} else
copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
- /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
- if (p_fdls >= 0)
+ // Set 'foldlevel' to 'foldlevelstart' if it's not negative.
+ if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
+ }
didset_window_options(curwin);
}
@@ -2448,7 +2548,7 @@ pos_T *buflist_findfpos(buf_T *buf)
{
static pos_T no_position = { 1, 0, 0 };
- wininfo_T *wip = find_wininfo(buf, FALSE);
+ wininfo_T *wip = find_wininfo(buf, false);
return (wip == NULL) ? &no_position : &(wip->wi_fpos);
}
@@ -2509,7 +2609,7 @@ void buflist_list(exarg_T *eap)
len = IOSIZE - 20;
}
- /* put "line 999" in column 40 or after the file name */
+ // put "line 999" in column 40 or after the file name
i = 40 - vim_strsize(IObuff);
do {
IObuff[len++] = ' ';
@@ -2519,7 +2619,7 @@ void buflist_list(exarg_T *eap)
buf == curbuf ? (int64_t)curwin->w_cursor.lnum
: (int64_t)buflist_findlnum(buf));
msg_outtrans(IObuff);
- ui_flush(); /* output one line at a time */
+ ui_flush(); // output one line at a time
os_breakcheck();
}
}
@@ -2535,8 +2635,9 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
buf_T *buf;
buf = buflist_findnr(fnum);
- if (buf == NULL || buf->b_fname == NULL)
+ if (buf == NULL || buf->b_fname == NULL) {
return FAIL;
+ }
*fname = buf->b_fname;
*lnum = buflist_findlnum(buf);
@@ -2550,12 +2651,12 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
* Returns FAIL for failure (file name already in use by other buffer)
* OK otherwise.
*/
-int
-setfname (
+int
+setfname(
buf_T *buf,
char_u *ffname,
char_u *sfname,
- int message /* give message when buffer already exists */
+ int message // give message when buffer already exists
)
{
buf_T *obuf = NULL;
@@ -2563,15 +2664,16 @@ setfname (
bool file_id_valid = false;
if (ffname == NULL || *ffname == NUL) {
- /* Removing the name. */
+ // Removing the name.
xfree(buf->b_ffname);
xfree(buf->b_sfname);
buf->b_ffname = NULL;
buf->b_sfname = NULL;
} else {
- fname_expand(buf, &ffname, &sfname); /* will allocate ffname */
- if (ffname == NULL) /* out of memory */
+ fname_expand(buf, &ffname, &sfname); // will allocate ffname
+ if (ffname == NULL) { // out of memory
return FAIL;
+ }
/*
* if the file name is already used in another buffer:
@@ -2583,18 +2685,19 @@ setfname (
obuf = buflist_findname_file_id(ffname, &file_id, file_id_valid);
}
if (obuf != NULL && obuf != buf) {
- if (obuf->b_ml.ml_mfp != NULL) { /* it's loaded, fail */
- if (message)
+ if (obuf->b_ml.ml_mfp != NULL) { // it's loaded, fail
+ if (message) {
EMSG(_("E95: Buffer with this name already exists"));
+ }
xfree(ffname);
return FAIL;
}
- /* delete from the list */
- close_buffer(NULL, obuf, DOBUF_WIPE, FALSE);
+ // delete from the list
+ close_buffer(NULL, obuf, DOBUF_WIPE, false);
}
sfname = vim_strsave(sfname);
#ifdef USE_FNAME_CASE
- path_fix_case(sfname); /* set correct case for short file name */
+ path_fix_case(sfname); // set correct case for short file name
#endif
xfree(buf->b_ffname);
xfree(buf->b_sfname);
@@ -2643,15 +2746,17 @@ void buf_name_changed(buf_T *buf)
/*
* If the file name changed, also change the name of the swapfile
*/
- if (buf->b_ml.ml_mfp != NULL)
+ if (buf->b_ml.ml_mfp != NULL) {
ml_setname(buf);
+ }
- if (curwin->w_buffer == buf)
- check_arg_idx(curwin); /* check file name for arg list */
- maketitle(); /* set window title */
- status_redraw_all(); /* status lines need to be redrawn */
- fmarks_check_names(buf); /* check named file marks */
- ml_timestamp(buf); /* reset timestamp */
+ if (curwin->w_buffer == buf) {
+ check_arg_idx(curwin); // check file name for arg list
+ }
+ maketitle(); // set window title
+ status_redraw_all(); // status lines need to be redrawn
+ fmarks_check_names(buf); // check named file marks
+ ml_timestamp(buf); // reset timestamp
}
/*
@@ -2684,8 +2789,9 @@ char_u * getaltfname(
linenr_T dummy;
if (buflist_name_nr(0, &fname, &dummy) == FAIL) {
- if (errmsg)
+ if (errmsg) {
EMSG(_(e_noalt));
+ }
return NULL;
}
return fname;
@@ -2715,10 +2821,12 @@ int buflist_add(char_u *fname, int flags)
void buflist_slash_adjust(void)
{
FOR_ALL_BUFFERS(bp) {
- if (bp->b_ffname != NULL)
+ if (bp->b_ffname != NULL) {
slash_adjust(bp->b_ffname);
- if (bp->b_sfname != NULL)
+ }
+ if (bp->b_sfname != NULL) {
slash_adjust(bp->b_sfname);
+ }
}
}
@@ -2730,7 +2838,7 @@ void buflist_slash_adjust(void)
*/
void buflist_altfpos(win_T *win)
{
- buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
+ buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, true);
}
/// Check that "ffname" is not the same file as current file.
@@ -2818,9 +2926,9 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id)
/*
* Print info about the current buffer.
*/
-void
-fileinfo (
- int fullname, /* when non-zero print full path */
+void
+fileinfo(
+ int fullname, // when non-zero print full path
int shorthelp,
int dont_truncate
)
@@ -2833,20 +2941,21 @@ fileinfo (
buffer = xmalloc(IOSIZE);
- if (fullname > 1) { /* 2 CTRL-G: include buffer number */
+ if (fullname > 1) { // 2 CTRL-G: include buffer number
vim_snprintf((char *)buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
p = buffer + STRLEN(buffer);
} else
p = buffer;
*p++ = '"';
- if (buf_spname(curbuf) != NULL)
+ if (buf_spname(curbuf) != NULL) {
STRLCPY(p, buf_spname(curbuf), IOSIZE - (p - buffer));
- else {
- if (!fullname && curbuf->b_fname != NULL)
+ } else {
+ if (!fullname && curbuf->b_fname != NULL) {
name = curbuf->b_fname;
- else
+ } else {
name = curbuf->b_ffname;
+ }
home_replace(shorthelp ? curbuf : NULL, name, p,
(size_t)(IOSIZE - (p - buffer)), true);
}
@@ -2877,12 +2986,13 @@ fileinfo (
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg));
} else if (p_ru) {
- /* Current line and column are already on the screen -- webb */
- if (curbuf->b_ml.ml_line_count == 1)
+ // Current line and column are already on the screen -- webb
+ if (curbuf->b_ml.ml_line_count == 1) {
vim_snprintf_add((char *)buffer, IOSIZE, _("1 line --%d%%--"), n);
- else
+ } else {
vim_snprintf_add((char *)buffer, IOSIZE, _("%" PRId64 " lines --%d%%--"),
- (int64_t)curbuf->b_ml.ml_line_count, n);
+ (int64_t)curbuf->b_ml.ml_line_count, n);
+ }
} else {
vim_snprintf_add((char *)buffer, IOSIZE,
_("line %" PRId64 " of %" PRId64 " --%d%%-- col "),
@@ -2902,18 +3012,19 @@ fileinfo (
* First call msg_start() to get the message in the right place. */
msg_start();
n = msg_scroll;
- msg_scroll = TRUE;
+ msg_scroll = true;
msg(buffer);
msg_scroll = n;
} else {
- p = msg_trunc_attr(buffer, FALSE, 0);
- if (restart_edit != 0 || (msg_scrolled && !need_wait_return))
- /* Need to repeat the message after redrawing when:
- * - When restart_edit is set (otherwise there will be a delay
- * before redrawing).
- * - When the screen was scrolled but there is no wait-return
- * prompt. */
+ p = msg_trunc_attr(buffer, false, 0);
+ if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) {
+ // Need to repeat the message after redrawing when:
+ // - When restart_edit is set (otherwise there will be a delay
+ // before redrawing).
+ // - When the screen was scrolled but there is no wait-return
+ // prompt.
set_keep_msg(p, 0);
+ }
}
xfree(buffer);
@@ -2921,10 +3032,11 @@ fileinfo (
void col_print(char_u *buf, size_t buflen, int col, int vcol)
{
- if (col == vcol)
+ if (col == vcol) {
vim_snprintf((char *)buf, buflen, "%d", col);
- else
+ } else {
vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
+ }
}
/*
@@ -2945,14 +3057,15 @@ void maketitle(void)
char buf[IOSIZE];
if (!redrawing()) {
- /* Postpone updating the title when 'lazyredraw' is set. */
- need_maketitle = TRUE;
+ // Postpone updating the title when 'lazyredraw' is set.
+ need_maketitle = true;
return;
}
- need_maketitle = FALSE;
- if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL)
+ need_maketitle = false;
+ if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) {
return;
+ }
if (p_title) {
if (p_titlelen > 0) {
@@ -2964,7 +3077,7 @@ void maketitle(void)
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
- int use_sandbox = FALSE;
+ int use_sandbox = false;
int save_called_emsg = called_emsg;
use_sandbox = was_set_insecurely((char_u *)"titlestring", 0);
@@ -3075,11 +3188,11 @@ void maketitle(void)
i_str = (char_u *)buf;
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
- int use_sandbox = FALSE;
+ int use_sandbox = false;
int save_called_emsg = called_emsg;
use_sandbox = was_set_insecurely((char_u *)"iconstring", 0);
- called_emsg = FALSE;
+ called_emsg = false;
build_stl_str_hl(curwin, i_str, sizeof(buf),
p_iconstring, use_sandbox,
0, 0, NULL, NULL);
@@ -3090,17 +3203,19 @@ void maketitle(void)
} else
i_str = p_iconstring;
} else {
- if (buf_spname(curbuf) != NULL)
+ if (buf_spname(curbuf) != NULL) {
i_name = buf_spname(curbuf);
- else /* use file name only in icon */
+ } else { // use file name only in icon
i_name = path_tail(curbuf->b_ffname);
+ }
*i_str = NUL;
- /* Truncate name at 100 bytes. */
+ // Truncate name at 100 bytes.
len = (int)STRLEN(i_name);
if (len > 100) {
len -= 100;
- if (has_mbyte)
+ if (has_mbyte) {
len += (*mb_tail_off)(i_name, i_name + len) + 1;
+ }
i_name += len;
}
STRCPY(i_str, i_name);
@@ -3110,8 +3225,9 @@ void maketitle(void)
mustset |= ti_change(i_str, &lasticon);
- if (mustset)
+ if (mustset) {
resettitle();
+ }
}
/// Used for title and icon: Check if "str" differs from "*last". Set "*last"
@@ -3233,15 +3349,17 @@ int build_stl_str_hl(
// use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') {
usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
- if (usefmt == NULL)
+ if (usefmt == NULL) {
usefmt = fmt;
+ }
}
- if (fillchar == 0)
+ if (fillchar == 0) {
fillchar = ' ';
- // Can't handle a multi-byte fill character yet.
- else if (mb_char2len(fillchar) > 1)
+ } else if (mb_char2len(fillchar) > 1) {
+ // Can't handle a multi-byte fill character yet.
fillchar = '-';
+ }
// Get line & check if empty (cursorpos will show "0-1").
char_u *line_ptr = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false);
@@ -3296,8 +3414,9 @@ int build_stl_str_hl(
// If we have processed the entire format string or run out of
// room in our output buffer, exit the loop.
- if (*fmt_p == NUL || out_p >= out_end_p)
+ if (*fmt_p == NUL || out_p >= out_end_p) {
break;
+ }
// The rest of this loop will handle a single `%` item.
// Note: We increment here to skip over the `%` character we are currently
@@ -3381,7 +3500,7 @@ int build_stl_str_hl(
// { Determine the number of bytes to remove
long n;
if (has_mbyte) {
- /* Find the first character that should be included. */
+ // Find the first character that should be included.
n = 0;
while (group_len >= items[groupitems[groupdepth]].maxwid) {
group_len -= ptr2cells(t + n);
@@ -3468,8 +3587,9 @@ int build_stl_str_hl(
// The first digit group is the item's min width
if (ascii_isdigit(*fmt_p)) {
minwid = getdigits_int(&fmt_p);
- if (minwid < 0) /* overflow */
+ if (minwid < 0) { // overflow
minwid = 0;
+ }
}
// User highlight groups override the min width field
@@ -3553,8 +3673,9 @@ int build_stl_str_hl(
fmt_p++;
if (ascii_isdigit(*fmt_p)) {
maxwid = getdigits_int(&fmt_p);
- if (maxwid <= 0) /* overflow */
+ if (maxwid <= 0) { // overflow
maxwid = 50;
+ }
}
}
@@ -3606,13 +3727,14 @@ int build_stl_str_hl(
home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true);
}
trans_characters(NameBuff, MAXPATHL);
- if (opt != STL_FILENAME)
+ if (opt != STL_FILENAME) {
str = NameBuff;
- else
+ } else {
str = path_tail(NameBuff);
+ }
break;
}
- case STL_VIM_EXPR: /* '{' */
+ case STL_VIM_EXPR: // '{'
{
itemisflag = true;
@@ -3621,8 +3743,9 @@ int build_stl_str_hl(
char_u *t = out_p;
while (*fmt_p != '}' && *fmt_p != NUL && out_p < out_end_p)
*out_p++ = *fmt_p++;
- if (*fmt_p != '}') /* missing '}' or out of space */
+ if (*fmt_p != '}') { // missing '}' or out of space
break;
+ }
fmt_p++;
*out_p = 0;
@@ -3689,7 +3812,7 @@ int build_stl_str_hl(
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
wp->w_p_list = true;
}
- ++virtcol;
+ virtcol++;
// Don't display %V if it's the same as %c.
if (opt == STL_VIRTCOL_ALT
&& (virtcol == (colnr_T)(!(State & INSERT) && empty_line
@@ -3730,8 +3853,9 @@ int build_stl_str_hl(
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN))
+ if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) {
str = tmp;
+ }
break;
case STL_PAGENUM:
num = printer_page_num;
@@ -3758,17 +3882,19 @@ int build_stl_str_hl(
FALLTHROUGH;
case STL_BYTEVAL:
num = byteval;
- if (num == NL)
+ if (num == NL) {
num = 0;
- else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC)
+ } else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) {
num = NL;
+ }
break;
case STL_ROFLAG:
case STL_ROFLAG_ALT:
itemisflag = true;
- if (wp->w_buffer->b_p_ro)
+ if (wp->w_buffer->b_p_ro) {
str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"));
+ }
break;
case STL_HELPFLAG:
@@ -3844,7 +3970,7 @@ int build_stl_str_hl(
// { The name of the highlight is surrounded by `#`
char_u *t = fmt_p;
while (*fmt_p != '#' && *fmt_p != NUL) {
- ++fmt_p;
+ fmt_p++;
}
// }
@@ -3899,8 +4025,9 @@ int build_stl_str_hl(
}
// Early out if there isn't enough room for the truncation marker
- if (out_p >= out_end_p)
+ if (out_p >= out_end_p) {
break;
+ }
// Add the truncation marker
*out_p++ = '<';
@@ -3911,10 +4038,11 @@ int build_stl_str_hl(
if (minwid > 0) {
for (; l < minwid && out_p < out_end_p; l++) {
// Don't put a "-" in front of a digit.
- if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t))
+ if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
*out_p++ = ' ';
- else
+ } else {
*out_p++ = fillchar;
+ }
}
minwid = 0;
} else {
@@ -3941,8 +4069,9 @@ int build_stl_str_hl(
// Otherwise if the item is a number, copy that to the output buffer.
} else if (num >= 0) {
- if (out_p + 20 > out_end_p)
- break; /* not sufficient space */
+ if (out_p + 20 > out_end_p) {
+ break; // not sufficient space
+ }
prevchar_isitem = true;
// { Build the formatting string
@@ -4028,8 +4157,9 @@ int build_stl_str_hl(
xfree(str);
}
- if (num >= 0 || (!itemisflag && str && *str))
- prevchar_isflag = false; /* Item not NULL, but not a flag */
+ if (num >= 0 || (!itemisflag && str && *str)) {
+ prevchar_isflag = false; // Item not NULL, but not a flag
+ }
// Item processed, move to the next
curitem++;
@@ -4082,8 +4212,9 @@ int build_stl_str_hl(
width = 0;
for (;; ) {
width += ptr2cells(trunc_p);
- if (width >= maxwidth)
+ if (width >= maxwidth) {
break;
+ }
// Note: Only advance the pointer if the next
// character will fit in the available output space
@@ -4287,8 +4418,8 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
return;
}
- long above; /* number of lines above window */
- long below; /* number of lines below window */
+ long above; // number of lines above window
+ long below; // number of lines below window
above = wp->w_topline - 1;
above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -4298,14 +4429,15 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
above = 0;
}
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
- if (below <= 0)
+ if (below <= 0) {
STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen);
- else if (above <= 0)
+ } else if (above <= 0) {
STRLCPY(buf, _("Top"), buflen);
- else
+ } else {
vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L
- ? (int)(above / ((above + below) / 100L))
- : (int)(above * 100L / (above + below)));
+ ? (int)(above / ((above + below) / 100L))
+ : (int)(above * 100L / (above + below)));
+ }
}
/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
@@ -4350,11 +4482,13 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
*/
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) { // if no file name given, nothing to do
return;
- if (*sfname == NULL) /* if no short file name given, use ffname */
+ }
+ if (*sfname == NULL) { // if no short file name given, use ffname
*sfname = *ffname;
- *ffname = (char_u *)fix_fname((char *)*ffname); /* expand to full path */
+ }
+ *ffname = (char_u *)fix_fname((char *)*ffname); // expand to full path
#ifdef WIN32
if (!buf->b_p_bin) {
@@ -4376,35 +4510,36 @@ char_u *alist_name(aentry_T *aep)
{
buf_T *bp;
- /* Use the name from the associated buffer if it exists. */
+ // Use the name from the associated buffer if it exists.
bp = buflist_findnr(aep->ae_fnum);
- if (bp == NULL || bp->b_fname == NULL)
+ if (bp == NULL || bp->b_fname == NULL) {
return aep->ae_fname;
+ }
return bp->b_fname;
}
/*
* do_arg_all(): Open up to 'count' windows, one for each argument.
*/
-void
-do_arg_all (
+void
+do_arg_all(
int count,
- int forceit, /* hide buffers in current windows */
- int keep_tabs /* keep current tabs, for ":tab drop file" */
+ int forceit, // hide buffers in current windows
+ 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
- * 2: opened in curtab
- * 3: opened in curtab and curwin
- */
- int opened_len; /* length of opened[] */
- int use_firstwin = FALSE; /* use first window for arglist */
+ char_u *opened; // Array of weight for which args are open:
+ // 0: not opened
+ // 1: opened in other tab
+ // 2: opened in curtab
+ // 3: opened in curtab and curwin
+
+ int opened_len; // length of opened[]
+ int use_firstwin = false; // use first window for arglist
int split_ret = OK;
bool p_ea_save;
- alist_T *alist; /* argument list to be used */
+ alist_T *alist; // argument list to be used
buf_T *buf;
tabpage_T *tpnext;
int had_tab = cmdmod.tab;
@@ -4429,7 +4564,7 @@ do_arg_all (
* freed while we are working here by "locking" it. We still have to
* watch out for its size to be changed. */
alist = curwin->w_alist;
- ++alist->al_refcount;
+ alist->al_refcount++;
old_curwin = curwin;
old_curtab = curtab;
@@ -4442,8 +4577,9 @@ do_arg_all (
* Windows that have a changed buffer and can't be hidden won't be closed.
* When the ":tab" modifier was used do this for all tab pages.
*/
- if (had_tab > 0)
- goto_tabpage_tp(first_tabpage, TRUE, TRUE);
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
for (;; ) {
win_T *wpnext = NULL;
tpnext = curtab->tp_next;
@@ -4455,7 +4591,7 @@ do_arg_all (
i = opened_len;
} else {
// check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; ++i) {
+ for (i = 0; i < opened_len; i++) {
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
@@ -4463,28 +4599,31 @@ do_arg_all (
int weight = 1;
if (old_curtab == curtab) {
- ++weight;
- if (old_curwin == wp)
- ++weight;
+ weight++;
+ if (old_curwin == wp) {
+ weight++;
+ }
}
if (weight > (int)opened[i]) {
opened[i] = (char_u)weight;
if (i == 0) {
- if (new_curwin != NULL)
+ if (new_curwin != NULL) {
new_curwin->w_arg_idx = opened_len;
+ }
new_curwin = wp;
new_curtab = curtab;
}
- } else if (keep_tabs)
+ } else if (keep_tabs) {
i = opened_len;
+ }
if (wp->w_alist != alist) {
/* Use the current argument list for all windows
* containing a file from it. */
alist_unlink(wp->w_alist);
wp->w_alist = alist;
- ++wp->w_alist->al_refcount;
+ wp->w_alist->al_refcount++;
}
break;
}
@@ -4516,33 +4655,36 @@ do_arg_all (
// check if autocommands removed the next window
if (!win_valid(wpnext)) {
// start all over...
- wpnext = firstwin;
+ wpnext = firstwin;
}
}
}
}
}
- /* Without the ":tab" modifier only do the current tab page. */
- if (had_tab == 0 || tpnext == NULL)
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
break;
+ }
- /* check if autocommands removed the next tab page */
- if (!valid_tabpage(tpnext))
- tpnext = first_tabpage; /* start all over...*/
- goto_tabpage_tp(tpnext, TRUE, TRUE);
+ // check if autocommands removed the next tab page
+ if (!valid_tabpage(tpnext)) {
+ tpnext = first_tabpage; // start all over...
+ }
+ goto_tabpage_tp(tpnext, true, true);
}
/*
* Open a window for files in the argument list that don't have one.
* ARGCOUNT may change while doing this, because of autocommands.
*/
- if (count > opened_len || count <= 0)
+ if (count > opened_len || count <= 0) {
count = opened_len;
+ }
- /* Don't execute Win/Buf Enter/Leave autocommands here. */
- ++autocmd_no_enter;
- ++autocmd_no_leave;
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
+ autocmd_no_leave++;
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
@@ -4558,7 +4700,7 @@ do_arg_all (
arg_had_last = true;
}
if (opened[i] > 0) {
- /* Move the already present window to below the current window */
+ // Move the already present window to below the current window
if (curwin->w_arg_idx != i) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_arg_idx == i) {
@@ -4573,15 +4715,17 @@ do_arg_all (
}
}
} else if (split_ret == OK) {
- if (!use_firstwin) { /* split current window */
+ if (!use_firstwin) { // split current window
p_ea_save = p_ea;
- p_ea = true; /* use space from all windows */
+ p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
p_ea = p_ea_save;
- if (split_ret == FAIL)
+ if (split_ret == FAIL) {
continue;
- } else /* first window: do autocmd for leaving this buffer */
- --autocmd_no_leave;
+ }
+ } else { // first window: do autocmd for leaving this buffer
+ autocmd_no_leave--;
+ }
/*
* edit file "i"
@@ -4603,12 +4747,13 @@ do_arg_all (
}
os_breakcheck();
- /* When ":tab" was used open a new tab for a new window repeatedly. */
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.tab = 9999;
+ }
}
- /* Remove the "lock" on the argument list. */
+ // Remove the "lock" on the argument list.
alist_unlink(alist);
autocmd_no_enter--;
@@ -4629,7 +4774,7 @@ do_arg_all (
win_enter(new_curwin, false);
}
- --autocmd_no_leave;
+ autocmd_no_leave--;
xfree(opened);
}
@@ -4645,18 +4790,20 @@ void ex_buffer_all(exarg_T *eap)
int open_wins = 0;
int r;
long count; // Maximum number of windows to open.
- int all; // When TRUE also load inactive buffers.
+ int all; // When true also load inactive buffers.
int had_tab = cmdmod.tab;
tabpage_T *tpnext;
- if (eap->addr_count == 0) /* make as many windows as possible */
+ if (eap->addr_count == 0) { // make as many windows as possible
count = 9999;
- else
- count = eap->line2; /* make as many windows as specified */
- if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide)
- all = FALSE;
- else
- all = TRUE;
+ } else {
+ count = eap->line2; // make as many windows as specified
+ }
+ if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide) {
+ all = false;
+ } else {
+ all = true;
+ }
setpcmark();
@@ -4665,8 +4812,9 @@ void ex_buffer_all(exarg_T *eap)
* Close superfluous windows (two windows for the same buffer).
* Also close windows that are not full-width.
*/
- if (had_tab > 0)
- goto_tabpage_tp(first_tabpage, TRUE, TRUE);
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
for (;; ) {
tpnext = curtab->tp_next;
for (wp = firstwin; wp != NULL; wp = wpnext) {
@@ -4685,44 +4833,51 @@ void ex_buffer_all(exarg_T *eap)
// something strange with windows
tpnext = first_tabpage; // start all over...
open_wins = 0;
- } else
- ++open_wins;
+ } else {
+ open_wins++;
+ }
}
- /* Without the ":tab" modifier only do the current tab page. */
- if (had_tab == 0 || tpnext == NULL)
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
break;
- goto_tabpage_tp(tpnext, TRUE, TRUE);
+ }
+ goto_tabpage_tp(tpnext, true, true);
}
- /*
- * Go through the buffer list. When a buffer doesn't have a window yet,
- * open one. Otherwise move the window to the right position.
- * Watch out for autocommands that delete buffers or windows!
- */
- /* Don't execute Win/Buf Enter/Leave autocommands here. */
- ++autocmd_no_enter;
+ //
+ // Go through the buffer list. When a buffer doesn't have a window yet,
+ // open one. Otherwise move the window to the right position.
+ // Watch out for autocommands that delete buffers or windows!
+ //
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
win_enter(lastwin, false);
- ++autocmd_no_leave;
+ autocmd_no_leave++;
for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) {
- /* Check if this buffer needs a window */
- if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl)
+ // Check if this buffer needs a window
+ if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) {
continue;
+ }
if (had_tab != 0) {
- /* With the ":tab" modifier don't move the window. */
- if (buf->b_nwindows > 0)
- wp = lastwin; /* buffer has a window, skip it */
- else
+ // With the ":tab" modifier don't move the window.
+ if (buf->b_nwindows > 0) {
+ wp = lastwin; // buffer has a window, skip it
+ } else {
wp = NULL;
+ }
} else {
- /* Check if this buffer already has a window */
- for (wp = firstwin; wp != NULL; wp = wp->w_next)
- if (wp->w_buffer == buf)
+ // Check if this buffer already has a window
+ for (wp = firstwin; wp != NULL; wp = wp->w_next) {
+ if (wp->w_buffer == buf) {
break;
- /* If the buffer already has a window, move it */
- if (wp != NULL)
+ }
+ }
+ // If the buffer already has a window, move it
+ if (wp != NULL) {
win_move_after(wp, curwin);
+ }
}
if (wp == NULL && split_ret == OK) {
@@ -4730,14 +4885,15 @@ void ex_buffer_all(exarg_T *eap)
set_bufref(&bufref, buf);
// Split the window and put the buffer in it.
p_ea_save = p_ea;
- p_ea = true; /* use space from all windows */
+ p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
- ++open_wins;
+ open_wins++;
p_ea = p_ea_save;
- if (split_ret == FAIL)
+ if (split_ret == FAIL) {
continue;
+ }
- /* Open the buffer in this window. */
+ // Open the buffer in this window.
swap_exists_action = SEA_DIALOG;
set_curbuf(buf, DOBUF_GOTO);
if (!bufref_valid(&bufref)) {
@@ -4748,15 +4904,15 @@ void ex_buffer_all(exarg_T *eap)
if (swap_exists_action == SEA_QUIT) {
cleanup_T cs;
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a window. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a window.
enter_cleanup(&cs);
// User selected Quit at ATTENTION prompt; close this window.
win_close(curwin, true);
open_wins--;
swap_exists_action = SEA_NONE;
- swap_exists_did_quit = TRUE;
+ swap_exists_did_quit = true;
/* Restore the error/interrupt/exception state if not
* discarded by a new aborting error, interrupt, or uncaught
@@ -4768,19 +4924,21 @@ void ex_buffer_all(exarg_T *eap)
os_breakcheck();
if (got_int) {
- (void)vgetc(); /* only break the file loading, not the rest */
+ (void)vgetc(); // only break the file loading, not the rest
break;
}
- /* Autocommands deleted the buffer or aborted script processing!!! */
- if (aborting())
+ // Autocommands deleted the buffer or aborted script processing!!!
+ if (aborting()) {
break;
- /* When ":tab" was used open a new tab for a new window repeatedly. */
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
+ }
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.tab = 9999;
+ }
}
- --autocmd_no_enter;
- win_enter(firstwin, false); /* back to first window */
- --autocmd_no_leave;
+ autocmd_no_enter--;
+ win_enter(firstwin, false); // back to first window
+ autocmd_no_leave--;
/*
* Close superfluous windows.
@@ -4789,7 +4947,7 @@ void ex_buffer_all(exarg_T *eap)
r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
|| autowrite(wp->w_buffer, false) == OK);
if (!win_valid(wp)) {
- /* BufWrite Autocommands made the window invalid, start over */
+ // BufWrite Autocommands made the window invalid, start over
wp = lastwin;
} else if (r) {
win_close(wp, !buf_hide(wp->w_buffer));
@@ -4797,8 +4955,9 @@ void ex_buffer_all(exarg_T *eap)
wp = lastwin;
} else {
wp = wp->w_prev;
- if (wp == NULL)
+ if (wp == NULL) {
break;
+ }
}
}
}
@@ -4820,40 +4979,46 @@ void do_modelines(int flags)
int nmlines;
static int entered = 0;
- if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
+ if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0) {
return;
+ }
/* Disallow recursive entry here. Can happen when executing a modeline
* triggers an autocommand, which reloads modelines with a ":do". */
- if (entered)
+ if (entered) {
return;
+ }
- ++entered;
+ entered++;
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
- ++lnum)
- if (chk_modeline(lnum, flags) == FAIL)
+ lnum++) {
+ if (chk_modeline(lnum, flags) == FAIL) {
nmlines = 0;
+ }
+ }
for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines
- && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
- if (chk_modeline(lnum, flags) == FAIL)
+ && lnum > curbuf->b_ml.ml_line_count - nmlines; lnum--) {
+ if (chk_modeline(lnum, flags) == FAIL) {
nmlines = 0;
- --entered;
+ }
+ }
+ entered--;
}
/*
* chk_modeline() - check a single line for a mode string
* Return FAIL if an error encountered.
*/
-static int
-chk_modeline (
+static int
+chk_modeline(
linenr_T lnum,
- int flags /* Same as for do_modelines(). */
+ int flags // Same as for do_modelines().
)
{
char_u *s;
char_u *e;
- char_u *linecopy; /* local copy of any modeline found */
+ char_u *linecopy; // local copy of any modeline found
int prev;
intmax_t vers;
int end;
@@ -4863,17 +5028,18 @@ chk_modeline (
scid_T save_SID;
prev = -1;
- for (s = ml_get(lnum); *s != NUL; ++s) {
+ for (s = ml_get(lnum); *s != NUL; s++) {
if (prev == -1 || ascii_isspace(prev)) {
if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
|| STRNCMP(s, "vi:", (size_t)3) == 0)
break;
- /* Accept both "vim" and "Vim". */
+ // Accept both "vim" and "Vim".
if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm') {
- if (s[3] == '<' || s[3] == '=' || s[3] == '>')
+ if (s[3] == '<' || s[3] == '=' || s[3] == '>') {
e = s + 4;
- else
+ } else {
e = s + 3;
+ }
if (getdigits_safe(&e, &vers) != OK) {
continue;
}
@@ -4897,32 +5063,36 @@ chk_modeline (
return retval;
}
- do /* skip over "ex:", "vi:" or "vim:" */
- ++s;
- while (s[-1] != ':');
+ do { // skip over "ex:", "vi:" or "vim:"
+ s++;
+ } while (s[-1] != ':');
- s = linecopy = vim_strsave(s); /* copy the line, it will change */
+ s = linecopy = vim_strsave(s); // copy the line, it will change
save_sourcing_lnum = sourcing_lnum;
save_sourcing_name = sourcing_name;
- sourcing_lnum = lnum; /* prepare for emsg() */
+ sourcing_lnum = lnum; // prepare for emsg()
sourcing_name = (char_u *)"modelines";
- end = FALSE;
- while (end == FALSE) {
+ end = false;
+ while (end == false) {
s = skipwhite(s);
- if (*s == NUL)
+ if (*s == NUL) {
break;
+ }
/*
* Find end of set command: ':' or end of line.
* Skip over "\:", replacing it with ":".
*/
- for (e = s; *e != ':' && *e != NUL; ++e)
- if (e[0] == '\\' && e[1] == ':')
+ for (e = s; *e != ':' && *e != NUL; e++) {
+ if (e[0] == '\\' && e[1] == ':') {
STRMOVE(e, e + 1);
- if (*e == NUL)
- end = TRUE;
+ }
+ }
+ if (*e == NUL) {
+ end = true;
+ }
/*
* If there is a "set" command, require a terminating ':' and
@@ -4933,22 +5103,30 @@ chk_modeline (
*/
if (STRNCMP(s, "set ", (size_t)4) == 0
|| STRNCMP(s, "se ", (size_t)3) == 0) {
- if (*e != ':') /* no terminating ':'? */
+ if (*e != ':') { // no terminating ':'?
break;
- end = TRUE;
+ }
+ end = true;
s = vim_strchr(s, ' ') + 1;
}
- *e = NUL; /* truncate the set command */
+ *e = NUL; // truncate the set command
- if (*s != NUL) { /* skip over an empty "::" */
+ if (*s != NUL) { // skip over an empty "::"
+ const int secure_save = secure;
save_SID = current_SID;
current_SID = SID_MODELINE;
+ // Make sure no risky things are executed as a side effect.
+ secure = 1;
+
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
+
+ secure = secure_save;
current_SID = save_SID;
- if (retval == FAIL) /* stop if error found */
+ if (retval == FAIL) { // stop if error found
break;
+ }
}
- s = e + 1; /* advance to next part */
+ s = e + 1; // advance to next part
}
sourcing_lnum = save_sourcing_lnum;
@@ -4965,6 +5143,55 @@ bool bt_help(const buf_T *const buf)
return buf != NULL && buf->b_help;
}
+// Return true if "buf" is the quickfix buffer.
+bool bt_quickfix(const buf_T *const buf)
+{
+ return buf != NULL && buf->b_p_bt[0] == 'q';
+}
+
+// Return true if "buf" is a terminal buffer.
+bool bt_terminal(const buf_T *const buf)
+{
+ return buf != NULL && buf->b_p_bt[0] == 't';
+}
+
+// Return true if "buf" is a "nofile", "acwrite" or "terminal" buffer.
+// This means the buffer name is not a file name.
+bool bt_nofile(const buf_T *const buf)
+{
+ return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
+ || buf->b_p_bt[0] == 'a' || buf->terminal);
+}
+
+// Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer.
+bool bt_dontwrite(const buf_T *const buf)
+{
+ return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal);
+}
+
+bool bt_dontwrite_msg(const buf_T *const buf)
+{
+ if (bt_dontwrite(buf)) {
+ EMSG(_("E382: Cannot write, 'buftype' option is set"));
+ return true;
+ }
+ return false;
+}
+
+// Return true if the buffer should be hidden, according to 'hidden', ":hide"
+// and 'bufhidden'.
+bool buf_hide(const buf_T *const buf)
+{
+ // 'bufhidden' overrules 'hidden' and ":hide", check it first
+ switch (buf->b_p_bh[0]) {
+ case 'u': // "unload"
+ case 'w': // "wipe"
+ case 'd': return false; // "delete"
+ case 'h': return true; // "hide"
+ }
+ return p_hid || cmdmod.hide;
+}
+
/*
* Return special buffer name.
* Returns NULL when the buffer has a normal file name.
@@ -4979,20 +5206,23 @@ char_u *buf_spname(buf_T *buf)
* For location list window, w_llist_ref points to the location list.
* For quickfix window, w_llist_ref is NULL.
*/
- if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL)
+ if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL) {
return (char_u *)_(msg_loclist);
- else
+ } else {
return (char_u *)_(msg_qflist);
+ }
}
- /* There is no _file_ when 'buftype' is "nofile", b_sfname
- * contains the name as specified by the user */
+ // There is no _file_ when 'buftype' is "nofile", b_sfname
+ // contains the name as specified by the user.
if (bt_nofile(buf)) {
- if (buf->b_sfname != NULL)
+ if (buf->b_sfname != NULL) {
return buf->b_sfname;
+ }
return (char_u *)_("[Scratch]");
}
- if (buf->b_fname == NULL)
+ if (buf->b_fname == NULL) {
return (char_u *)_("[No Name]");
+ }
return NULL;
}
@@ -5024,19 +5254,24 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
* Insert the sign into the signlist.
*/
static void insert_sign(
- buf_T *buf, /* buffer to store sign in */
- signlist_T *prev, /* previous sign entry */
- signlist_T *next, /* next sign entry */
- int id, /* sign ID */
- 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
+ signlist_T *next, // next sign entry
+ int id, // sign ID
+ linenr_T lnum, // line number which gets the mark
+ int typenr // typenr of sign we are adding
+)
{
signlist_T *newsign = xmalloc(sizeof(signlist_T));
newsign->id = id;
newsign->lnum = lnum;
newsign->typenr = typenr;
newsign->next = next;
+ newsign->prev = prev;
+ if (next != NULL) {
+ next->prev = newsign;
+ }
+ buf->b_signcols_max = -1;
if (prev == NULL) {
/* When adding first sign need to redraw the windows to create the
@@ -5046,7 +5281,7 @@ static void insert_sign(
changed_cline_bef_curs();
}
- /* first sign in signlist */
+ // first sign in signlist
buf->b_signlist = newsign;
}
else {
@@ -5054,18 +5289,109 @@ static void insert_sign(
}
}
+static int sign_compare(const void *a1, const void *a2)
+{
+ const signlist_T *s1 = *(const signlist_T **)a1;
+ const signlist_T *s2 = *(const signlist_T **)a2;
+
+ // Sort by line number and the by id
+
+ if (s1->lnum > s2->lnum) {
+ return 1;
+ }
+ if (s1->lnum < s2->lnum) {
+ return -1;
+ }
+ if (s1->id > s2->id) {
+ return 1;
+ }
+ if (s1->id < s2->id) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int buf_signcols(buf_T *buf)
+{
+ if (buf->b_signcols_max == -1) {
+ signlist_T *sign; // a sign in the signlist
+ signlist_T **signs_array;
+ signlist_T **prev_sign;
+ int nr_signs = 0, i = 0, same;
+
+ // Count the number of signs
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ nr_signs++;
+ }
+
+ // Make an array of all the signs
+ signs_array = xcalloc((size_t)nr_signs, sizeof(*sign));
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ signs_array[i] = sign;
+ i++;
+ }
+
+ // Sort the array
+ qsort(signs_array, (size_t)nr_signs, sizeof(signlist_T *),
+ sign_compare);
+
+ // Find the maximum amount of signs existing in a single line
+ buf->b_signcols_max = 0;
+
+ same = 1;
+ for (i = 1; i < nr_signs; i++) {
+ if (signs_array[i - 1]->lnum != signs_array[i]->lnum) {
+ if (buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+ same = 1;
+ } else {
+ same++;
+ }
+ }
+
+ if (nr_signs > 0 && buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+
+ // Recreate the linked list with the sorted order of the array
+ buf->b_signlist = NULL;
+ prev_sign = &buf->b_signlist;
+
+ for (i = 0; i < nr_signs; i++) {
+ sign = signs_array[i];
+ sign->next = NULL;
+ *prev_sign = sign;
+
+ prev_sign = &sign->next;
+ }
+
+ xfree(signs_array);
+
+ // Check if we need to redraw
+ if (buf->b_signcols_max != buf->b_signcols) {
+ buf->b_signcols = buf->b_signcols_max;
+ redraw_buf_later(buf, NOT_VALID);
+ }
+ }
+
+ return buf->b_signcols;
+}
+
/*
* 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 */
- 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
+ int id, // sign ID
+ linenr_T lnum, // line number which gets the mark
+ int typenr // typenr of sign we are adding
+)
{
- signlist_T *sign; /* a sign in the signlist */
- signlist_T *prev; /* the previous sign */
+ signlist_T **lastp; // pointer to pointer to current sign
+ signlist_T *sign; // a sign in the signlist
+ signlist_T *prev; // the previous sign
prev = NULL;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
@@ -5073,26 +5399,57 @@ void buf_addsign(
sign->typenr = typenr;
return;
} else if ((lnum == sign->lnum && id != sign->id)
- || (id < 0 && lnum < sign->lnum)) { // attempt to keep signs sorted by lnum
- insert_sign(buf, prev, sign, id, lnum, typenr);
- return;
+ || (id < 0 && lnum < sign->lnum)) {
+ // keep signs sorted by lnum: insert new sign at head of list for
+ // this lnum
+ while (prev != NULL && prev->lnum == lnum) {
+ prev = prev->prev;
+ }
+ if (prev == NULL) {
+ sign = buf->b_signlist;
+ } else {
+ sign = prev->next;
+ }
+ insert_sign(buf, prev, sign, id, lnum, typenr);
+ return;
}
prev = sign;
}
+
+ // insert new sign at head of list for this lnum
+ while (prev != NULL && prev->lnum == lnum) {
+ prev = prev->prev;
+ }
+ if (prev == NULL) {
+ sign = buf->b_signlist;
+ } else {
+ sign = prev->next;
+ }
insert_sign(buf, prev, sign, id, lnum, typenr);
- return;
+ // Having more than one sign with _the same type_ and on the _same line_ is
+ // unwanted, let's prevent it.
+
+ lastp = &buf->b_signlist;
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) {
+ *lastp = sign->next;
+ xfree(sign);
+ } else {
+ lastp = &sign->next;
+ }
+ }
}
// For an existing, placed sign "markId" change the type to "typenr".
// Returns the line number of the sign, or zero if the sign is not found.
linenr_T buf_change_sign_type(
- buf_T *buf, /* buffer to store sign in */
- int markId, /* sign ID */
- int typenr /* typenr of sign we are adding */
- )
+ buf_T *buf, // buffer to store sign in
+ int markId, // sign ID
+ int typenr // typenr of sign we are adding
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->id == markId) {
@@ -5104,16 +5461,23 @@ linenr_T buf_change_sign_type(
return (linenr_T)0;
}
+
/// Gets a sign from a given line.
-/// In case of multiple signs, returns the most recently placed one.
///
/// @param buf Buffer in which to search
/// @param lnum Line in which to search
/// @param type Type of sign to look for
-/// @return Identifier of the first matching sign, or 0
-int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
+/// @param idx if there multiple signs, this index will pick the n-th
+// out of the most `max_signs` sorted ascending by Id.
+/// @param max_signs the number of signs, with priority for the ones
+// with the highest Ids.
+/// @return Identifier of the matching sign, or 0
+int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type,
+ int idx, int max_signs)
{
signlist_T *sign; // a sign in a b_signlist
+ signlist_T *matches[9];
+ int nr_matches = 0;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum
@@ -5124,29 +5488,50 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
&& sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
|| (type == SIGN_NUMHL
&& sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
- return sign->typenr;
+ matches[nr_matches] = sign;
+ nr_matches++;
+
+ if (nr_matches == ARRAY_SIZE(matches)) {
+ break;
+ }
}
}
+
+ if (nr_matches > 0) {
+ if (nr_matches > max_signs) {
+ idx += nr_matches - max_signs;
+ }
+
+ if (idx >= nr_matches) {
+ return 0;
+ }
+
+ return matches[idx]->typenr;
+ }
+
return 0;
}
-
linenr_T buf_delsign(
- buf_T *buf, /* buffer sign is stored in */
- int id /* sign id */
- )
+ buf_T *buf, // buffer sign is stored in
+ int id // sign id
+)
{
- signlist_T **lastp; /* pointer to pointer to current sign */
- signlist_T *sign; /* a sign in a b_signlist */
- signlist_T *next; /* the next sign in a b_signlist */
- linenr_T lnum; /* line number whose sign was deleted */
+ signlist_T **lastp; // pointer to pointer to current sign
+ signlist_T *sign; // a sign in a b_signlist
+ signlist_T *next; // the next sign in a b_signlist
+ linenr_T lnum; // line number whose sign was deleted
+ buf->b_signcols_max = -1;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
next = sign->next;
if (sign->id == id) {
*lastp = next;
+ if (next != NULL) {
+ next->prev = sign->prev;
+ }
lnum = sign->lnum;
xfree(sign);
break;
@@ -5172,11 +5557,11 @@ linenr_T buf_delsign(
* get loaded.
*/
int buf_findsign(
- buf_T *buf, /* buffer to store sign in */
- int id /* sign ID */
- )
+ buf_T *buf, // buffer to store sign in
+ int id // sign ID
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->id == id) {
@@ -5188,11 +5573,11 @@ int buf_findsign(
}
int buf_findsign_id(
- buf_T *buf, /* buffer whose sign we are searching for */
- linenr_T lnum /* line number of sign */
- )
+ buf_T *buf, // buffer whose sign we are searching for
+ linenr_T lnum // line number of sign
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum) {
@@ -5223,6 +5608,7 @@ void buf_delete_signs(buf_T *buf)
xfree(buf->b_signlist);
buf->b_signlist = next;
}
+ buf->b_signcols_max = -1;
}
/*
@@ -5277,18 +5663,27 @@ void sign_list_placed(buf_T *rbuf)
*/
void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
{
- signlist_T *sign; /* a sign in a b_signlist */
+ signlist_T *sign; // a sign in a b_signlist
+ signlist_T *next; // the next sign in a b_signlist
+ signlist_T **lastp; // pointer to pointer to current sign
+
+ curbuf->b_signcols_max = -1;
+ lastp = &curbuf->b_signlist;
- for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) {
+ for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
+ next = sign->next;
if (sign->lnum >= line1 && sign->lnum <= line2) {
if (amount == MAXLNUM) {
- sign->lnum = line1;
+ *lastp = next;
+ xfree(sign);
+ continue;
} else {
sign->lnum += amount;
}
- }
- else if (sign->lnum > line2)
+ } else if (sign->lnum > line2) {
sign->lnum += amount_after;
+ }
+ lastp = &sign->next;
}
}
@@ -5642,10 +6037,11 @@ void set_buflisted(int on)
{
if (on != curbuf->b_p_bl) {
curbuf->b_p_bl = on;
- if (on)
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
- else
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
+ if (on) {
+ apply_autocmds(EVENT_BUFADD, NULL, NULL, false, curbuf);
+ } else {
+ apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
+ }
}
}
@@ -5681,7 +6077,7 @@ bool buf_contents_changed(buf_T *buf)
// compare the two files line by line
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
differ = false;
- for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
differ = true;
break;
@@ -5706,10 +6102,10 @@ bool buf_contents_changed(buf_T *buf)
* this buffer. Call this to wipe out a temp buffer that does not contain any
* marks.
*/
-void
-wipe_buffer (
+void
+wipe_buffer(
buf_T *buf,
- int aucmd /* When TRUE trigger autocommands. */
+ int aucmd // When true trigger autocommands.
)
{
if (!aucmd) {
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 64c906fc96..ee3fda5f6d 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -74,6 +74,8 @@ static inline void buf_set_changedtick(buf_T *const buf,
static inline void buf_set_changedtick(buf_T *const buf,
const varnumber_T changedtick)
{
+ typval_T old_val = buf->changedtick_di.di_tv;
+
#ifndef NDEBUG
dictitem_T *const changedtick_di = tv_dict_find(
buf->b_vars, S_LEN("changedtick"));
@@ -87,6 +89,13 @@ static inline void buf_set_changedtick(buf_T *const buf,
assert(changedtick_di == (dictitem_T *)&buf->changedtick_di);
#endif
buf->changedtick_di.di_tv.vval.v_number = changedtick;
+
+ if (tv_dict_is_watched(buf->b_vars)) {
+ tv_dict_watcher_notify(buf->b_vars,
+ (char *)buf->changedtick_di.di_key,
+ &buf->changedtick_di.di_tv,
+ &old_val);
+ }
}
static inline varnumber_T buf_get_changedtick(const buf_T *const buf)
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 48cef9b1e7..5e28a7b513 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -388,24 +388,25 @@ typedef struct {
* a window may have its own instance.
*/
typedef struct {
- hashtab_T b_keywtab; /* syntax keywords hash table */
- hashtab_T b_keywtab_ic; /* idem, ignore case */
- int b_syn_error; /* TRUE when error occurred in HL */
- int b_syn_ic; /* ignore case for :syn cmds */
- int b_syn_spell; /* SYNSPL_ values */
- garray_T b_syn_patterns; /* table for syntax patterns */
- garray_T b_syn_clusters; /* table for syntax clusters */
- int b_spell_cluster_id; /* @Spell cluster ID or 0 */
- int b_nospell_cluster_id; /* @NoSpell cluster ID or 0 */
- int b_syn_containedin; /* TRUE when there is an item with a
- "containedin" argument */
- int b_syn_sync_flags; /* flags about how to sync */
- short b_syn_sync_id; /* group to sync on */
- long b_syn_sync_minlines; /* minimal sync lines offset */
- long b_syn_sync_maxlines; /* maximal sync lines offset */
- long b_syn_sync_linebreaks; /* offset for multi-line pattern */
- char_u *b_syn_linecont_pat; /* line continuation pattern */
- regprog_T *b_syn_linecont_prog; /* line continuation program */
+ hashtab_T b_keywtab; // syntax keywords hash table
+ hashtab_T b_keywtab_ic; // idem, ignore case
+ int b_syn_error; // TRUE when error occurred in HL
+ bool b_syn_slow; // true when 'redrawtime' reached
+ int b_syn_ic; // ignore case for :syn cmds
+ int b_syn_spell; // SYNSPL_ values
+ garray_T b_syn_patterns; // table for syntax patterns
+ garray_T b_syn_clusters; // table for syntax clusters
+ int b_spell_cluster_id; // @Spell cluster ID or 0
+ int b_nospell_cluster_id; // @NoSpell cluster ID or 0
+ int b_syn_containedin; // TRUE when there is an item with a
+ // "containedin" argument
+ int b_syn_sync_flags; // flags about how to sync
+ int16_t b_syn_sync_id; // group to sync on
+ long b_syn_sync_minlines; // minimal sync lines offset
+ long b_syn_sync_maxlines; // maximal sync lines offset
+ long b_syn_sync_linebreaks; // offset for multi-line pattern
+ char_u *b_syn_linecont_pat; // line continuation pattern
+ regprog_T *b_syn_linecont_prog; // line continuation program
syn_time_T b_syn_linecont_time;
int b_syn_linecont_ic; /* ignore-case flag for above */
int b_syn_topgrp; /* for ":syntax include" */
@@ -778,7 +779,9 @@ struct file_buffer {
* normally points to this, but some windows
* may use a different synblock_T. */
- signlist_T *b_signlist; /* list of signs to draw */
+ signlist_T *b_signlist; // list of signs to draw
+ int b_signcols_max; // cached maximum number of sign columns
+ int b_signcols; // last calculated number of sign columns
Terminal *terminal; // Terminal instance associated with the buffer
@@ -958,24 +961,32 @@ struct matchitem {
int conceal_char; ///< cchar for Conceal highlighting
};
-typedef enum {
- kFloatAnchorEast = 1,
- kFloatAnchorSouth = 2,
+typedef int FloatAnchor;
+typedef int FloatRelative;
- kFloatAnchorNW = 0,
- kFloatAnchorNE = 1,
- kFloatAnchorSW = 2,
- kFloatAnchorSE = 3,
-} FloatAnchor;
+enum {
+ kFloatAnchorEast = 1,
+ kFloatAnchorSouth = 2,
+};
+
+// NW -> 0
+// NE -> kFloatAnchorEast
+// SW -> kFloatAnchorSouth
+// SE -> kFloatAnchorSouth | kFloatAnchorEast
+EXTERN const char *const float_anchor_str[] INIT(= { "NW", "NE", "SW", "SE" });
+
+enum {
+ kFloatRelativeEditor = 0,
+ kFloatRelativeWindow = 1,
+ kFloatRelativeCursor = 2,
+};
-typedef enum {
- kFloatRelativeEditor = 0,
- kFloatRelativeWindow = 1,
- kFloatRelativeCursor = 2,
-} FloatRelative;
+EXTERN const char *const float_relative_str[] INIT(= { "editor", "window",
+ "cursor" });
typedef struct {
Window window;
+ int height, width;
double row, col;
FloatAnchor anchor;
FloatRelative relative;
@@ -983,22 +994,31 @@ typedef struct {
bool focusable;
} FloatConfig;
-#define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \
+#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
+ .row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \
.focusable = true })
-/*
- * Structure which contains all information that belongs to a window
- *
- * All row numbers are relative to the start of the window, except w_winrow.
- */
+// Structure to store last cursor position and topline. Used by check_lnums()
+// and reset_lnums().
+typedef struct
+{
+ int w_topline_save; // original topline value
+ int w_topline_corr; // corrected topline value
+ pos_T w_cursor_save; // original cursor position
+ pos_T w_cursor_corr; // corrected cursor position
+} pos_save_T;
+
+/// Structure which contains all information that belongs to a window.
+///
+/// All row numbers are relative to the start of the window, except w_winrow.
struct window_S {
handle_T handle; ///< unique identifier for the window
buf_T *w_buffer; ///< buffer we are a window into (used
///< often, keep it the first item!)
- synblock_T *w_s; /* for :ownsyntax */
+ synblock_T *w_s; ///< for :ownsyntax
int w_hl_id_normal; ///< 'winhighlight' normal id
int w_hl_attr_normal; ///< 'winhighlight' normal final attrs
@@ -1008,24 +1028,25 @@ struct window_S {
int w_hl_needs_update; ///< attrs need to be recalculated
- win_T *w_prev; /* link to previous window */
- win_T *w_next; /* link to next window */
- bool w_closing; /* window is being closed, don't let
- autocommands close it too. */
+ win_T *w_prev; ///< link to previous window
+ win_T *w_next; ///< link to next window
+ bool w_closing; ///< window is being closed, don't let
+ /// autocommands close it too.
- frame_T *w_frame; /* frame containing this window */
+ frame_T *w_frame; ///< frame containing this window
- pos_T w_cursor; /* cursor position in buffer */
+ pos_T w_cursor; ///< cursor position in buffer
- colnr_T w_curswant; /* The column we'd like to be at. This is
- used to try to stay in the same column
- for up/down cursor motions. */
+ colnr_T w_curswant; ///< Column we want to be at. This is
+ /// used to try to stay in the same column
+ /// for up/down cursor motions.
int w_set_curswant; // If set, then update w_curswant the next
// time through cursupdate() to the
// current virtual column
linenr_T w_last_cursorline; ///< where last 'cursorline' was drawn
+ pos_T w_last_cursormoved; ///< for CursorMoved event
// the next seven are used to update the visual part
char w_old_visual_mode; ///< last known VIsual_mode
@@ -1080,17 +1101,18 @@ struct window_S {
colnr_T w_skipcol; /* starting column when a single line
doesn't fit in the window */
- /*
- * Layout of the window in the screen.
- * May need to add "msg_scrolled" to "w_winrow" in rare situations.
- */
- int w_winrow; /* first row of window in screen */
- int w_height; /* number of rows in window, excluding
- status/command line(s) */
- int w_status_height; /* number of status lines (0 or 1) */
- int w_wincol; /* Leftmost column of window in screen. */
- int w_width; /* Width of window, excluding separation. */
- int w_vsep_width; /* Number of separator columns (0 or 1). */
+ //
+ // Layout of the window in the screen.
+ // May need to add "msg_scrolled" to "w_winrow" in rare situations.
+ //
+ int w_winrow; // first row of window in screen
+ int w_height; // number of rows in window, excluding
+ // status/command line(s)
+ int w_status_height; // number of status lines (0 or 1)
+ int w_wincol; // Leftmost column of window in screen.
+ int w_width; // Width of window, excluding separation.
+ int w_vsep_width; // Number of separator columns (0 or 1).
+ pos_save_T w_save_cursor; // backup of cursor pos and topline
// inner size of window, which can be overridden by external UI
int w_height_inner;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index ddff93d83b..b155b3861f 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1777,9 +1777,12 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \
+ const uvarnumber_T digit = (uvarnumber_T)(conv); \
/* avoid ubsan error for overflow */ \
- if (un < UVARNUMBER_MAX / base) { \
- un = base * un + (uvarnumber_T)(conv); \
+ if (un < UVARNUMBER_MAX / base \
+ || (un == UVARNUMBER_MAX / base \
+ && (base != 10 || digit <= UVARNUMBER_MAX % 10))) { \
+ un = base * un + digit; \
} else { \
un = UVARNUMBER_MAX; \
} \
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 667bc54e2e..49bc2ab2f0 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -58,9 +58,10 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
-/*
- * definitions used for CTRL-X submode
- */
+// Definitions used for CTRL-X submode.
+// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
+// and ctrl_x_mode_names[].
+
#define CTRL_X_WANT_IDENT 0x100
#define CTRL_X_NOT_DEFINED_YET 1
@@ -83,17 +84,18 @@
#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
#define CTRL_X_MODE_LINE_OR_EVAL(m) (m == CTRL_X_WHOLE_LINE || m == CTRL_X_EVAL)
+// Message for CTRL-X mode, index is ctrl_x_mode.
static char *ctrl_x_msgs[] =
{
- N_(" Keyword completion (^N^P)"), /* ctrl_x_mode == 0, ^P/^N compl. */
+ N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
- NULL,
+ NULL, // CTRL_X_SCROLL: depends on state
N_(" Whole line completion (^L^N^P)"),
N_(" File name completion (^F^N^P)"),
N_(" Tag completion (^]^N^P)"),
N_(" Path pattern completion (^N^P)"),
N_(" Definition completion (^D^N^P)"),
- NULL,
+ NULL, // CTRL_X_FINISHED
N_(" Dictionary completion (^K^N^P)"),
N_(" Thesaurus completion (^T^N^P)"),
N_(" Command-line completion (^V^N^P)"),
@@ -104,6 +106,26 @@ static char *ctrl_x_msgs[] =
NULL, // CTRL_X_EVAL doesn't use msg.
};
+static char *ctrl_x_mode_names[] = {
+ "keyword",
+ "ctrl_x",
+ "unknown", // CTRL_X_SCROLL
+ "whole_line",
+ "files",
+ "tags",
+ "path_patterns",
+ "path_defines",
+ "unknown", // CTRL_X_FINISHED
+ "dictionary",
+ "thesaurus",
+ "cmdline",
+ "function",
+ "omni",
+ "spell",
+ NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
+ "eval"
+};
+
static char e_hitend[] = N_("Hit end of paragraph");
static char e_complwin[] = N_("E839: Completion function changed window");
static char e_compldel[] = N_("E840: Completion function deleted text");
@@ -115,16 +137,18 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char_u *cp_str; /* matched text */
- char cp_icase; /* TRUE or FALSE: ignore case */
- char_u *(cp_text[CPT_COUNT]); /* text for the menu */
- char_u *cp_fname; /* file containing the match, allocated when
- * cp_flags has FREE_FNAME */
- int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */
- int cp_number; /* sequence number */
+ char_u *cp_str; // matched text
+ char cp_icase; // TRUE or FALSE: ignore case
+ char cp_equal; // TRUE or FALSE: ins_compl_equal always ok
+ char_u *(cp_text[CPT_COUNT]); // text for the menu
+ char_u *cp_fname; // file containing the match, allocated when
+ // cp_flags has FREE_FNAME
+ int cp_flags; // ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME
+ int cp_number; // sequence number
};
-#define ORIGINAL_TEXT (1) /* the original text when the expansion begun */
+// flags for ins_compl_add()
+#define ORIGINAL_TEXT (1) // the original text when the expansion begun
#define FREE_FNAME (2)
/*
@@ -645,8 +669,9 @@ static int insert_execute(VimState *state, int key)
// there is nothing to add, CTRL-L works like CTRL-P then.
if (s->c == Ctrl_L
&& (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)
- || (int)STRLEN(compl_shown_match->cp_str)
- > curwin->w_cursor.col - compl_col)) {
+ || (compl_shown_match->cp_str != NULL
+ && (int)STRLEN(compl_shown_match->cp_str)
+ > curwin->w_cursor.col - compl_col))) {
ins_compl_addfrommatch();
return 1; // continue
}
@@ -1376,7 +1401,7 @@ ins_redraw (
// Trigger CursorMoved if the cursor moved. Not when the popup menu is
// visible, the command might delete it.
if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
- && !equalpos(last_cursormoved, curwin->w_cursor)
+ && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)
&& !pum_visible()) {
// Need to update the screen first, to make sure syntax
// highlighting is correct after making a change (e.g., inserting
@@ -1392,7 +1417,7 @@ ins_redraw (
ins_apply_autocmds(EVENT_CURSORMOVEDI);
}
conceal_cursor_moved = true;
- last_cursormoved = curwin->w_cursor;
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
// Trigger TextChangedI if changedtick differs.
@@ -2012,14 +2037,14 @@ static bool ins_compl_accept_char(int c)
return vim_iswordc(c);
}
-/*
- * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
- * case of the originally typed text is used, and the case of the completed
- * text is inferred, ie this tries to work out what case you probably wanted
- * the rest of the word to be in -- webb
- */
-int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags)
+// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
+// case of the originally typed text is used, and the case of the completed
+// text is inferred, ie this tries to work out what case you probably wanted
+// the rest of the word to be in -- webb
+int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname,
+ int dir, int flags)
{
+ char_u *str = str_arg;
int i, c;
int actual_len; /* Take multi-byte characters */
int actual_compl_length; /* into account. */
@@ -2148,10 +2173,10 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
xfree(wca);
- return ins_compl_add(IObuff, len, icase, fname, NULL, false, dir, flags,
- false);
+ str = IObuff;
}
- return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false);
+ return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags,
+ false, false);
}
/// Add a match to the list of matches
@@ -2168,6 +2193,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
/// cptext itself will not be freed.
/// @param[in] cdir Completion direction.
/// @param[in] adup True if duplicate matches are to be accepted.
+/// @param[in] equal Match is always accepted by ins_compl_equal.
///
/// @return NOTDONE if the given string is already in the list of completions,
/// otherwise it is added to the list and OK is returned. FAIL will be
@@ -2176,7 +2202,8 @@ static int ins_compl_add(char_u *const str, int len,
const bool icase, char_u *const fname,
char_u *const *const cptext,
const bool cptext_allocated,
- const Direction cdir, int flags, const bool adup)
+ const Direction cdir, int flags, const bool adup,
+ int equal)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -2228,6 +2255,7 @@ static int ins_compl_add(char_u *const str, int len,
match->cp_number = 0;
match->cp_str = vim_strnsave(str, len);
match->cp_icase = icase;
+ match->cp_equal = equal;
/* match-fname is:
* - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -2301,6 +2329,9 @@ static int ins_compl_add(char_u *const str, int len,
static bool ins_compl_equal(compl_T *match, char_u *str, size_t len)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
+ if (match->cp_equal) {
+ return true;
+ }
if (match->cp_icase) {
return STRNICMP(match->cp_str, str, len) == 0;
}
@@ -2374,7 +2405,8 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase)
for (i = 0; i < num_matches && add_r != FAIL; i++)
if ((add_r = ins_compl_add(matches[i], -1, icase,
- NULL, NULL, false, dir, 0, false)) == OK) {
+ NULL, NULL, false, dir, 0, false,
+ false)) == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
}
@@ -2443,7 +2475,7 @@ void set_completion(colnr_T startcol, list_T *list)
compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col,
compl_length);
if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0,
- ORIGINAL_TEXT, false) != OK) {
+ ORIGINAL_TEXT, false, false) != OK) {
return;
}
@@ -2656,8 +2688,25 @@ void ins_compl_show_pum(void)
col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_selected_item = cur;
- pum_display(compl_match_array, compl_match_arraysize, cur, array_changed);
+ pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0);
curwin->w_cursor.col = col;
+
+ if (!has_event(EVENT_COMPLETECHANGED)) {
+ return;
+ }
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+ if (cur < 0) {
+ tv_dict_add_dict(dict, S_LEN("completed_item"), tv_dict_alloc());
+ } else {
+ dict_T *item = ins_compl_dict_alloc(compl_curr_match);
+ tv_dict_add_dict(dict, S_LEN("completed_item"), item);
+ }
+ pum_set_boundings(dict);
+ tv_dict_set_keys_readonly(dict);
+ textlock++;
+ apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf);
+ textlock--;
+ tv_dict_clear(dict);
}
#define DICT_FIRST (1) /* use just first element in "dict" */
@@ -2963,6 +3012,99 @@ bool ins_compl_active(void)
return compl_started;
}
+// Get complete information
+void get_complete_info(list_T *what_list, dict_T *retdict)
+{
+#define CI_WHAT_MODE 0x01
+#define CI_WHAT_PUM_VISIBLE 0x02
+#define CI_WHAT_ITEMS 0x04
+#define CI_WHAT_SELECTED 0x08
+#define CI_WHAT_INSERTED 0x10
+#define CI_WHAT_ALL 0xff
+ int what_flag;
+
+ if (what_list == NULL) {
+ what_flag = CI_WHAT_ALL;
+ } else {
+ what_flag = 0;
+ for (listitem_T *item = tv_list_first(what_list)
+ ; item != NULL
+ ; item = TV_LIST_ITEM_NEXT(what_list, item)) {
+ const char *what = tv_get_string(TV_LIST_ITEM_TV(item));
+
+ if (STRCMP(what, "mode") == 0) {
+ what_flag |= CI_WHAT_MODE;
+ } else if (STRCMP(what, "pum_visible") == 0) {
+ what_flag |= CI_WHAT_PUM_VISIBLE;
+ } else if (STRCMP(what, "items") == 0) {
+ what_flag |= CI_WHAT_ITEMS;
+ } else if (STRCMP(what, "selected") == 0) {
+ what_flag |= CI_WHAT_SELECTED;
+ } else if (STRCMP(what, "inserted") == 0) {
+ what_flag |= CI_WHAT_INSERTED;
+ }
+ }
+ }
+
+ int ret = OK;
+ if (what_flag & CI_WHAT_MODE) {
+ ret = tv_dict_add_str(retdict, S_LEN("mode"),
+ (char *)ins_compl_mode());
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) {
+ ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_ITEMS)) {
+ list_T *li = tv_list_alloc(ins_compl_len());
+
+ ret = tv_dict_add_list(retdict, S_LEN("items"), li);
+ if (ret == OK && compl_first_match != NULL) {
+ compl_T *match = compl_first_match;
+ do {
+ if (!(match->cp_flags & ORIGINAL_TEXT)) {
+ dict_T *di = tv_dict_alloc();
+
+ tv_list_append_dict(li, di);
+ tv_dict_add_str(di, S_LEN("word"),
+ (char *)EMPTY_IF_NULL(match->cp_str));
+ tv_dict_add_str(di, S_LEN("abbr"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR]));
+ tv_dict_add_str(di, S_LEN("menu"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU]));
+ tv_dict_add_str(di, S_LEN("kind"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND]));
+ tv_dict_add_str(di, S_LEN("info"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO]));
+ tv_dict_add_str(di, S_LEN("user_data"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA]));
+ }
+ match = match->cp_next;
+ } while (match != NULL && match != compl_first_match);
+ }
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_SELECTED)) {
+ ret = tv_dict_add_nr(retdict, S_LEN("selected"),
+ (compl_curr_match != NULL)
+ ? compl_curr_match->cp_number - 1 : -1);
+ }
+
+ // TODO(vim):
+ // if (ret == OK && (what_flag & CI_WHAT_INSERTED))
+}
+
+// Return Insert completion mode name string
+static char_u * ins_compl_mode(void)
+{
+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) {
+ return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
+ }
+ return (char_u *)"";
+}
+
+
/*
* Delete one character before the cursor and show the subset of the matches
* that match the word that is now before the cursor.
@@ -3626,6 +3768,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
bool icase = false;
bool adup = false;
bool aempty = false;
+ bool aequal = false;
char *(cptext[CPT_COUNT]);
if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) {
@@ -3640,6 +3783,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase");
adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup");
aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty");
+ if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL) {
+ aequal = tv_dict_get_number(tv->vval.v_dict, "equal");
+ }
} else {
word = (const char *)tv_get_string_chk(tv);
memset(cptext, 0, sizeof(cptext));
@@ -3651,7 +3797,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
return FAIL;
}
return ins_compl_add((char_u *)word, -1, icase, NULL,
- (char_u **)cptext, true, dir, 0, adup);
+ (char_u **)cptext, true, dir, 0, adup, aequal);
}
/*
@@ -4097,31 +4243,37 @@ static void ins_compl_insert(int in_compl_func)
else
compl_used_match = TRUE;
- // Set completed item.
+ dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict);
+ if (!in_compl_func) {
+ compl_curr_match = compl_shown_match;
+ }
+}
+
+// Convert to complete item dict
+static dict_T *ins_compl_dict_alloc(compl_T *match)
+{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc();
tv_dict_add_str(
dict, S_LEN("word"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str));
+ (const char *)EMPTY_IF_NULL(match->cp_str));
tv_dict_add_str(
dict, S_LEN("abbr"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR]));
tv_dict_add_str(
dict, S_LEN("menu"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU]));
tv_dict_add_str(
dict, S_LEN("kind"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND]));
tv_dict_add_str(
dict, S_LEN("info"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO]));
tv_dict_add_str(
dict, S_LEN("user_data"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA]));
- set_vim_var_dict(VV_COMPLETED_ITEM, dict);
- if (!in_compl_func) {
- compl_curr_match = compl_shown_match;
- }
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA]));
+ return dict;
}
/*
@@ -4807,7 +4959,7 @@ static int ins_complete(int c, bool enable_pum)
xfree(compl_orig_text);
compl_orig_text = vim_strnsave(line + compl_col, compl_length);
if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0,
- ORIGINAL_TEXT, false) != OK) {
+ ORIGINAL_TEXT, false, false) != OK) {
xfree(compl_pattern);
compl_pattern = NULL;
xfree(compl_orig_text);
@@ -5486,16 +5638,33 @@ internal_format (
/* remember position of blank just before text */
end_col = curwin->w_cursor.col;
- /* find start of sequence of blanks */
+ // find start of sequence of blanks
+ int wcc = 0; // counter for whitespace chars
while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
dec_cursor();
cc = gchar_cursor();
+
+ // Increment count of how many whitespace chars in this
+ // group; we only need to know if it's more than one.
+ if (wcc < 2) {
+ wcc++;
+ }
}
- if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
- break; /* only spaces in front of text */
- /* Don't break until after the comment leader */
- if (curwin->w_cursor.col < leader_len)
+ if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
+ break; // only spaces in front of text
+ }
+
+ // Don't break after a period when 'formatoptions' has 'p' and
+ // there are less than two spaces.
+ if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
+ continue;
+ }
+
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
break;
+ }
+
if (has_format_option(FO_ONE_LETTER)) {
/* do not break after one-letter words */
if (curwin->w_cursor.col == 0)
@@ -5859,10 +6028,7 @@ comp_textwidth (
textwidth -= 1;
}
textwidth -= curwin->w_p_fdc;
-
- if (signcolumn_on(curwin)) {
- textwidth -= 1;
- }
+ textwidth -= win_signcol_count(curwin);
if (curwin->w_p_nu || curwin->w_p_rnu)
textwidth -= 8;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5ef2a8772e..6479163028 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -241,13 +241,14 @@ typedef enum {
///< the value (prevents error message).
} GetLvalFlags;
-// function flags
+// flags used in uf_flags
#define FC_ABORT 0x01 // abort function on error
#define FC_RANGE 0x02 // function accepts range
#define FC_DICT 0x04 // Dict function, uses "self"
#define FC_CLOSURE 0x08 // closure, uses outer scope variables
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
+#define FC_SANDBOX 0x40 // function defined in the sandbox
// The names of packages that once were loaded are remembered.
static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL };
@@ -448,6 +449,7 @@ typedef struct {
int timer_id;
int repeat_count;
int refcount;
+ int emsg_count; ///< Errors in a repeating timer.
long timeout;
bool stopped;
bool paused;
@@ -3933,7 +3935,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
int op;
varnumber_T n1, n2;
bool use_float = false;
- float_T f1 = 0, f2;
+ float_T f1 = 0, f2 = 0;
bool error = false;
/*
@@ -5219,7 +5221,7 @@ bool garbage_collect(bool testing)
(void)garbage_collect(testing);
}
} else if (p_verbose > 0) {
- verb_msg((char_u *)_(
+ verb_msg(_(
"Not enough memory to set references, garbage collection aborted!"));
}
#undef ABORTING
@@ -5853,6 +5855,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
if (prof_def_func()) {
func_do_profile(fp);
}
+ if (sandbox) {
+ flags |= FC_SANDBOX;
+ }
fp->uf_varargs = true;
fp->uf_flags = flags;
fp->uf_calls = 0;
@@ -6328,8 +6333,12 @@ call_func(
}
- /* execute the function if no errors detected and executing */
- if (evaluate && error == ERROR_NONE) {
+ // Execute the function if executing and no errors were detected.
+ if (!evaluate) {
+ // Not evaluating, which means the return value is unknown. This
+ // matters for giving error messages.
+ rettv->v_type = VAR_UNKNOWN;
+ } else if (error == ERROR_NONE) {
char_u *rfname = fname;
/* Ignore "g:" before a function name. */
@@ -6654,12 +6663,24 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1; /* Failed */
}
-/*
- * "argc()" function
- */
static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = ARGCOUNT;
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // use the current window
+ rettv->vval.v_number = ARGCOUNT;
+ } else if (argvars[0].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[0]) == -1) {
+ // use the global argument list
+ rettv->vval.v_number = GARGCOUNT;
+ } else {
+ // use the argument list of the specified window
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp != NULL) {
+ rettv->vval.v_number = WARGCOUNT(wp);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+ }
}
/*
@@ -6680,28 +6701,54 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// Get the argument list for a given window
+static void get_arglist_as_rettv(aentry_T *arglist, int argcount,
+ typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, argcount);
+ if (arglist != NULL) {
+ for (int idx = 0; idx < argcount; idx++) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)alist_name(&arglist[idx]), -1);
+ }
+ }
+}
+
/*
* "argv(nr)" function
*/
static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int idx;
+ aentry_T *arglist = NULL;
+ int argcount = -1;
if (argvars[0].v_type != VAR_UNKNOWN) {
- idx = (int)tv_get_number_chk(&argvars[0], NULL);
- if (idx >= 0 && idx < ARGCOUNT) {
- rettv->vval.v_string = (char_u *)xstrdup(
- (const char *)alist_name(&ARGLIST[idx]));
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ arglist = ARGLIST;
+ argcount = ARGCOUNT;
+ } else if (argvars[1].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[1]) == -1) {
+ arglist = GARGLIST;
+ argcount = GARGCOUNT;
} else {
- rettv->vval.v_string = NULL;
+ win_T *wp = find_win_by_nr_or_id(&argvars[1]);
+ if (wp != NULL) {
+ // Use the argument list of the specified window
+ arglist = WARGLIST(wp);
+ argcount = WARGCOUNT(wp);
+ }
}
rettv->v_type = VAR_STRING;
- } else {
- tv_list_alloc_ret(rettv, ARGCOUNT);
- for (idx = 0; idx < ARGCOUNT; idx++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)alist_name(&ARGLIST[idx]), -1);
+ rettv->vval.v_string = NULL;
+ int idx = tv_get_number_chk(&argvars[0], NULL);
+ if (arglist != NULL && idx >= 0 && idx < argcount) {
+ rettv->vval.v_string = (char_u *)xstrdup(
+ (const char *)alist_name(&arglist[idx]));
+ } else if (idx == -1) {
+ get_arglist_as_rettv(arglist, argcount, rettv);
}
+ } else {
+ get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
}
}
@@ -6830,6 +6877,27 @@ static void assert_equal_common(typval_T *argvars, assert_type_T atype)
}
}
+static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
+ garray_T ga;
+
+ called_vim_beep = false;
+ suppress_errthrow = true;
+ emsg_silent = false;
+ do_cmdline_cmd(cmd);
+ if (!called_vim_beep) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (const char_u *)"command did not beep: ");
+ ga_concat(&ga, (const char_u *)cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+
+ suppress_errthrow = false;
+ emsg_on_display = false;
+}
+
// "assert_equal(expected, actual[, msg])" function
static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -7556,6 +7624,23 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
RedrawingDisabled = saved;
}
+// "complete_info()" function
+static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_dict_alloc_ret(rettv);
+
+ list_T *what_list = NULL;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG(_(e_listreq));
+ return;
+ }
+ what_list = argvars[0].vval.v_list;
+ }
+ get_complete_info(what_list, rettv->vval.v_dict);
+}
+
/*
* "confirm(message, buttons[, default [, type]])" function
*/
@@ -7983,7 +8068,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlID = HLF_CHD; // Changed line.
}
}
- rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID;
+ rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1);
}
/*
@@ -8194,6 +8279,19 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = path;
}
+/// Find a window: When using a Window ID in any tab page, when using a number
+/// in the current tab page.
+win_T * find_win_by_nr_or_id(typval_T *vp)
+{
+ int nr = (int)tv_get_number_chk(vp, NULL);
+
+ if (nr >= LOWEST_WIN_ID) {
+ return win_id2wp(vp);
+ }
+
+ return find_win_by_nr(vp, NULL);
+}
+
/*
* "exists()" function
*/
@@ -9953,6 +10051,37 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = type;
}
+// "getjumplist()" function
+static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ win_T *const wp = find_tabwin(&argvars[0], &argvars[1]);
+ if (wp == NULL) {
+ return;
+ }
+
+ cleanup_jumplist(wp, true);
+
+ list_T *const l = tv_list_alloc(wp->w_jumplistlen);
+ tv_list_append_list(rettv->vval.v_list, l);
+ tv_list_append_number(rettv->vval.v_list, wp->w_jumplistidx);
+
+ for (int i = 0; i < wp->w_jumplistlen; i++) {
+ if (wp->w_jumplist[i].fmark.mark.lnum == 0) {
+ continue;
+ }
+ dict_T *const d = tv_dict_alloc();
+ tv_list_append_dict(l, d);
+ tv_dict_add_nr(d, S_LEN("lnum"), wp->w_jumplist[i].fmark.mark.lnum);
+ tv_dict_add_nr(d, S_LEN("col"), wp->w_jumplist[i].fmark.mark.col);
+ tv_dict_add_nr(d, S_LEN("coladd"), wp->w_jumplist[i].fmark.mark.coladd);
+ tv_dict_add_nr(d, S_LEN("bufnr"), wp->w_jumplist[i].fmark.fnum);
+ if (wp->w_jumplist[i].fname != NULL) {
+ tv_dict_add_str(d, S_LEN("filename"), (char *)wp->w_jumplist[i].fname);
+ }
+ }
+}
+
/*
* "getline(lnum, [end])" function
*/
@@ -9988,7 +10117,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
dict_T *d = what_arg->vval.v_dict;
if (d != NULL) {
- get_errorlist_properties(wp, d, rettv->vval.v_dict);
+ qf_get_properties(wp, d, rettv->vval.v_dict);
}
} else {
EMSG(_(e_dictreq));
@@ -10000,7 +10129,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
/// "getloclist()" function
static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp = find_win_by_nr(&argvars[0], NULL);
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
@@ -10019,7 +10148,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// match added with matchaddpos()
for (i = 0; i < MAXPOSMATCH; i++) {
llpos_T *llpos;
- char buf[6];
+ char buf[30]; // use 30 to avoid compiler warning
llpos = &cur->pos.pos[i];
if (llpos->lnum == 0) {
@@ -10306,6 +10435,23 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getwinvar(argvars, rettv, 1);
}
+// "gettagstack()" function
+static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp = curwin; // default is current window
+
+ tv_dict_alloc_ret(rettv);
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+ }
+
+ get_tagstack(wp, rettv->vval.v_dict);
+}
+
/// Returns information about a window as a dictionary.
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
{
@@ -10371,11 +10517,19 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, 2);
- const win_T *const wp = find_win_by_nr(&argvars[0], NULL);
+ const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
}
+// "getwinpos({timeout})" function
+static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(rettv->vval.v_list, -1);
+ tv_list_append_number(rettv->vval.v_list, -1);
+}
+
/*
* "getwinposx()" function
*/
@@ -10707,6 +10861,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef __APPLE__
"mac",
"macunix",
+ "osx",
#endif
"menu",
"mksession",
@@ -11180,7 +11335,6 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int inputsecret_flag = 0;
-
/*
* This function is used by f_input() and f_inputdialog() functions. The third
* argument to f_input() specifies the type of completion to use at the
@@ -11369,25 +11523,21 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL};
-/*
- * "inputrestore()" function
- */
+/// "inputrestore()" function
static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (!GA_EMPTY(&ga_userinput)) {
- --ga_userinput.ga_len;
+ ga_userinput.ga_len--;
restore_typeahead((tasave_T *)(ga_userinput.ga_data)
- + ga_userinput.ga_len);
- /* default return is zero == OK */
+ + ga_userinput.ga_len);
+ // default return is zero == OK
} else if (p_verbose > 1) {
- verb_msg((char_u *)_("called inputrestore() more often than inputsave()"));
- rettv->vval.v_number = 1; /* Failed */
+ verb_msg(_("called inputrestore() more often than inputsave()"));
+ rettv->vval.v_number = 1; // Failed
}
}
-/*
- * "inputsave()" function
- */
+/// "inputsave()" function
static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
// Add an entry to the stack of typeahead storage.
@@ -11395,9 +11545,7 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_typeahead(p);
}
-/*
- * "inputsecret()" function
- */
+/// "inputsecret()" function
static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
cmdline_star++;
@@ -11564,10 +11712,11 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
{
- const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars);
+ const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xmalloc(len + 1);
- vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
+ vim_vsnprintf_typval((char *)rettv->vval.v_string, len + 1, "%p",
+ dummy_ap, argvars);
}
/*
@@ -12503,6 +12652,31 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
find_some_match(argvars, rettv, kSomeMatch);
}
+static int matchadd_dict_arg(typval_T *tv, const char **conceal_char,
+ win_T **win)
+{
+ dictitem_T *di;
+
+ if (tv->v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return FAIL;
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) {
+ *conceal_char = tv_get_string(&di->di_tv);
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) {
+ *win = find_win_by_nr_or_id(&di->di_tv);
+ if (*win == NULL) {
+ EMSG(_("E957: Invalid window number"));
+ return FAIL;
+ }
+ }
+
+ return OK;
+}
+
/*
* "matchadd()" function
*/
@@ -12516,6 +12690,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int id = -1;
bool error = false;
const char *conceal_char = NULL;
+ win_T *win = curwin;
rettv->vval.v_number = -1;
@@ -12526,16 +12701,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
- dictitem_T *di;
- if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
- != NULL) {
- conceal_char = tv_get_string(&di->di_tv);
- }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
}
}
}
@@ -12547,8 +12715,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL,
- conceal_char);
+ rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char);
}
static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@@ -12576,21 +12743,15 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int prio = 10;
int id = -1;
const char *conceal_char = NULL;
+ win_T *win = curwin;
if (argvars[2].v_type != VAR_UNKNOWN) {
prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
- dictitem_T *di;
- if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
- != NULL) {
- conceal_char = tv_get_string(&di->di_tv);
- }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
}
}
}
@@ -12604,8 +12765,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l,
- conceal_char);
+ rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char);
}
/*
@@ -12752,33 +12912,36 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char buf[NUMBUFLEN];
const char *const dir = tv_get_string_buf(&argvars[0], buf);
if (*dir == NUL) {
- rettv->vval.v_number = FAIL;
- } else {
- if (*path_tail((char_u *)dir) == NUL) {
- // Remove trailing slashes.
- *path_tail_with_sep((char_u *)dir) = NUL;
- }
+ return;
+ }
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prot = tv_get_number_chk(&argvars[2], NULL);
- }
- if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) {
- char *failed_dir;
- int ret = os_mkdir_recurse(dir, prot, &failed_dir);
- if (ret != 0) {
- EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
- xfree(failed_dir);
- rettv->vval.v_number = FAIL;
- return;
- } else {
- rettv->vval.v_number = OK;
- return;
- }
+ if (*path_tail((char_u *)dir) == NUL) {
+ // Remove trailing slashes.
+ *path_tail_with_sep((char_u *)dir) = NUL;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prot = tv_get_number_chk(&argvars[2], NULL);
+ if (prot == -1) {
+ return;
+ }
+ }
+ if (strcmp(tv_get_string(&argvars[1]), "p") == 0) {
+ char *failed_dir;
+ int ret = os_mkdir_recurse(dir, prot, &failed_dir);
+ if (ret != 0) {
+ EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
+ xfree(failed_dir);
+ rettv->vval.v_number = FAIL;
+ return;
+ } else {
+ rettv->vval.v_number = OK;
+ return;
}
}
- rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot);
}
+ rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
}
/// "mode()" function
@@ -13020,11 +13183,11 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
did_emsg = false;
char buf[NUMBUFLEN];
const char *fmt = tv_get_string_buf(&argvars[0], buf);
- len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1);
+ len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc(len + 1);
rettv->vval.v_string = (char_u *)s;
- (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1);
+ (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1);
}
did_emsg |= saved_did_emsg;
}
@@ -14761,7 +14924,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
skip_args:
if (!title) {
- title = (wp ? "setloclist()" : "setqflist()");
+ title = (wp ? ":setloclist()" : ":setqflist()");
}
recursive++;
@@ -14781,7 +14944,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
- win = find_win_by_nr(&argvars[0], NULL);
+ win = find_win_by_nr_or_id(&argvars[0]);
if (win != NULL) {
set_qf_ll_list(win, &argvars[1], rettv);
}
@@ -14837,7 +15000,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// match from matchaddpos()
for (i = 1; i < 9; i++) {
- char buf[5];
+ char buf[30]; // use 30 to avoid compiler warning
snprintf(buf, sizeof(buf), "pos%d", i);
dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
if (pos_di != NULL) {
@@ -15087,6 +15250,60 @@ static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
setwinvar(argvars, rettv, 1);
}
+// "settagstack()" function
+static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ static char *e_invact2 = N_("E962: Invalid action: '%s'");
+ win_T *wp;
+ dict_T *d;
+ int action = 'r';
+
+ rettv->vval.v_number = -1;
+
+ // first argument: window number or id
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+
+ // second argument: dict with items to set in the tag stack
+ if (argvars[1].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ d = argvars[1].vval.v_dict;
+ if (d == NULL) {
+ return;
+ }
+
+ // third argument: action - 'a' for append and 'r' for replace.
+ // default is to replace the stack.
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ action = 'r';
+ } else if (argvars[2].v_type == VAR_STRING) {
+ const char *actstr;
+ actstr = tv_get_string_chk(&argvars[2]);
+ if (actstr == NULL) {
+ return;
+ }
+ if ((*actstr == 'r' || *actstr == 'a') && actstr[1] == NUL) {
+ action = *actstr;
+ } else {
+ EMSG2(_(e_invact2), actstr);
+ return;
+ }
+ } else {
+ EMSG(_(e_stringreq));
+ return;
+ }
+
+ if (set_tagstack(wp, d, action) == OK) {
+ rettv->vval.v_number = 0;
+ } else {
+ EMSG(_(e_listreq));
+ }
+}
+
/*
* "setwinvar()" function
*/
@@ -16306,6 +16523,27 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// "swapinfo(swap_filename)" function
+static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_dict_alloc_ret(rettv);
+ get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
+}
+
+/// "swapname(expr)" function
+static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ buf_T *buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL
+ || buf->b_ml.ml_mfp == NULL
+ || buf->b_ml.ml_mfp->mf_fname == NULL) {
+ rettv->vval.v_string = NULL;
+ } else {
+ rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname);
+ }
+}
+
/// "synID(lnum, col, trans)" function
static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -16631,7 +16869,6 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-
/*
* "tabpagenr()" function
*/
@@ -16668,6 +16905,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
twin = (tp == curtab) ? curwin : tp->tp_curwin;
if (argvar->v_type != VAR_UNKNOWN) {
+ bool invalid_arg = false;
const char *const arg = tv_get_string_chk(argvar);
if (arg == NULL) {
nr = 0; // Type error; errmsg already given.
@@ -16679,6 +16917,31 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
nr = 0;
}
} else {
+ // Extract the window count (if specified). e.g. winnr('3j')
+ char_u *endp;
+ long count = strtol((char *)arg, (char **)&endp, 10);
+ if (count <= 0) {
+ // if count is not specified, default to 1
+ count = 1;
+ }
+ if (endp != NULL && *endp != '\0') {
+ if (strequal((char *)endp, "j")) {
+ twin = win_vert_neighbor(tp, twin, false, count);
+ } else if (strequal((char *)endp, "k")) {
+ twin = win_vert_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "h")) {
+ twin = win_horz_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "l")) {
+ twin = win_horz_neighbor(tp, twin, false, count);
+ } else {
+ invalid_arg = true;
+ }
+ } else {
+ invalid_arg = true;
+ }
+ }
+
+ if (invalid_arg) {
EMSG2(_(e_invexpr2), arg);
nr = 0;
}
@@ -16712,7 +16975,6 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = nr;
}
-
/*
* "tagfiles()" function
*/
@@ -17075,6 +17337,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer->refcount = 1;
timer->stopped = false;
timer->paused = false;
+ timer->emsg_count = 0;
timer->repeat_count = repeat;
timer->timeout = timeout;
timer->timer_id = last_timer_id++;
@@ -17117,6 +17380,9 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
static void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
+ int save_did_emsg = did_emsg;
+ int save_called_emsg = called_emsg;
+
if (timer->stopped || timer->paused) {
return;
}
@@ -17131,8 +17397,24 @@ static void timer_due_cb(TimeWatcher *tw, void *data)
argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = timer->timer_id;
typval_T rettv = TV_INITIAL_VALUE;
+ called_emsg = false;
callback_call(&timer->callback, 1, argv, &rettv);
+
+ // Handle error message
+ if (called_emsg && did_emsg) {
+ timer->emsg_count++;
+ if (current_exception != NULL) {
+ discard_current_exception();
+ }
+ }
+ did_emsg = save_did_emsg;
+ called_emsg = save_called_emsg;
+
+ if (timer->emsg_count >= 3) {
+ timer_stop(timer);
+ }
+
tv_clear(&rettv);
if (!timer->stopped && timer->timeout == 0) {
@@ -17419,7 +17701,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// If there is no file name there will be no undo file.
rettv->vval.v_string = NULL;
} else {
- char *ffname = FullName_save(fname, false);
+ char *ffname = FullName_save(fname, true);
if (ffname != NULL) {
rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false);
@@ -17532,18 +17814,15 @@ static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = win_id2win(argvars);
}
-/*
- * "winbufnr(nr)" function
- */
+/// "winbufnr(nr)" function
static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_buffer->b_fnum;
+ }
}
/*
@@ -17555,18 +17834,15 @@ static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = curwin->w_wcol + 1;
}
-/*
- * "winheight(nr)" function
- */
+/// "winheight(nr)" function
static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_height;
+ }
}
/*
@@ -17830,18 +18106,15 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
return ret;
}
-/*
- * "winwidth(nr)" function
- */
+/// "winwidth(nr)" function
static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_width;
+ }
}
/// "wordcount()" function
@@ -18053,7 +18326,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
* Return FAIL when conversion is not possible, doesn't check the position for
* validity.
*/
-static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
+int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
{
list_T *l;
long i = 0;
@@ -19570,16 +19843,15 @@ void ex_echo(exarg_T *eap)
{
char_u *arg = eap->arg;
typval_T rettv;
- bool needclr = true;
bool atstart = true;
const int did_emsg_before = did_emsg;
if (eap->skip)
++emsg_skip;
while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) {
- /* If eval1() causes an error message the text from the command may
- * still need to be cleared. E.g., "echo 22,44". */
- need_clr_eos = needclr;
+ // If eval1() causes an error message the text from the command may
+ // still need to be cleared. E.g., "echo 22,44".
+ need_clr_eos = true;
{
char_u *p = arg;
@@ -19623,14 +19895,14 @@ void ex_echo(exarg_T *eap)
}
eap->nextcmd = check_nextcmd(arg);
- if (eap->skip)
- --emsg_skip;
- else {
- /* remove text that may still be there from the command */
- if (needclr)
- msg_clr_eos();
- if (eap->cmdidx == CMD_echo)
+ if (eap->skip) {
+ emsg_skip--;
+ } else {
+ // remove text that may still be there from the command
+ msg_clr_eos();
+ if (eap->cmdidx == CMD_echo) {
msg_end();
+ }
}
}
@@ -20348,6 +20620,9 @@ void ex_function(exarg_T *eap)
if (prof_def_func())
func_do_profile(fp);
fp->uf_varargs = varargs;
+ if (sandbox) {
+ flags |= FC_SANDBOX;
+ }
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
@@ -21338,6 +21613,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
scid_T save_current_SID;
+ bool using_sandbox = false;
funccall_T *fc;
int save_did_emsg;
static int depth = 0;
@@ -21495,6 +21771,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
save_sourcing_name = sourcing_name;
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 1;
+
+ if (fp->uf_flags & FC_SANDBOX) {
+ using_sandbox = true;
+ sandbox++;
+ }
+
// need space for new sourcing_name:
// * save_sourcing_name
// * "["number"].." or "function "
@@ -21655,6 +21937,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (do_profiling_yes) {
script_prof_restore(&wait_start);
}
+ if (using_sandbox) {
+ sandbox--;
+ }
if (p_verbose >= 12 && sourcing_name != NULL) {
++no_wait_return;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 78cac4878d..d4bb69613e 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -25,6 +25,7 @@ return {
arglistid={args={0, 2}},
argv={args={0, 1}},
asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc
+ assert_beeps={args={1, 2}},
assert_equal={args={2, 3}},
assert_exception={args={1, 2}},
assert_fails={args={1, 2}},
@@ -64,6 +65,7 @@ return {
complete={args=2},
complete_add={args=1},
complete_check={},
+ complete_info={args={0, 1}},
confirm={args={1, 4}},
copy={args=1},
cos={args=1, func="float_op_wrapper", data="&cos"},
@@ -129,6 +131,7 @@ return {
getfsize={args=1},
getftime={args=1},
getftype={args=1},
+ getjumplist={args={0, 2}},
getline={args={1, 2}},
getloclist={args={1, 2}},
getmatches={},
@@ -140,7 +143,9 @@ return {
gettabinfo={args={0, 1}},
gettabvar={args={2, 3}},
gettabwinvar={args={3, 4}},
+ gettagstack={args={0, 1}},
getwininfo={args={0, 1}},
+ getwinpos={args={0, 1}},
getwinposx={},
getwinposy={},
getwinvar={args={2, 3}},
@@ -266,6 +271,7 @@ return {
setreg={args={2, 3}},
settabvar={args=3},
settabwinvar={args=4},
+ settagstack={args={2, 3}},
setwinvar={args=3},
sha256={args=1},
shellescape={args={1, 2}},
@@ -298,6 +304,8 @@ return {
strwidth={args=1},
submatch={args={1, 2}},
substitute={args=4},
+ swapinfo={args={1}},
+ swapname={args={1}},
synID={args=3},
synIDattr={args={2, 3}},
synIDtrans={args=1},
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 912aecafec..ffb46abfea 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1656,12 +1656,7 @@ int tv_dict_add_special(dict_T *const d, const char *const key,
/// Add a string entry to dictionary
///
-/// @param[out] d Dictionary to add entry to.
-/// @param[in] key Key to add.
-/// @param[in] key_len Key length.
-/// @param[in] val String to add.
-///
-/// @return OK in case of success, FAIL when key already exists.
+/// @see tv_dict_add_allocated_str
int tv_dict_add_str(dict_T *const d,
const char *const key, const size_t key_len,
const char *const val)
@@ -1672,6 +1667,27 @@ int tv_dict_add_str(dict_T *const d,
/// Add a string entry to dictionary
///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] val String to add. NULL adds empty string.
+/// @param[in] len Use this many bytes from `val`, or -1 for whole string.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_str_len(dict_T *const d,
+ const char *const key, const size_t key_len,
+ char *const val, int len)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+ char *s = val ? val : "";
+ if (val != NULL) {
+ s = (len < 0) ? xstrdup(val) : xstrndup(val, (size_t)len);
+ }
+ return tv_dict_add_allocated_str(d, key, key_len, s);
+}
+
+/// Add a string entry to dictionary
+///
/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of
/// creating a new copy.
///
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 6812b342bf..19907d2e45 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -21,16 +21,14 @@
#endif
void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize)
- FUNC_ATTR_NONNULL_ARG(1)
- FUNC_ATTR_NONNULL_ARG(2)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(loop, stream, fd, NULL);
rstream_init(stream, bufsize);
}
void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize)
- FUNC_ATTR_NONNULL_ARG(1)
- FUNC_ATTR_NONNULL_ARG(2)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(NULL, stream, -1, uvstream);
rstream_init(stream, bufsize);
@@ -138,6 +136,10 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
}
// Called by the by the 'idle' handle to emulate a reading event
+//
+// Idle callbacks are invoked once per event loop:
+// - to perform some very low priority activity.
+// - to keep the loop "alive" (so there is always an event to process)
static void fread_idle_cb(uv_idle_t *handle)
{
uv_fs_t req;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index ba25b76ec7..70b1d890d6 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -53,9 +53,19 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
stream->uv.idle.data = stream;
} else {
assert(type == UV_NAMED_PIPE || type == UV_TTY);
+#ifdef WIN32
+ if (type == UV_TTY) {
+ uv_tty_init(&loop->uv, &stream->uv.tty, fd, 0);
+ uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_RAW);
+ stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.tty);
+ } else {
+#endif
uv_pipe_init(&loop->uv, &stream->uv.pipe, 0);
uv_pipe_open(&stream->uv.pipe, fd);
stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.pipe);
+#ifdef WIN32
+ }
+#endif
}
}
diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h
index e713323f5c..a5c33a66a2 100644
--- a/src/nvim/event/stream.h
+++ b/src/nvim/event/stream.h
@@ -13,7 +13,7 @@ typedef struct stream Stream;
/// Type of function called when the Stream buffer is filled with data
///
/// @param stream The Stream instance
-/// @param rbuffer The associated RBuffer instance
+/// @param buf The associated RBuffer instance
/// @param count Number of bytes that was read.
/// @param data User-defined data
/// @param eof If the stream reached EOF.
@@ -23,7 +23,7 @@ typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count,
/// Type of function called when the Stream has information about a write
/// request.
///
-/// @param wstream The Stream instance
+/// @param stream The Stream instance
/// @param data User-defined data
/// @param status 0 on success, anything else indicates failure
typedef void (*stream_write_cb)(Stream *stream, void *data, int status);
@@ -36,6 +36,9 @@ struct stream {
uv_pipe_t pipe;
uv_tcp_t tcp;
uv_idle_t idle;
+#ifdef WIN32
+ uv_tty_t tty;
+#endif
} uv;
uv_stream_t *uvstream;
uv_buf_t uvbuf;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4356767cc9..8436ac810e 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -855,7 +855,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
return FAIL;
for (extra = 0, l = line1; l <= line2; l++) {
str = vim_strsave(ml_get(l + extra));
- ml_append(dest + l - line1, str, (colnr_T)0, FALSE);
+ ml_append(dest + l - line1, str, (colnr_T)0, false);
xfree(str);
if (dest < line1)
extra++;
@@ -914,9 +914,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
return FAIL;
- for (l = line1; l <= line2; l++)
- ml_delete(line1 + extra, TRUE);
-
+ for (l = line1; l <= line2; l++) {
+ ml_delete(line1 + extra, true);
+ }
if (!global_busy && num_lines > p_report) {
if (num_lines == 1)
MSG(_("1 line moved"));
@@ -982,7 +982,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
/* need to use vim_strsave() because the line will be unlocked within
* ml_append() */
p = vim_strsave(ml_get(line1));
- ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
+ ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false);
xfree(p);
/* situation 2: skip already copied lines */
@@ -2720,7 +2720,7 @@ static int append_indent = 0; /* autoindent for first line */
void ex_append(exarg_T *eap)
{
char_u *theline;
- int did_undo = FALSE;
+ bool did_undo = false;
linenr_T lnum = eap->line2;
int indent = 0;
char_u *p;
@@ -2808,16 +2808,16 @@ void ex_append(exarg_T *eap)
if (p[0] == NUL)
theline[0] = NUL;
- did_undo = TRUE;
- ml_append(lnum, theline, (colnr_T)0, FALSE);
+ did_undo = true;
+ ml_append(lnum, theline, (colnr_T)0, false);
appended_lines_mark(lnum + (empty ? 1 : 0), 1L);
xfree(theline);
++lnum;
if (empty) {
- ml_delete(2L, FALSE);
- empty = FALSE;
+ ml_delete(2L, false);
+ empty = 0;
}
}
State = NORMAL;
@@ -2862,7 +2862,7 @@ void ex_change(exarg_T *eap)
for (lnum = eap->line2; lnum >= eap->line1; --lnum) {
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
break;
- ml_delete(eap->line1, FALSE);
+ ml_delete(eap->line1, false);
}
/* make sure the cursor is not beyond the end of the file now */
@@ -3181,6 +3181,7 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
subflags->do_ask = false;
subflags->do_error = true;
subflags->do_print = false;
+ subflags->do_list = false;
subflags->do_count = false;
subflags->do_number = false;
subflags->do_ic = kSubHonorOptions;
@@ -3691,10 +3692,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
i = msg_scroll;
msg_scroll = 0; /* truncate msg when
needed */
- msg_no_more = TRUE;
- /* write message same highlighting as for
- * wait_return */
- smsg_attr(HL_ATTR(HLF_R),
+ msg_no_more = true;
+ msg_ext_set_kind("confirm_sub");
+ smsg_attr(HL_ATTR(HLF_R), // Same highlight as wait_return().
_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
msg_no_more = FALSE;
msg_scroll = i;
@@ -3819,7 +3819,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
if (!preview || has_second_delim) {
if (subflags.do_count) {
// prevent accidentally changing the buffer by a function
- save_ma = curbuf->b_p_ma;
curbuf->b_p_ma = false;
sandbox++;
}
@@ -3830,9 +3829,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
sub, sub_firstline, false, p_magic, true);
+ // If getting the substitute string caused an error, don't do
+ // the replacement.
// Don't keep flags set by a recursive call
subflags = subflags_save;
- if (subflags.do_count) {
+ if (aborting() || subflags.do_count) {
curbuf->b_p_ma = save_ma;
if (sandbox > 0) {
sandbox--;
@@ -3982,8 +3983,9 @@ skip:
++lnum;
if (u_savedel(lnum, nmatch_tl) != OK)
break;
- for (i = 0; i < nmatch_tl; ++i)
- ml_delete(lnum, (int)FALSE);
+ for (i = 0; i < nmatch_tl; i++) {
+ ml_delete(lnum, false);
+ }
mark_adjust(lnum, lnum + nmatch_tl - 1,
(long)MAXLNUM, -nmatch_tl, false);
if (subflags.do_ask) {
@@ -5147,10 +5149,11 @@ void fix_help_buffer(void)
}
convert_setup(&vc, NULL, NULL);
- ml_append(lnum, cp, (colnr_T)0, FALSE);
- if (cp != IObuff)
+ ml_append(lnum, cp, (colnr_T)0, false);
+ if (cp != IObuff) {
xfree(cp);
- ++lnum;
+ }
+ lnum++;
}
fclose(fd);
}
@@ -6385,6 +6388,7 @@ void ex_substitute(exarg_T *eap)
}
block_autocmds(); // Disable events during command preview.
+ input_disable_events();
char_u *save_eap = eap->arg;
garray_T save_view;
@@ -6427,6 +6431,7 @@ void ex_substitute(exarg_T *eap)
restore_search_patterns();
win_size_restore(&save_view);
ga_clear(&save_view);
+ input_enable_events();
unblock_autocmds();
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 4806eff1b4..0f69d476f9 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1004,7 +1004,7 @@ return {
},
{
command='function',
- flags=bit.bor(EXTRA, BANG, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN),
addr_type=ADDR_LINES,
func='ex_function',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 4684a1b31d..40ff29d4a8 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1298,7 +1298,12 @@ void dialog_changed(buf_T *buf, bool checkall)
{
char_u buff[DIALOG_MSG_SIZE];
int ret;
- exarg_T ea;
+ // Init ea pseudo-structure, this is needed for the check_overwrite()
+ // function.
+ exarg_T ea = {
+ .append = false,
+ .forceit = false,
+ };
dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
if (checkall) {
@@ -1307,10 +1312,6 @@ void dialog_changed(buf_T *buf, bool checkall)
ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
}
- // Init ea pseudo-structure, this is needed for the check_overwrite()
- // function.
- ea.append = ea.forceit = false;
-
if (ret == VIM_YES) {
if (buf->b_fname != NULL
&& check_overwrite(&ea,
@@ -1952,14 +1953,17 @@ void ex_next(exarg_T *eap)
void ex_argedit(exarg_T *eap)
{
int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
+ // Whether curbuf will be reused, curbuf->b_ffname will be set.
+ bool curbuf_is_reusable = curbuf_reusable();
if (do_arglist(eap->arg, AL_ADD, i) == FAIL) {
return;
}
maketitle();
- if (curwin->w_arg_idx == 0 && (curbuf->b_ml.ml_flags & ML_EMPTY)
- && curbuf->b_ffname == NULL) {
+ if (curwin->w_arg_idx == 0
+ && (curbuf->b_ml.ml_flags & ML_EMPTY)
+ && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
i = 0;
}
// Edit the argument.
@@ -2257,7 +2261,8 @@ static int alist_add_list(int count, char_u **files, int after)
}
for (int i = 0; i < count; i++) {
ARGLIST[after + i].ae_fname = files[i];
- ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i],
+ BLN_LISTED | BLN_CURBUF);
}
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
@@ -3399,13 +3404,18 @@ char_u *getsourceline(int c, void *cookie, int indent)
// Get the next line and concatenate it when it starts with a
// backslash. We always need to read the next line, keep it in
// sp->nextline.
+ // Also check for a comment in between continuation lines: "\ .
sp->nextline = get_one_sourceline(sp);
- if (sp->nextline != NULL && *(p = skipwhite(sp->nextline)) == '\\') {
+ if (sp->nextline != NULL
+ && (*(p = skipwhite(sp->nextline)) == '\\'
+ || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
garray_T ga;
ga_init(&ga, (int)sizeof(char_u), 400);
ga_concat(&ga, line);
- ga_concat(&ga, p + 1);
+ if (*p == '\\') {
+ ga_concat(&ga, p + 1);
+ }
for (;; ) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
@@ -3413,15 +3423,16 @@ char_u *getsourceline(int c, void *cookie, int indent)
break;
}
p = skipwhite(sp->nextline);
- if (*p != '\\') {
+ if (*p == '\\') {
+ // Adjust the growsize to the current length to speed up
+ // concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
+ }
+ ga_concat(&ga, p + 1);
+ } else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') {
break;
}
- // Adjust the growsize to the current length to speed up
- // concatenating many lines.
- if (ga.ga_len > 400) {
- ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
- }
- ga_concat(&ga, p + 1);
}
ga_append(&ga, NUL);
xfree(line);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5b9b4fed12..9c4a3f389a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -78,7 +78,7 @@
static int quitmore = 0;
static int ex_pressedreturn = FALSE;
-/* whether ":lcd" was produced for a session */
+/// Whether ":lcd" or ":tcd" was produced for a session.
static int did_lcd;
typedef struct ucmd {
@@ -839,9 +839,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
sourcing_lnum = current_exception->throw_lnum;
current_exception->throw_name = NULL;
- discard_current_exception(); /* uses IObuff if 'verbose' */
- suppress_errthrow = TRUE;
- force_abort = TRUE;
+ discard_current_exception(); // uses IObuff if 'verbose'
+ suppress_errthrow = true;
+ force_abort = true;
+ msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
if (messages != NULL) {
do {
@@ -2263,8 +2264,11 @@ static char_u * do_one_cmd(char_u **cmdlinep,
need_rethrow = check_cstack = FALSE;
doend:
- if (curwin->w_cursor.lnum == 0) /* can happen with zero line number */
+ // can happen with zero line number
+ if (curwin->w_cursor.lnum == 0) {
curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ }
if (errormsg != NULL && *errormsg != NUL && !did_emsg) {
if (flags & DOCMD_VERBOSE) {
@@ -2440,10 +2444,24 @@ static char_u *find_command(exarg_T *eap, int *full)
}
}
- if (ASCII_ISLOWER(*eap->cmd))
- eap->cmdidx = cmdidxs[CharOrdLow(*eap->cmd)];
- else
- eap->cmdidx = cmdidxs[26];
+ if (ASCII_ISLOWER(eap->cmd[0])) {
+ const int c1 = eap->cmd[0];
+ const int c2 = eap->cmd[1];
+
+ if (command_count != (int)CMD_SIZE) {
+ iemsg((char *)_("E943: Command table needs to be updated, run 'make'"));
+ getout(1);
+ }
+
+ // Use a precomputed index for fast look-up in cmdnames[]
+ // taking into account the first 2 letters of eap->cmd.
+ eap->cmdidx = cmdidxs1[CharOrdLow(c1)];
+ if (ASCII_ISLOWER(c2)) {
+ eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)];
+ }
+ } else {
+ eap->cmdidx = CMD_bang;
+ }
for (; (int)eap->cmdidx < (int)CMD_SIZE;
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1))
@@ -3235,7 +3253,7 @@ const char * set_one_cmd_context(
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
- if (*p_wop != NUL) {
+ if (wop_flags & WOP_TAGFILE) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
@@ -4488,8 +4506,8 @@ static int get_tabpage_arg(exarg_T *eap)
tab_number = 0;
} else {
tab_number = eap->line2;
- if (!unaccept_arg0 && **eap->cmdlinep == '-') {
- --tab_number;
+ if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') {
+ tab_number--;
if (tab_number < unaccept_arg0) {
eap->errmsg = e_invarg;
}
@@ -6903,10 +6921,11 @@ static void ex_resize(exarg_T *eap)
n = 9999;
win_setwidth_win(n, wp);
} else {
- if (*eap->arg == '-' || *eap->arg == '+')
+ if (*eap->arg == '-' || *eap->arg == '+') {
n += curwin->w_height;
- else if (n == 0 && eap->arg[0] == NUL) /* default is very wide */
+ } else if (n == 0 && eap->arg[0] == NUL) { // default is very high
n = 9999;
+ }
win_setheight_win(n, wp);
}
}
@@ -7195,10 +7214,11 @@ static void ex_read(exarg_T *eap)
else
lnum = 1;
if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) {
- ml_delete(lnum, FALSE);
+ ml_delete(lnum, false);
if (curwin->w_cursor.lnum > 1
- && curwin->w_cursor.lnum >= lnum)
- --curwin->w_cursor.lnum;
+ && curwin->w_cursor.lnum >= lnum) {
+ curwin->w_cursor.lnum--;
+ }
deleted_lines_mark(lnum, 1L);
}
}
@@ -7224,7 +7244,7 @@ void free_cd_dir(void)
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab or window).
-void post_chdir(CdScope scope)
+void post_chdir(CdScope scope, bool trigger_dirchanged)
{
// Always overwrite the window-local CWD.
xfree(curwin->w_localdir);
@@ -7264,7 +7284,10 @@ void post_chdir(CdScope scope)
}
shorten_fnames(true);
- do_autocmd_dirchanged(cwd, scope);
+
+ if (trigger_dirchanged) {
+ do_autocmd_dirchanged(cwd, scope);
+ }
}
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
@@ -7323,10 +7346,10 @@ void ex_cd(exarg_T *eap)
break;
}
- if (vim_chdir(new_dir, scope)) {
+ if (vim_chdir(new_dir)) {
EMSG(_(e_failed));
} else {
- post_chdir(scope);
+ post_chdir(scope, true);
// Echo the new current directory if the command was typed.
if (KeyTyped || p_verbose >= 5) {
ex_pwd(eap);
@@ -7787,11 +7810,12 @@ static void ex_redir(exarg_T *eap)
redir_off = FALSE;
}
-/*
- * ":redraw": force redraw
- */
+/// ":redraw": force redraw
static void ex_redraw(exarg_T *eap)
{
+ if (State & CMDPREVIEW) {
+ return; // Ignore :redraw during 'inccommand' preview. #9777
+ }
int r = RedrawingDisabled;
int p = p_lz;
@@ -7820,11 +7844,12 @@ static void ex_redraw(exarg_T *eap)
ui_flush();
}
-/*
- * ":redrawstatus": force redraw of status line(s)
- */
+/// ":redrawstatus": force redraw of status line(s)
static void ex_redrawstatus(exarg_T *eap)
{
+ if (State & CMDPREVIEW) {
+ return; // Ignore :redrawstatus during 'inccommand' preview. #9777
+ }
int r = RedrawingDisabled;
int p = p_lz;
@@ -9130,6 +9155,7 @@ makeopens(
|| put_eol(fd) == FAIL) {
return FAIL;
}
+ did_lcd = true;
}
/* Don't continue in another tab page when doing only the current one
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 7f7851f078..5b2a1c5c51 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -569,10 +569,12 @@ static void discard_exception(except_T *excp, int was_finished)
*/
void discard_current_exception(void)
{
- discard_exception(current_exception, false);
+ 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.
- current_exception = NULL;
need_rethrow = false;
}
@@ -1766,6 +1768,7 @@ void enter_cleanup(cleanup_T *csp)
*/
if (current_exception || need_rethrow) {
csp->exception = current_exception;
+ current_exception = NULL;
} else {
csp->exception = NULL;
if (did_emsg) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8e6fc5ad4f..b16023b0ec 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -214,6 +214,15 @@ static int hislen = 0; /* actual length of history tables */
/// user interrupting highlight function to not interrupt command-line.
static bool getln_interrupted_highlight = false;
+// "compl_match_array" points the currently displayed list of entries in the
+// popup menu. It is NULL when there is no popup menu.
+static pumitem_T *compl_match_array = NULL;
+static int compl_match_arraysize;
+// First column in cmdline of the matched item for completion.
+static int compl_startcol;
+static int compl_selected;
+
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -357,7 +366,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
// redraw the statusline for statuslines that display the current mode
// using the mode() function.
- if (KeyTyped && msg_scrolled == 0) {
+ if (!cmd_silent && msg_scrolled == 0) {
curwin->w_redr_status = true;
redraw_statuslines();
}
@@ -615,8 +624,10 @@ static int command_line_execute(VimState *state, int key)
if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- if (ui_has(kUIWildmenu)) {
- ui_call_wildmenu_hide();
+ if (compl_match_array) {
+ pum_undisplay(true);
+ xfree(compl_match_array);
+ compl_match_array = NULL;
}
if (s->xpc.xp_numfiles != -1) {
(void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
@@ -1378,6 +1389,7 @@ static int command_line_handle_key(CommandLineState *s)
}
}
}
+ ccline.special_char = NUL;
redrawcmd();
return command_line_changed(s);
@@ -1436,6 +1448,9 @@ static int command_line_handle_key(CommandLineState *s)
&& ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
set_cmdspos_cursor();
+ if (ccline.special_char != NUL) {
+ putcmdline(ccline.special_char, ccline.special_shift);
+ }
return command_line_not_changed(s);
@@ -1693,6 +1708,7 @@ static int command_line_handle_key(CommandLineState *s)
putcmdline('^', true);
s->c = get_literal(); // get next (two) character(s)
s->do_abbr = false; // don't do abbreviation now
+ ccline.special_char = NUL;
// may need to remove ^ when composing char was typed
if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
if (ui_has(kUICmdline)) {
@@ -1710,6 +1726,7 @@ static int command_line_handle_key(CommandLineState *s)
s->ignore_drag_release = true;
putcmdline('?', true);
s->c = get_digraph(true);
+ ccline.special_char = NUL;
if (s->c != NUL) {
break;
@@ -3071,15 +3088,13 @@ void putcmdline(int c, int shift)
draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
}
msg_no_more = false;
- } else {
- ccline.special_char = c;
- ccline.special_shift = shift;
- if (ccline.redraw_state != kCmdRedrawAll) {
+ } else if (ccline.redraw_state != kCmdRedrawAll) {
ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift,
ccline.level);
- }
}
cursorcmd();
+ ccline.special_char = c;
+ ccline.special_shift = shift;
ui_cursor_shape();
}
@@ -3097,6 +3112,7 @@ void unputcmdline(void)
}
msg_no_more = false;
cursorcmd();
+ ccline.special_char = NUL;
ui_cursor_shape();
}
@@ -3457,6 +3473,10 @@ void redrawcmd(void)
set_cmdspos_cursor();
+ if (ccline.special_char != NUL) {
+ putcmdline(ccline.special_char, ccline.special_shift);
+ }
+
/*
* An emsg() before may have set msg_scroll. This is used in normal mode,
* in cmdline mode we can reset them now.
@@ -3746,13 +3766,12 @@ ExpandOne (
else
findex = -1;
}
- if (p_wmnu) {
- if (ui_has(kUIWildmenu)) {
- ui_call_wildmenu_select(findex);
- } else {
- win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
- findex, cmd_showtail);
- }
+ if (compl_match_array) {
+ compl_selected = findex;
+ cmdline_pum_display(false);
+ } else if (p_wmnu) {
+ win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
+ findex, cmd_showtail);
}
if (findex == -1) {
return vim_strsave(orig_save);
@@ -4069,6 +4088,12 @@ void tilde_replace(char_u *orig_pat, int num_files, char_u **files)
}
}
+void cmdline_pum_display(bool changed_array)
+{
+ pum_display(compl_match_array, compl_match_arraysize, compl_selected,
+ changed_array, compl_startcol);
+}
+
/*
* Show all matches for completion on the command line.
* Returns EXPAND_NOTHING when the character that triggered expansion should
@@ -4102,12 +4127,28 @@ static int showmatches(expand_T *xp, int wildmenu)
showtail = cmd_showtail;
}
- if (ui_has(kUIWildmenu)) {
- Array args = ARRAY_DICT_INIT;
+ bool compl_use_pum = (ui_has(kUICmdline)
+ ? ui_has(kUIPopupmenu)
+ : wildmenu && (wop_flags & WOP_PUM))
+ || ui_has(kUIWildmenu);
+
+ if (compl_use_pum) {
+ compl_match_arraysize = num_files;
+ compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T));
for (i = 0; i < num_files; i++) {
- ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i])));
+ compl_match_array[i].pum_text = L_SHOWFILE(i);
}
- ui_call_wildmenu_show(args);
+ ssize_t offset = showtail ? sm_gettail(xp->xp_pattern)-xp->xp_pattern : 0;
+ if (ui_has(kUICmdline)) {
+ compl_startcol = ccline.cmdpos - strnlen((char *)xp->xp_pattern+offset,
+ xp->xp_pattern_len-offset);
+ } else {
+ compl_startcol = ccline.cmdspos
+ - mb_string2cells_len(xp->xp_pattern+offset,
+ xp->xp_pattern_len-offset);
+ }
+ compl_selected = -1;
+ cmdline_pum_display(true);
return EXPAND_OK;
}
@@ -6069,9 +6110,9 @@ static int open_cmdwin(void)
do {
if (++i == hislen)
i = 0;
- if (history[histtype][i].hisstr != NULL)
- ml_append(lnum++, history[histtype][i].hisstr,
- (colnr_T)0, FALSE);
+ if (history[histtype][i].hisstr != NULL) {
+ ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false);
+ }
} while (i != hisidx[histtype]);
}
}
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index ee775bab4a..05611fb8ba 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -687,20 +687,24 @@ char_u *vim_findfile(void *search_ctx_arg)
if (!vim_isAbsName(stackp->ffs_fix_path)
&& search_ctx->ffsc_start_dir) {
if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, search_ctx->ffsc_start_dir);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
}
// append the fix part of the search path
if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, stackp->ffs_fix_path);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
@@ -715,6 +719,7 @@ char_u *vim_findfile(void *search_ctx_arg)
if (*p > 0) {
(*p)--;
if (len + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
file_path[len++] = '*';
@@ -743,6 +748,7 @@ char_u *vim_findfile(void *search_ctx_arg)
while (*rest_of_wildcards
&& !vim_ispathsep(*rest_of_wildcards)) {
if (len + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
file_path[len++] = *rest_of_wildcards++;
@@ -792,10 +798,12 @@ char_u *vim_findfile(void *search_ctx_arg)
// prepare the filename to be checked for existence below
if (STRLEN(stackp->ffs_filearray[i]) + 1
+ STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, stackp->ffs_filearray[i]);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, search_ctx->ffsc_file_to_search);
@@ -964,7 +972,6 @@ char_u *vim_findfile(void *search_ctx_arg)
}
fail:
- ff_free_stack_element(stackp);
xfree(file_path);
return NULL;
}
@@ -1634,7 +1641,7 @@ int vim_chdirfile(char_u *fname)
}
/// Change directory to "new_dir". Search 'cdpath' for relative directory names.
-int vim_chdir(char_u *new_dir, CdScope scope)
+int vim_chdir(char_u *new_dir)
{
char_u *dir_name = find_directory_in_path(new_dir, STRLEN(new_dir),
FNAME_MESS, curbuf->b_ffname);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 08a4b76783..55463bdf30 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include "nvim/vim.h"
+#include "nvim/api/private/handle.h"
#include "nvim/ascii.h"
#include "nvim/fileio.h"
#include "nvim/buffer.h"
@@ -47,6 +48,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -308,29 +310,30 @@ readfile (
#ifdef UNIX
int swap_mode = -1; /* protection bits for swap file */
#endif
- int fileformat = 0; /* end-of-line format */
- int keep_fileformat = FALSE;
+ int fileformat = 0; // end-of-line format
+ bool keep_fileformat = false;
+ FileInfo file_info;
int file_readonly;
linenr_T skip_count = 0;
linenr_T read_count = 0;
int msg_save = msg_scroll;
linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of
// last read was missing the eol
- int file_rewind = false;
+ bool file_rewind = false;
int can_retry;
- linenr_T conv_error = 0; /* line nr with conversion error */
- linenr_T illegal_byte = 0; /* line nr with illegal byte */
- int keep_dest_enc = FALSE; /* don't retry when char doesn't fit
- in destination encoding */
+ linenr_T conv_error = 0; // line nr with conversion error
+ linenr_T illegal_byte = 0; // line nr with illegal byte
+ bool keep_dest_enc = false; // don't retry when char doesn't fit
+ // in destination encoding
int bad_char_behavior = BAD_REPLACE;
/* BAD_KEEP, BAD_DROP or character to
* replace with */
char_u *tmpname = NULL; /* name of 'charconvert' output file */
int fio_flags = 0;
- char_u *fenc; /* fileencoding to use */
- int fenc_alloced; /* fenc_next is in allocated memory */
- char_u *fenc_next = NULL; /* next item in 'fencs' or NULL */
- int advance_fenc = FALSE;
+ char_u *fenc; // fileencoding to use
+ bool fenc_alloced; // fenc_next is in allocated memory
+ char_u *fenc_next = NULL; // next item in 'fencs' or NULL
+ bool advance_fenc = false;
long real_size = 0;
# ifdef USE_ICONV
iconv_t iconv_fd = (iconv_t)-1; /* descriptor for iconv() or -1 */
@@ -481,7 +484,6 @@ readfile (
if (newfile && !read_stdin && !read_buffer && !read_fifo) {
// Remember time of file.
- FileInfo file_info;
if (os_fileinfo((char *)fname, &file_info)) {
buf_store_file_info(curbuf, &file_info);
curbuf->b_mtime_read = curbuf->b_mtime;
@@ -627,13 +629,30 @@ readfile (
// Set swap file protection bits after creating it.
if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL
&& curbuf->b_ml.ml_mfp->mf_fname != NULL) {
- (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname,
- (long)swap_mode);
+ const char *swap_fname = (const char *)curbuf->b_ml.ml_mfp->mf_fname;
+
+ // If the group-read bit is set but not the world-read bit, then
+ // the group must be equal to the group of the original file. If
+ // we can't make that happen then reset the group-read bit. This
+ // avoids making the swap file readable to more users when the
+ // primary group of the user is too permissive.
+ if ((swap_mode & 044) == 040) {
+ FileInfo swap_info;
+
+ if (os_fileinfo(swap_fname, &swap_info)
+ && file_info.stat.st_gid != swap_info.stat.st_gid
+ && os_fchown(curbuf->b_ml.ml_mfp->mf_fd, -1, file_info.stat.st_gid)
+ == -1) {
+ swap_mode &= 0600;
+ }
+ }
+
+ (void)os_setperm(swap_fname, swap_mode);
}
#endif
}
- /* If "Quit" selected at ATTENTION dialog, don't load the file */
+ // If "Quit" selected at ATTENTION dialog, don't load the file.
if (swap_exists_action == SEA_QUIT) {
if (!read_buffer && !read_stdin)
close(fd);
@@ -747,11 +766,11 @@ readfile (
*/
if (eap != NULL && eap->force_enc != 0) {
fenc = enc_canonize(eap->cmd + eap->force_enc);
- fenc_alloced = TRUE;
- keep_dest_enc = TRUE;
+ fenc_alloced = true;
+ keep_dest_enc = true;
} else if (curbuf->b_p_bin) {
- fenc = (char_u *)""; /* binary: don't convert */
- fenc_alloced = FALSE;
+ fenc = (char_u *)""; // binary: don't convert
+ fenc_alloced = false;
} else if (curbuf->b_help) {
// Help files are either utf-8 or latin1. Try utf-8 first, if this
// fails it must be latin1.
@@ -762,12 +781,12 @@ readfile (
fenc_alloced = false;
} else if (*p_fencs == NUL) {
- fenc = curbuf->b_p_fenc; /* use format from buffer */
- fenc_alloced = FALSE;
+ fenc = curbuf->b_p_fenc; // use format from buffer
+ fenc_alloced = false;
} else {
fenc_next = p_fencs; /* try items in 'fileencodings' */
fenc = next_fenc(&fenc_next);
- fenc_alloced = TRUE;
+ fenc_alloced = true;
}
/*
@@ -800,10 +819,11 @@ retry:
error = true;
goto failed;
}
- /* Delete the previously read lines. */
- while (lnum > from)
- ml_delete(lnum--, FALSE);
- file_rewind = FALSE;
+ // Delete the previously read lines.
+ while (lnum > from) {
+ ml_delete(lnum--, false);
+ }
+ file_rewind = false;
if (set_options) {
curbuf->b_p_bomb = FALSE;
curbuf->b_start_bomb = FALSE;
@@ -815,9 +835,9 @@ retry:
* When retrying with another "fenc" and the first time "fileformat"
* will be reset.
*/
- if (keep_fileformat)
- keep_fileformat = FALSE;
- else {
+ if (keep_fileformat) {
+ keep_fileformat = false;
+ } else {
if (eap != NULL && eap->force_ff != 0) {
fileformat = get_fileformat_force(curbuf, eap);
try_unix = try_dos = try_mac = FALSE;
@@ -841,7 +861,7 @@ retry:
/*
* Try the next entry in 'fileencodings'.
*/
- advance_fenc = FALSE;
+ advance_fenc = false;
if (eap != NULL && eap->force_enc != 0) {
/* Conversion given with "++cc=" wasn't possible, read
@@ -851,7 +871,7 @@ retry:
if (fenc_alloced)
xfree(fenc);
fenc = (char_u *)"";
- fenc_alloced = FALSE;
+ fenc_alloced = false;
} else {
if (fenc_alloced)
xfree(fenc);
@@ -860,7 +880,7 @@ retry:
fenc_alloced = (fenc_next != NULL);
} else {
fenc = (char_u *)"";
- fenc_alloced = FALSE;
+ fenc_alloced = false;
}
}
if (tmpname != NULL) {
@@ -926,8 +946,8 @@ retry:
if (tmpname == NULL) {
tmpname = readfile_charconvert(fname, fenc, &fd);
if (tmpname == NULL) {
- /* Conversion failed. Try another one. */
- advance_fenc = TRUE;
+ // Conversion failed. Try another one.
+ advance_fenc = true;
if (fd < 0) {
/* Re-opening the original file failed! */
EMSG(_("E202: Conversion made file unreadable!"));
@@ -945,7 +965,7 @@ retry:
) {
/* Conversion wanted but we can't.
* Try the next conversion in 'fileencodings' */
- advance_fenc = TRUE;
+ advance_fenc = true;
goto retry;
}
}
@@ -1180,14 +1200,14 @@ retry:
if (fio_flags == FIO_UCSBOM) {
if (ccname == NULL) {
- /* No BOM detected: retry with next encoding. */
- advance_fenc = TRUE;
+ // No BOM detected: retry with next encoding.
+ advance_fenc = true;
} else {
/* BOM detected: set "fenc" and jump back */
if (fenc_alloced)
xfree(fenc);
fenc = ccname;
- fenc_alloced = FALSE;
+ fenc_alloced = false;
}
/* retry reading without getting new bytes or rewinding */
skip_read = TRUE;
@@ -1512,9 +1532,9 @@ rewind_retry:
did_iconv = TRUE;
else
# endif
- /* use next item from 'fileencodings' */
- advance_fenc = TRUE;
- file_rewind = TRUE;
+ // use next item from 'fileencodings'
+ advance_fenc = true;
+ file_rewind = true;
goto retry;
}
}
@@ -1648,8 +1668,8 @@ rewind_retry:
fileformat = EOL_UNIX;
if (set_options)
set_fileformat(EOL_UNIX, OPT_LOCAL);
- file_rewind = TRUE;
- keep_fileformat = TRUE;
+ file_rewind = true;
+ keep_fileformat = true;
goto retry;
}
ff_error = EOL_DOS;
@@ -1760,8 +1780,8 @@ failed:
if (!recoverymode) {
/* need to delete the last line, which comes from the empty buffer */
if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) {
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
- --linecnt;
+ ml_delete(curbuf->b_ml.ml_line_count, false);
+ linecnt--;
}
linecnt = curbuf->b_ml.ml_line_count - linecnt;
if (filesize == 0)
@@ -2762,7 +2782,7 @@ buf_write (
backup_ext = p_bex;
if (backup_copy) {
- char_u *wp;
+ char_u *wp;
int some_error = false;
char_u *dirp;
char_u *rootname;
@@ -3456,7 +3476,7 @@ restore_backup:
// copy the file.
if (os_copy((char *)backup, (char *)fname, UV_FS_COPYFILE_FICLONE)
== 0) {
- end = 1;
+ end = 1; // success
}
} else {
if (vim_rename(backup, fname) == 0) {
@@ -3591,9 +3611,11 @@ restore_backup:
/*
* Remove the backup unless 'backup' option is set
*/
- if (!p_bk && backup != NULL && os_remove((char *)backup) != 0)
+ if (!p_bk && backup != NULL
+ && !write_info.bw_conv_error
+ && os_remove((char *)backup) != 0) {
EMSG(_("E207: Can't delete backup file"));
-
+ }
goto nofail;
@@ -4812,9 +4834,9 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
/* Copy the lines in "frombuf" to "tobuf". */
curbuf = tobuf;
- for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum) {
- p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE));
- if (ml_append(lnum - 1, p, 0, FALSE) == FAIL) {
+ for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) {
+ p = vim_strsave(ml_get_buf(frombuf, lnum, false));
+ if (ml_append(lnum - 1, p, 0, false) == FAIL) {
xfree(p);
retval = FAIL;
break;
@@ -4825,13 +4847,14 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
/* Delete all the lines in "frombuf". */
if (retval != FAIL) {
curbuf = frombuf;
- for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; --lnum)
- if (ml_delete(lnum, FALSE) == FAIL) {
- /* Oops! We could try putting back the saved lines, but that
- * might fail again... */
+ for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) {
+ if (ml_delete(lnum, false) == FAIL) {
+ // Oops! We could try putting back the saved lines, but that
+ // might fail again...
retval = FAIL;
break;
}
+ }
}
curbuf = tbuf;
@@ -5509,39 +5532,48 @@ static void au_del_cmd(AutoCmd *ac)
au_need_clean = true;
}
-/*
- * Cleanup autocommands and patterns that have been deleted.
- * This is only done when not executing autocommands.
- */
+/// Cleanup autocommands and patterns that have been deleted.
+/// This is only done when not executing autocommands.
static void au_cleanup(void)
{
AutoPat *ap, **prev_ap;
AutoCmd *ac, **prev_ac;
event_T event;
- if (autocmd_busy || !au_need_clean)
+ if (autocmd_busy || !au_need_clean) {
return;
+ }
- /* loop over all events */
+ // Loop over all events.
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1)) {
- /* loop over all autocommand patterns */
+ // Loop over all autocommand patterns.
prev_ap = &(first_autopat[(int)event]);
for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
- /* loop over all commands for this pattern */
+ // Loop over all commands for this pattern.
prev_ac = &(ap->cmds);
+ bool has_cmd = false;
+
for (ac = *prev_ac; ac != NULL; ac = *prev_ac) {
- /* remove the command if the pattern is to be deleted or when
- * the command has been marked for deletion */
+ // Remove the command if the pattern is to be deleted or when
+ // the command has been marked for deletion.
if (ap->pat == NULL || ac->cmd == NULL) {
*prev_ac = ac->next;
xfree(ac->cmd);
xfree(ac);
- } else
+ } else {
+ has_cmd = true;
prev_ac = &(ac->next);
+ }
+ }
+
+ if (ap->pat != NULL && !has_cmd) {
+ // Pattern was not marked for deletion, but all of its commands were.
+ // So mark the pattern for deletion.
+ au_remove_pat(ap);
}
- /* remove the pattern if it has been marked for deletion */
+ // Remove the pattern if it has been marked for deletion.
if (ap->pat == NULL) {
if (ap->next == NULL) {
if (prev_ap == &(first_autopat[(int)event])) {
@@ -5555,12 +5587,13 @@ static void au_cleanup(void)
*prev_ap = ap->next;
vim_regfree(ap->reg_prog);
xfree(ap);
- } else
+ } else {
prev_ap = &(ap->next);
+ }
}
}
- au_need_clean = FALSE;
+ au_need_clean = false;
}
/*
@@ -5965,20 +5998,36 @@ void do_autocmd(char_u *arg_in, int forceit)
}
}
- // Check for "once" flag.
cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "once", 4) == 0
- && ascii_iswhite(cmd[4])) {
- once = true;
- cmd = skipwhite(cmd + 4);
- }
+ for (size_t i = 0; i < 2; i++) {
+ if (*cmd != NUL) {
+ // Check for "++once" flag.
+ if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
+ if (once) {
+ EMSG2(_(e_duparg2), "++once");
+ }
+ once = true;
+ cmd = skipwhite(cmd + 6);
+ }
- // Check for "nested" flag.
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
- && ascii_iswhite(cmd[6])) {
- nested = true;
- cmd = skipwhite(cmd + 6);
+ // Check for "++nested" flag.
+ if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) {
+ if (nested) {
+ EMSG2(_(e_duparg2), "++nested");
+ }
+ nested = true;
+ cmd = skipwhite(cmd + 8);
+ }
+
+ // Check for the old (deprecated) "nested" flag.
+ if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
+ if (nested) {
+ EMSG2(_(e_duparg2), "nested");
+ }
+ nested = true;
+ cmd = skipwhite(cmd + 6);
+ }
+ }
}
// Find the start of the commands.
@@ -6393,21 +6442,17 @@ bool check_nomodeline(char_u **argp)
return true;
}
-/*
- * Prepare for executing autocommands for (hidden) buffer "buf".
- * Search for a visible window containing the current buffer. If there isn't
- * one then use "aucmd_win".
- * Set "curbuf" and "curwin" to match "buf".
- */
-void
-aucmd_prepbuf (
- aco_save_T *aco, /* structure to save values in */
- buf_T *buf /* new curbuf */
-)
+/// Prepare for executing autocommands for (hidden) buffer `buf`.
+/// If the current buffer is not in any visible window, put it in a temporary
+/// floating window `aucmd_win`.
+/// Set `curbuf` and `curwin` to match `buf`.
+///
+/// @param aco structure to save values in
+/// @param buf new curbuf
+void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
{
- win_T *win;
- int save_ea;
- int save_acd;
+ win_T *win;
+ bool need_append = true; // Append `aucmd_win` to the window list.
/* Find a window that is for the new buffer */
if (buf == curbuf) { /* be quick when buf is curbuf */
@@ -6422,9 +6467,10 @@ aucmd_prepbuf (
}
}
- /* Allocate "aucmd_win" when needed. */
+ // Allocate the `aucmd_win` dummy floating window.
if (win == NULL && aucmd_win == NULL) {
win_alloc_aucmd_win();
+ need_append = false;
}
if (win == NULL && aucmd_win_used)
/* Strange recursive autocommand, fall back to using the current
@@ -6432,6 +6478,7 @@ aucmd_prepbuf (
win = curwin;
aco->save_curwin = curwin;
+ aco->save_prevwin = prevwin;
aco->save_curbuf = curbuf;
if (win != NULL) {
/* There is a window for "buf" in the current tab page, make it the
@@ -6458,21 +6505,16 @@ aucmd_prepbuf (
aco->globaldir = globaldir;
globaldir = NULL;
-
- /* Split the current window, put the aucmd_win in the upper half.
- * We don't want the BufEnter or WinEnter autocommands. */
- block_autocmds();
- make_snapshot(SNAP_AUCMD_IDX);
- save_ea = p_ea;
- p_ea = false;
-
- /* Prevent chdir() call in win_enter_ext(), through do_autochdir(). */
- save_acd = p_acd;
+ block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
+ if (need_append) {
+ win_append(lastwin, aucmd_win);
+ handle_register_window(aucmd_win);
+ win_config_float(aucmd_win, aucmd_win->w_float_config);
+ }
+ // Prevent chdir() call in win_enter_ext(), through do_autochdir()
+ int save_acd = p_acd;
p_acd = false;
-
- (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
- (void)win_comp_pos(); /* recompute window positions */
- p_ea = save_ea;
+ win_enter(aucmd_win, false);
p_acd = save_acd;
unblock_autocmds();
curwin = aucmd_win;
@@ -6488,8 +6530,6 @@ aucmd_prepbuf (
/// @param aco structure holding saved values
void aucmd_restbuf(aco_save_T *aco)
{
- int dummy;
-
if (aco->use_aucmd_win) {
curbuf->b_nwindows--;
// Find "aucmd_win", it can't be closed, but it may be in another tab page.
@@ -6508,9 +6548,14 @@ void aucmd_restbuf(aco_save_T *aco)
}
win_found:
- // Remove the window and frame from the tree of frames.
- (void)winframe_remove(curwin, &dummy, NULL);
win_remove(curwin, NULL);
+ handle_unregister_window(curwin);
+ if (curwin->w_grid.chars != NULL) {
+ ui_comp_remove_grid(&curwin->w_grid);
+ ui_call_win_hide(curwin->w_grid.handle);
+ grid_free(&curwin->w_grid);
+ }
+
aucmd_win_used = false;
last_status(false); // may need to remove last status line
@@ -6519,8 +6564,6 @@ win_found:
close_tabpage(curtab);
}
- restore_snapshot(SNAP_AUCMD_IDX, false);
- (void)win_comp_pos(); // recompute window positions
unblock_autocmds();
if (win_valid(aco->save_curwin)) {
@@ -6529,6 +6572,8 @@ win_found:
// Hmm, original window disappeared. Just use the first one.
curwin = firstwin;
}
+ prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
+ : firstwin; // window disappeared?
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;
@@ -6561,6 +6606,8 @@ win_found:
}
curwin = aco->save_curwin;
+ prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
+ : firstwin; // window disappeared?
curbuf = curwin->w_buffer;
// In case the autocommand moves the cursor to a position that does not
// exist in curbuf
@@ -6956,6 +7003,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
do_cmdline(NULL, getnextac, (void *)&patcmd,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+ reset_lnums(); // restore cursor and topline, unless they were changed
+
if (eap != NULL) {
(void)set_cmdarg(NULL, save_cmdarg);
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 8db4b89806..a6011ec414 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -23,6 +23,7 @@ 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
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index 6e5e47c060..d3b600a40c 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -205,10 +205,15 @@
#endif
#ifdef DEFINE_FUNC_ATTRIBUTES
+/// Non-deferred API function.
# define FUNC_API_ASYNC
+/// Internal C function not exposed in the RPC API.
# define FUNC_API_NOEXPORT
+/// API function not exposed in VimL/eval.
# define FUNC_API_REMOTE_ONLY
+/// API function introduced at the given API level.
# define FUNC_API_SINCE(X)
+/// API function deprecated since the given API level.
# define FUNC_API_DEPRECATED_SINCE(X)
# define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC
# define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x)
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index cb566d46ca..7859d7c71a 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -28,34 +28,41 @@ local lastchar = nil
local i
local cmd
local first = true
-local prevfirstchar = nil
local byte_a = string.byte('a')
local byte_z = string.byte('z')
+local a_to_z = byte_z - byte_a + 1
-local cmdidxs = string.format([[
-static const cmdidx_T cmdidxs[%u] = {
-]], byte_z - byte_a + 2)
+-- Table giving the index of the first command in cmdnames[] to lookup
+-- based on the first letter of a command.
+local cmdidxs1_out = string.format([[
+static const uint16_t cmdidxs1[%u] = {
+]], a_to_z)
+-- Table giving the index of the first command in cmdnames[] to lookup
+-- based on the first 2 letters of a command.
+-- Values in cmdidxs2[c1][c2] are relative to cmdidxs1[c1] so that they
+-- fit in a byte.
+local cmdidxs2_out = string.format([[
+static const char_u cmdidxs2[%u][%u] = {
+/* a b c d e f g h i j k l m n o p q r s t u v w x y z */
+
+]], a_to_z, a_to_z)
enumfile:write([[
typedef enum CMD_index {
]])
defsfile:write(string.format([[
+static const int command_count = %u;
+]], #defs))
+defsfile:write(string.format([[
static CommandDefinition cmdnames[%u] = {
]], #defs))
+local cmds, cmdidxs1, cmdidxs2 = {}, {}, {}
for i, cmd in ipairs(defs) do
local enumname = cmd.enum or ('CMD_' .. cmd.command)
- firstchar = string.byte(cmd.command)
- if firstchar ~= prevfirstchar then
- if (not prevfirstchar
- or (byte_a <= firstchar and firstchar <= byte_z)
- or (byte_a <= prevfirstchar and prevfirstchar <= byte_z)) then
- if not first then
- cmdidxs = cmdidxs .. ',\n'
- end
- cmdidxs = cmdidxs .. ' ' .. enumname
- end
- prevfirstchar = firstchar
+ local byte_cmd = cmd.command:sub(1, 1):byte()
+ if byte_a <= byte_cmd and byte_cmd <= byte_z then
+ table.insert(cmds, cmd.command)
end
if first then
first = false
@@ -71,6 +78,35 @@ for i, cmd in ipairs(defs) do
.cmd_addr_type = %i
}]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type))
end
+for i = #cmds, 1, -1 do
+ local cmd = cmds[i]
+ -- First and second characters of the command
+ local c1 = cmd:sub(1, 1)
+ cmdidxs1[c1] = i - 1
+ if cmd:len() >= 2 then
+ local c2 = cmd:sub(2, 2)
+ local byte_c2 = string.byte(c2)
+ if byte_a <= byte_c2 and byte_c2 <= byte_z then
+ if not cmdidxs2[c1] then
+ cmdidxs2[c1] = {}
+ end
+ cmdidxs2[c1][c2] = i - 1
+ end
+ end
+end
+for i = byte_a, byte_z do
+ local c1 = string.char(i)
+ cmdidxs1_out = cmdidxs1_out .. ' /* ' .. c1 .. ' */ ' .. cmdidxs1[c1] .. ',\n'
+ cmdidxs2_out = cmdidxs2_out .. ' /* ' .. c1 .. ' */ {'
+ for j = byte_a, byte_z do
+ local c2 = string.char(j)
+ cmdidxs2_out = cmdidxs2_out ..
+ ((cmdidxs2[c1] and cmdidxs2[c1][c2])
+ and string.format('%3d', cmdidxs2[c1][c2] - cmdidxs1[c1])
+ or ' 0') .. ','
+ end
+ cmdidxs2_out = cmdidxs2_out .. ' },\n'
+end
defsfile:write([[
};
@@ -81,8 +117,5 @@ enumfile:write([[
CMD_USER_BUF = -2
} cmdidx_T;
]])
-cmdidxs = cmdidxs .. [[
-
-};
-]]
-defsfile:write(cmdidxs)
+defsfile:write(cmdidxs1_out .. '};\n')
+defsfile:write(cmdidxs2_out .. '};\n')
diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua
index 66430ba26e..3130cecd82 100644
--- a/src/nvim/generators/gen_unicode_tables.lua
+++ b/src/nvim/generators/gen_unicode_tables.lua
@@ -17,7 +17,7 @@
-- which don't have ambiguous or double width, and emoji_all has all Emojis.
if arg[1] == '--help' then
print('Usage:')
- print(' genunicodetables.lua unicode/ unicode_tables.generated.h')
+ print(' gen_unicode_tables.lua unicode/ unicode_tables.generated.h')
os.exit(0)
end
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 139bf7b802..ec14803a2e 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1287,9 +1287,9 @@ openscript (
oldcurscript = curscript;
do {
- update_topline_cursor(); /* update cursor position and topline */
- normal_cmd(&oa, FALSE); /* execute one command */
- vpeekc(); /* check for end of file */
+ update_topline_cursor(); // update cursor position and topline
+ normal_cmd(&oa, false); // execute one command
+ vpeekc(); // check for end of file
} while (scriptin[oldcurscript] != NULL);
State = save_State;
@@ -1425,49 +1425,54 @@ int vgetc(void)
/* a keypad or special function key was not mapped, use it like
* its ASCII equivalent */
switch (c) {
- case K_KPLUS: c = '+'; break;
- case K_KMINUS: c = '-'; break;
- case K_KDIVIDE: c = '/'; break;
- case K_KMULTIPLY: c = '*'; break;
- case K_KENTER: c = CAR; break;
- case K_KPOINT:
- c = '.'; break;
- case K_K0: c = '0'; break;
- case K_K1: c = '1'; break;
- case K_K2: c = '2'; break;
- case K_K3: c = '3'; break;
- case K_K4: c = '4'; break;
- case K_K5: c = '5'; break;
- case K_K6: c = '6'; break;
- case K_K7: c = '7'; break;
- case K_K8: c = '8'; break;
- case K_K9: c = '9'; break;
-
- case K_XHOME:
- case K_ZHOME: if (mod_mask == MOD_MASK_SHIFT) {
- c = K_S_HOME;
- mod_mask = 0;
- } else if (mod_mask == MOD_MASK_CTRL) {
- c = K_C_HOME;
- mod_mask = 0;
- } else
- c = K_HOME;
- break;
- case K_XEND:
- case K_ZEND: if (mod_mask == MOD_MASK_SHIFT) {
- c = K_S_END;
- mod_mask = 0;
- } else if (mod_mask == MOD_MASK_CTRL) {
- c = K_C_END;
- mod_mask = 0;
- } else
- c = K_END;
- break;
+ case K_KPLUS: c = '+'; break;
+ case K_KMINUS: c = '-'; break;
+ case K_KDIVIDE: c = '/'; break;
+ case K_KMULTIPLY: c = '*'; break;
+ case K_KENTER: c = CAR; break;
+ case K_KPOINT: c = '.'; break;
+ case K_KCOMMA: c = ','; break;
+ case K_KEQUAL: c = '='; break;
+ case K_K0: c = '0'; break;
+ case K_K1: c = '1'; break;
+ case K_K2: c = '2'; break;
+ case K_K3: c = '3'; break;
+ case K_K4: c = '4'; break;
+ case K_K5: c = '5'; break;
+ case K_K6: c = '6'; break;
+ case K_K7: c = '7'; break;
+ case K_K8: c = '8'; break;
+ case K_K9: c = '9'; break;
+
+ case K_XHOME:
+ case K_ZHOME:
+ if (mod_mask == MOD_MASK_SHIFT) {
+ c = K_S_HOME;
+ mod_mask = 0;
+ } else if (mod_mask == MOD_MASK_CTRL) {
+ c = K_C_HOME;
+ mod_mask = 0;
+ } else {
+ c = K_HOME;
+ }
+ break;
+ case K_XEND:
+ case K_ZEND:
+ if (mod_mask == MOD_MASK_SHIFT) {
+ c = K_S_END;
+ mod_mask = 0;
+ } else if (mod_mask == MOD_MASK_CTRL) {
+ c = K_C_END;
+ mod_mask = 0;
+ } else {
+ c = K_END;
+ }
+ break;
- case K_XUP: c = K_UP; break;
- case K_XDOWN: c = K_DOWN; break;
- case K_XLEFT: c = K_LEFT; break;
- case K_XRIGHT: c = K_RIGHT; break;
+ case K_XUP: c = K_UP; break;
+ case K_XDOWN: c = K_DOWN; break;
+ case K_XLEFT: c = K_LEFT; break;
+ case K_XRIGHT: c = K_RIGHT; break;
}
/* For a multi-byte character get all the bytes and return the
@@ -2265,18 +2270,25 @@ static int vgetorpeek(int advance)
// that has a <Nop> RHS.
timedout = false;
}
+
+ long wait_time = 0;
+
+ if (advance) {
+ if (typebuf.tb_len == 0
+ || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) {
+ // blocking wait
+ wait_time = -1L;
+ } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) {
+ wait_time = p_ttm;
+ } else {
+ wait_time = p_tm;
+ }
+ }
+
wait_tb_len = typebuf.tb_len;
c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len,
- typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1,
- !advance
- ? 0
- : ((typebuf.tb_len == 0
- || !(p_timeout || (p_ttimeout
- && keylen == KEYLEN_PART_KEY)))
- ? -1L
- : ((keylen == KEYLEN_PART_KEY && p_ttm >= 0)
- ? p_ttm
- : p_tm)));
+ typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1,
+ wait_time);
if (i != 0)
pop_showcmd();
@@ -2490,286 +2502,334 @@ int fix_input_buffer(char_u *buf, int len)
return len;
}
-/*
- * map[!] : show all key mappings
- * map[!] {lhs} : show key mapping for {lhs}
- * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
- * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
- * unmap[!] {lhs} : remove key mapping for {lhs}
- * abbr : show all abbreviations
- * abbr {lhs} : show abbreviations for {lhs}
- * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
- * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
- * unabbr {lhs} : remove abbreviation for {lhs}
- *
- * maptype: 0 for :map, 1 for :unmap, 2 for noremap.
- *
- * arg is pointer to any arguments. Note: arg cannot be a read-only string,
- * it will be modified.
- *
- * for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
- * for :map! mode is INSERT + CMDLINE
- * for :cmap mode is CMDLINE
- * for :imap mode is INSERT
- * for :lmap mode is LANGMAP
- * for :nmap mode is NORMAL
- * for :vmap mode is VISUAL + SELECTMODE
- * for :xmap mode is VISUAL
- * for :smap mode is SELECTMODE
- * for :omap mode is OP_PENDING
- * for :tmap mode is TERM_FOCUS
- *
- * for :abbr mode is INSERT + CMDLINE
- * for :iabbr mode is INSERT
- * for :cabbr mode is CMDLINE
- *
- * Return 0 for success
- * 1 for invalid arguments
- * 2 for no match
- * 4 for out of mem (deprecated, WON'T HAPPEN)
- * 5 for entry not unique
- */
-int
-do_map (
- int maptype,
- char_u *arg,
- int mode,
- int abbrev /* not a mapping but an abbreviation */
-)
+/// Replace termcodes in the given LHS and RHS and store the results into the
+/// `lhs` and `rhs` of the given @ref MapArguments struct.
+///
+/// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs`
+/// will hold a copy of the given `orig_rhs`.
+///
+/// The `*_len` variables will be set appropriately. If the length of
+/// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the
+/// original larger length and `lhs` will be truncated.
+///
+/// If RHS is equal to "<Nop>", `rhs` will be the empty string, `rhs_len`
+/// will be zero, and `rhs_is_noop` will be set to true.
+///
+/// Any memory allocated by @ref replace_termcodes is freed before this function
+/// returns.
+///
+/// @param[in] orig_lhs Original mapping LHS, with characters to replace.
+/// @param[in] orig_lhs_len `strlen` of orig_lhs.
+/// @param[in] orig_rhs Original mapping RHS, with characters to replace.
+/// @param[in] orig_rhs_len `strlen` of orig_rhs.
+/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
+/// @param[out] mapargs MapArguments struct holding the replaced strings.
+void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len,
+ const char_u *orig_rhs, const size_t orig_rhs_len,
+ int cpo_flags, MapArguments *mapargs)
{
- char_u *keys;
- mapblock_T *mp, **mpp;
- char_u *rhs;
- char_u *p;
- int n;
- int len = 0; /* init for GCC */
- int hasarg;
- int haskey;
- int did_it = FALSE;
- int did_local = FALSE;
- int round;
- char_u *keys_buf = NULL;
- char_u *arg_buf = NULL;
- int retval = 0;
- int do_backslash;
- int hash;
- int new_hash;
- mapblock_T **abbr_table;
- mapblock_T **map_table;
- bool unique = false;
- bool nowait = false;
- bool silent = false;
- bool expr = false;
- int noremap;
- char_u *orig_rhs;
+ char_u *lhs_buf = NULL;
+ char_u *rhs_buf = NULL;
- keys = arg;
- map_table = maphash;
- abbr_table = &first_abbr;
+ // If mapping has been given as ^V<C_UP> say, then replace the term codes
+ // with the appropriate two bytes. If it is a shifted special key, unshift
+ // it too, giving another two bytes.
+ //
+ // replace_termcodes() may move the result to allocated memory, which
+ // needs to be freed later (*lhs_buf and *rhs_buf).
+ // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
+ char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf,
+ true, true, true, cpo_flags);
+ mapargs->lhs_len = STRLEN(replaced);
+ xstrlcpy((char *)mapargs->lhs, (char *)replaced, sizeof(mapargs->lhs));
+
+ mapargs->orig_rhs_len = orig_rhs_len;
+ mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)mapargs->orig_rhs, (char *)orig_rhs,
+ mapargs->orig_rhs_len + 1);
+
+ if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
+ mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
+ mapargs->rhs_len = 0;
+ mapargs->rhs_is_noop = true;
+ } else {
+ replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
+ false, true, true, cpo_flags);
+ mapargs->rhs_len = STRLEN(replaced);
+ mapargs->rhs_is_noop = false;
+ mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)mapargs->rhs, (char *)replaced, mapargs->rhs_len + 1);
+ }
- /* For ":noremap" don't remap, otherwise do remap. */
- if (maptype == 2)
- noremap = REMAP_NONE;
- else
- noremap = REMAP_YES;
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+}
- /* Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
- * any order. */
- for (;; ) {
- /*
- * Check for "<buffer>": mapping local to buffer.
- */
- if (STRNCMP(keys, "<buffer>", 8) == 0) {
- keys = skipwhite(keys + 8);
- map_table = curbuf->b_maphash;
- abbr_table = &curbuf->b_first_abbr;
+/// Parse a string of |:map-arguments| into a @ref MapArguments struct.
+///
+/// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and
+/// {rhs} are replaced by @ref set_maparg_lhs_rhs.
+///
+/// rhs and orig_rhs in the returned mapargs will be set to null or a pointer
+/// to allocated memory and should be freed even on error.
+///
+/// @param[in] strargs String of map args, e.g. "<buffer> <expr><silent>".
+/// May contain leading or trailing whitespace.
+/// @param[in] is_unmap True, if strargs should be parsed like an |:unmap|
+/// command. |:unmap| commands interpret *all* text to the
+/// right of the last map argument as the {lhs} of the
+/// mapping, i.e. a literal ' ' character is treated like
+/// a "<space>", rather than separating the {lhs} from the
+/// {rhs}.
+/// @param[out] mapargs MapArguments struct holding all extracted argument
+/// values.
+/// @return 0 on success, 1 if invalid arguments are detected.
+int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
+{
+ const char_u *to_parse = strargs;
+ to_parse = skipwhite(to_parse);
+ MapArguments parsed_args; // copy these into mapargs "all at once" when done
+ memset(&parsed_args, 0, sizeof(parsed_args));
+
+ // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
+ // any order.
+ while (true) {
+ if (STRNCMP(to_parse, "<buffer>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.buffer = true;
continue;
}
- /*
- * Check for "<nowait>": don't wait for more characters.
- */
- if (STRNCMP(keys, "<nowait>", 8) == 0) {
- keys = skipwhite(keys + 8);
- nowait = true;
+ if (STRNCMP(to_parse, "<nowait>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.nowait = true;
continue;
}
- /*
- * Check for "<silent>": don't echo commands.
- */
- if (STRNCMP(keys, "<silent>", 8) == 0) {
- keys = skipwhite(keys + 8);
- silent = true;
+ if (STRNCMP(to_parse, "<silent>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.silent = true;
continue;
}
// Ignore obsolete "<special>" modifier.
- if (STRNCMP(keys, "<special>", 9) == 0) {
- keys = skipwhite(keys + 9);
+ if (STRNCMP(to_parse, "<special>", 9) == 0) {
+ to_parse = skipwhite(to_parse + 9);
continue;
}
- /*
- * Check for "<script>": remap script-local mappings only
- */
- if (STRNCMP(keys, "<script>", 8) == 0) {
- keys = skipwhite(keys + 8);
- noremap = REMAP_SCRIPT;
+ if (STRNCMP(to_parse, "<script>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.script = true;
continue;
}
- /*
- * Check for "<expr>": {rhs} is an expression.
- */
- if (STRNCMP(keys, "<expr>", 6) == 0) {
- keys = skipwhite(keys + 6);
- expr = true;
+ if (STRNCMP(to_parse, "<expr>", 6) == 0) {
+ to_parse = skipwhite(to_parse + 6);
+ parsed_args.expr = true;
continue;
}
- /*
- * Check for "<unique>": don't overwrite an existing mapping.
- */
- if (STRNCMP(keys, "<unique>", 8) == 0) {
- keys = skipwhite(keys + 8);
- unique = true;
+
+ if (STRNCMP(to_parse, "<unique>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.unique = true;
continue;
}
break;
}
- validate_maphash();
-
- /*
- * Find end of keys and skip CTRL-Vs (and backslashes) in it.
- * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
- * with :unmap white space is included in the keys, no argument possible.
- */
- p = keys;
- do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
- while (*p && (maptype == 1 || !ascii_iswhite(*p))) {
- if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) {
- p++; // skip CTRL-V or backslash
+ // Find the next whitespace character, call that the end of {lhs}.
+ //
+ // If a character (e.g. whitespace) is immediately preceded by a CTRL-V,
+ // "scan past" that character, i.e. don't "terminate" LHS with that character
+ // if it's whitespace.
+ //
+ // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'.
+ //
+ // With :unmap, literal white space is included in the {lhs}; there is no
+ // separate {rhs}.
+ const char_u *lhs_end = to_parse;
+ bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
+ while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) {
+ if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\'))
+ && lhs_end[1] != NUL) {
+ lhs_end++; // skip CTRL-V or backslash
}
- p++;
+ lhs_end++;
+ }
+
+ // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}.
+ // Use that to initialize {rhs_start}.
+ const char_u *rhs_start = skipwhite(lhs_end);
+
+ // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes
+ // (e.g. "<Space>" is longer than ' '), so first copy into a buffer.
+ size_t orig_lhs_len = (size_t)(lhs_end - to_parse);
+ char_u *lhs_to_replace = xcalloc(orig_lhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)lhs_to_replace, (char *)to_parse, orig_lhs_len + 1);
+
+ size_t orig_rhs_len = STRLEN(rhs_start);
+ set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
+ rhs_start, orig_rhs_len,
+ CPO_TO_CPO_FLAGS, &parsed_args);
+
+ xfree(lhs_to_replace);
+
+ *mapargs = parsed_args;
+
+ if (parsed_args.lhs_len > MAXMAPLEN) {
+ return 1;
+ }
+ return 0;
+}
+
+/// Sets or removes a mapping or abbreviation in buffer `buf`.
+///
+/// @param maptype @see do_map
+/// @param args Fully parsed and "preprocessed" arguments for the
+/// (un)map/abbrev command. Termcodes should have already been
+/// replaced; whitespace, `<` and `>` signs, etc. in {lhs} and
+/// {rhs} are assumed to be literal components of the mapping.
+/// @param mode @see do_map
+/// @param is_abbrev @see do_map
+/// @param buf Target Buffer
+int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
+ buf_T *buf)
+{
+ mapblock_T *mp, **mpp;
+ char_u *p;
+ int n;
+ int len = 0; // init for GCC
+ int did_it = false;
+ int did_local = false;
+ int round;
+ int retval = 0;
+ int hash;
+ int new_hash;
+ mapblock_T **abbr_table;
+ mapblock_T **map_table;
+ int noremap;
+
+ map_table = maphash;
+ abbr_table = &first_abbr;
+
+ // For ":noremap" don't remap, otherwise do remap.
+ if (maptype == 2) {
+ noremap = REMAP_NONE;
+ } else {
+ noremap = REMAP_YES;
}
- if (*p != NUL) {
- *p++ = NUL;
+
+ if (args->buffer) {
+ // If <buffer> was given, we'll be searching through the buffer's
+ // mappings/abbreviations, not the globals.
+ map_table = buf->b_maphash;
+ abbr_table = &buf->b_first_abbr;
}
+ if (args->script) {
+ noremap = REMAP_SCRIPT;
+ }
+
+ validate_maphash();
- p = skipwhite(p);
- rhs = p;
- hasarg = (*rhs != NUL);
- haskey = (*keys != NUL);
+ bool has_lhs = (args->lhs[0] != NUL);
+ bool has_rhs = (args->rhs[0] != NUL) || args->rhs_is_noop;
- /* check for :unmap without argument */
- if (maptype == 1 && !haskey) {
+ // check for :unmap without argument
+ if (maptype == 1 && !has_lhs) {
retval = 1;
goto theend;
}
- // If mapping has been given as ^V<C_UP> say, then replace the term codes
- // with the appropriate two bytes. If it is a shifted special key, unshift
- // it too, giving another two bytes.
- // replace_termcodes() may move the result to allocated memory, which
- // needs to be freed later (*keys_buf and *arg_buf).
- // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
- if (haskey) {
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
- CPO_TO_CPO_FLAGS);
- }
- orig_rhs = rhs;
- if (hasarg) {
- if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing
- rhs = (char_u *)"";
- } else {
- rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true,
- CPO_TO_CPO_FLAGS);
- }
- }
+ char_u *lhs = (char_u *)&args->lhs;
+ char_u *rhs = (char_u *)args->rhs;
+ char_u *orig_rhs = args->orig_rhs;
- //
// check arguments and translate function keys
- //
- if (haskey) {
- len = (int)STRLEN(keys);
- if (len > MAXMAPLEN) { /* maximum length of MAXMAPLEN chars */
+ if (has_lhs) {
+ len = (int)args->lhs_len;
+ if (len > MAXMAPLEN) {
retval = 1;
goto theend;
}
- if (abbrev && maptype != 1) {
- /*
- * If an abbreviation ends in a keyword character, the
- * rest must be all keyword-char or all non-keyword-char.
- * Otherwise we won't be able to find the start of it in a
- * vi-compatible way.
- */
+ if (is_abbrev && maptype != 1) {
+ //
+ // If an abbreviation ends in a keyword character, the
+ // rest must be all keyword-char or all non-keyword-char.
+ // Otherwise we won't be able to find the start of it in a
+ // vi-compatible way.
+ //
if (has_mbyte) {
int first, last;
int same = -1;
- first = vim_iswordp(keys);
+ first = vim_iswordp(lhs);
last = first;
- p = keys + (*mb_ptr2len)(keys);
+ p = lhs + (*mb_ptr2len)(lhs);
n = 1;
- while (p < keys + len) {
- ++n; /* nr of (multi-byte) chars */
- last = vim_iswordp(p); /* type of last char */
- if (same == -1 && last != first)
- same = n - 1; /* count of same char type */
+ while (p < lhs + len) {
+ n++; // nr of (multi-byte) chars
+ last = vim_iswordp(p); // type of last char
+ if (same == -1 && last != first) {
+ same = n - 1; // count of same char type
+ }
p += (*mb_ptr2len)(p);
}
if (last && n > 2 && same >= 0 && same < n - 1) {
retval = 1;
goto theend;
}
- } else if (vim_iswordc(keys[len - 1])) /* ends in keyword char */
- for (n = 0; n < len - 2; ++n)
- if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) {
+ } else if (vim_iswordc(lhs[len - 1])) { // ends in keyword char
+ for (n = 0; n < len - 2; n++) {
+ if (vim_iswordc(lhs[n]) != vim_iswordc(lhs[len - 2])) {
retval = 1;
goto theend;
}
- /* An abbreviation cannot contain white space. */
- for (n = 0; n < len; ++n)
- if (ascii_iswhite(keys[n])) {
+ } // for
+ }
+ // An abbreviation cannot contain white space.
+ for (n = 0; n < len; n++) {
+ if (ascii_iswhite(lhs[n])) {
retval = 1;
goto theend;
}
+ } // for
}
}
- if (haskey && hasarg && abbrev) /* if we will add an abbreviation */
- no_abbr = FALSE; /* reset flag that indicates there are
- no abbreviations */
+ if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
+ no_abbr = false; // reset flag that indicates there are no abbreviations
+ }
- if (!haskey || (maptype != 1 && !hasarg))
+ if (!has_lhs || (maptype != 1 && !has_rhs)) {
msg_start();
+ }
- /*
- * Check if a new local mapping wasn't already defined globally.
- */
- if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) {
- /* need to loop over all global hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash != 0) /* there is only one abbreviation list */
+ // Check if a new local mapping wasn't already defined globally.
+ if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
break;
+ }
mp = first_abbr;
- } else
+ } else {
mp = maphash[hash];
+ }
for (; mp != NULL && !got_int; mp = mp->m_next) {
- /* check entries with the same mode */
+ // check entries with the same mode
if ((mp->m_mode & mode) != 0
&& mp->m_keylen == len
- && unique
- && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) {
- if (abbrev)
+ && args->unique
+ && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) {
+ if (is_abbrev) {
EMSG2(_("E224: global abbreviation already exists for %s"),
- mp->m_keys);
- else
- EMSG2(_("E225: global mapping already exists for %s"),
- mp->m_keys);
+ mp->m_keys);
+ } else {
+ EMSG2(_("E225: global mapping already exists for %s"), mp->m_keys);
+ }
retval = 5;
goto theend;
}
@@ -2777,30 +2837,29 @@ do_map (
}
}
- /*
- * When listing global mappings, also list buffer-local ones here.
- */
- if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) {
- /* need to loop over all global hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash != 0) /* there is only one abbreviation list */
+ // When listing global mappings, also list buffer-local ones here.
+ if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
break;
- mp = curbuf->b_first_abbr;
- } else
- mp = curbuf->b_maphash[hash];
+ }
+ mp = buf->b_first_abbr;
+ } else {
+ mp = buf->b_maphash[hash];
+ }
for (; mp != NULL && !got_int; mp = mp->m_next) {
- /* check entries with the same mode */
+ // check entries with the same mode
if ((mp->m_mode & mode) != 0) {
- if (!haskey) { /* show all entries */
- showmap(mp, TRUE);
- did_local = TRUE;
+ if (!has_lhs) { // show all entries
+ showmap(mp, true);
+ did_local = true;
} else {
n = mp->m_keylen;
- if (STRNCMP(mp->m_keys, keys,
- (size_t)(n < len ? n : len)) == 0) {
- showmap(mp, TRUE);
- did_local = TRUE;
+ if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
+ showmap(mp, true);
+ did_local = true;
}
}
}
@@ -2808,103 +2867,98 @@ do_map (
}
}
- /*
- * Find an entry in the maphash[] list that matches.
- * For :unmap we may loop two times: once to try to unmap an entry with a
- * matching 'from' part, a second time, if the first fails, to unmap an
- * entry with a matching 'to' part. This was done to allow ":ab foo bar"
- * to be unmapped by typing ":unab foo", where "foo" will be replaced by
- * "bar" because of the abbreviation.
- */
+ // Find an entry in the maphash[] list that matches.
+ // For :unmap we may loop two times: once to try to unmap an entry with a
+ // matching 'from' part, a second time, if the first fails, to unmap an
+ // entry with a matching 'to' part. This was done to allow ":ab foo bar"
+ // to be unmapped by typing ":unab foo", where "foo" will be replaced by
+ // "bar" because of the abbreviation.
for (round = 0; (round == 0 || maptype == 1) && round <= 1
- && !did_it && !got_int; ++round) {
- /* need to loop over all hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash > 0) /* there is only one abbreviation list */
+ && !did_it && !got_int; round++) {
+ // need to loop over all hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash > 0) { // there is only one abbreviation list
break;
+ }
mpp = abbr_table;
- } else
+ } else {
mpp = &(map_table[hash]);
+ }
for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
-
- if (!(mp->m_mode & mode)) { /* skip entries with wrong mode */
+ if (!(mp->m_mode & mode)) { // skip entries with wrong mode
mpp = &(mp->m_next);
continue;
}
- if (!haskey) { /* show all entries */
+ if (!has_lhs) { // show all entries
showmap(mp, map_table != maphash);
- did_it = TRUE;
- } else { /* do we have a match? */
- if (round) { /* second round: Try unmap "rhs" string */
+ did_it = true;
+ } else { // do we have a match?
+ if (round) { // second round: Try unmap "rhs" string
n = (int)STRLEN(mp->m_str);
p = mp->m_str;
} else {
n = mp->m_keylen;
p = mp->m_keys;
}
- if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) {
- if (maptype == 1) { /* delete entry */
- /* Only accept a full match. For abbreviations we
- * ignore trailing space when matching with the
- * "lhs", since an abbreviation can't have
- * trailing space. */
- if (n != len && (!abbrev || round || n > len
- || *skipwhite(keys + n) != NUL)) {
+ if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
+ if (maptype == 1) { // delete entry
+ // Only accept a full match. For abbreviations we
+ // ignore trailing space when matching with the
+ // "lhs", since an abbreviation can't have
+ // trailing space.
+ if (n != len && (!is_abbrev || round || n > len
+ || *skipwhite(lhs + n) != NUL)) {
mpp = &(mp->m_next);
continue;
}
- /*
- * We reset the indicated mode bits. If nothing is
- * left the entry is deleted below.
- */
+ // We reset the indicated mode bits. If nothing is
+ // left the entry is deleted below.
mp->m_mode &= ~mode;
- did_it = TRUE; /* remember we did something */
- } else if (!hasarg) { /* show matching entry */
+ did_it = true; // remember we did something
+ } else if (!has_rhs) { // show matching entry
showmap(mp, map_table != maphash);
- did_it = TRUE;
- } else if (n != len) { /* new entry is ambiguous */
+ did_it = true;
+ } else if (n != len) { // new entry is ambiguous
mpp = &(mp->m_next);
continue;
- } else if (unique) {
- if (abbrev)
- EMSG2(_("E226: abbreviation already exists for %s"),
- p);
- else
+ } else if (args->unique) {
+ if (is_abbrev) {
+ EMSG2(_("E226: abbreviation already exists for %s"), p);
+ } else {
EMSG2(_("E227: mapping already exists for %s"), p);
+ }
retval = 5;
goto theend;
- } else { /* new rhs for existing entry */
- mp->m_mode &= ~mode; /* remove mode bits */
- if (mp->m_mode == 0 && !did_it) { /* reuse entry */
+ } else { // new rhs for existing entry
+ mp->m_mode &= ~mode; // remove mode bits
+ if (mp->m_mode == 0 && !did_it) { // reuse entry
xfree(mp->m_str);
mp->m_str = vim_strsave(rhs);
xfree(mp->m_orig_str);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_noremap = noremap;
- mp->m_nowait = nowait;
- mp->m_silent = silent;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
mp->m_mode = mode;
- mp->m_expr = expr;
+ mp->m_expr = args->expr;
mp->m_script_ID = current_SID;
- did_it = TRUE;
+ did_it = true;
}
}
- if (mp->m_mode == 0) { // entry can be deleted
+ if (mp->m_mode == 0) { // entry can be deleted
mapblock_free(mpp);
- continue; // continue with *mpp
+ continue; // continue with *mpp
}
- /*
- * May need to put this entry into another hash list.
- */
+ // May need to put this entry into another hash list.
new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- if (!abbrev && new_hash != hash) {
+ if (!is_abbrev && new_hash != hash) {
*mpp = mp->m_next;
mp->m_next = map_table[new_hash];
map_table[new_hash] = mp;
- continue; /* continue with *mpp */
+ continue; // continue with *mpp
}
}
}
@@ -2913,13 +2967,13 @@ do_map (
}
}
- if (maptype == 1) { /* delete entry */
+ if (maptype == 1) { // delete entry
if (!did_it) {
- retval = 2; /* no match */
- } else if (*keys == Ctrl_C) {
+ retval = 2; // no match
+ } else if (*lhs == Ctrl_C) {
// If CTRL-C has been unmapped, reuse it for Interrupting.
- if (map_table == curbuf->b_maphash) {
- curbuf->b_mapped_ctrl_c &= ~mode;
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c &= ~mode;
} else {
mapped_ctrl_c &= ~mode;
}
@@ -2927,48 +2981,46 @@ do_map (
goto theend;
}
- if (!haskey || !hasarg) { /* print entries */
- if (!did_it
- && !did_local
- ) {
- if (abbrev)
+ if (!has_lhs || !has_rhs) { // print entries
+ if (!did_it && !did_local) {
+ if (is_abbrev) {
MSG(_("No abbreviation found"));
- else
+ } else {
MSG(_("No mapping found"));
+ }
}
- goto theend; /* listing finished */
+ goto theend; // listing finished
}
- if (did_it) /* have added the new entry already */
+ if (did_it) { // have added the new entry already
goto theend;
+ }
- /*
- * Get here when adding a new entry to the maphash[] list or abbrlist.
- */
+ // Get here when adding a new entry to the maphash[] list or abbrlist.
mp = xmalloc(sizeof(mapblock_T));
// If CTRL-C has been mapped, don't always use it for Interrupting.
- if (*keys == Ctrl_C) {
- if (map_table == curbuf->b_maphash) {
- curbuf->b_mapped_ctrl_c |= mode;
+ if (*lhs == Ctrl_C) {
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c |= mode;
} else {
mapped_ctrl_c |= mode;
}
}
- mp->m_keys = vim_strsave(keys);
+ mp->m_keys = vim_strsave(lhs);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
- mp->m_nowait = nowait;
- mp->m_silent = silent;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
mp->m_mode = mode;
- mp->m_expr = expr;
+ mp->m_expr = args->expr;
mp->m_script_ID = current_SID;
- /* add the new entry in front of the abbrlist or maphash[] list */
- if (abbrev) {
+ // add the new entry in front of the abbrlist or maphash[] list
+ if (is_abbrev) {
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
@@ -2978,11 +3030,80 @@ do_map (
}
theend:
- xfree(keys_buf);
- xfree(arg_buf);
return retval;
}
+
+/// Set or remove a mapping or an abbreviation in the current buffer, OR
+/// display (matching) mappings/abbreviations.
+///
+/// ```vim
+/// map[!] " show all key mappings
+/// map[!] {lhs} " show key mapping for {lhs}
+/// map[!] {lhs} {rhs} " set key mapping for {lhs} to {rhs}
+/// noremap[!] {lhs} {rhs} " same, but no remapping for {rhs}
+/// unmap[!] {lhs} " remove key mapping for {lhs}
+/// abbr " show all abbreviations
+/// abbr {lhs} " show abbreviations for {lhs}
+/// abbr {lhs} {rhs} " set abbreviation for {lhs} to {rhs}
+/// noreabbr {lhs} {rhs} " same, but no remapping for {rhs}
+/// unabbr {lhs} " remove abbreviation for {lhs}
+///
+/// for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
+/// for :map! mode is INSERT + CMDLINE
+/// for :cmap mode is CMDLINE
+/// for :imap mode is INSERT
+/// for :lmap mode is LANGMAP
+/// for :nmap mode is NORMAL
+/// for :vmap mode is VISUAL + SELECTMODE
+/// for :xmap mode is VISUAL
+/// for :smap mode is SELECTMODE
+/// for :omap mode is OP_PENDING
+/// for :tmap mode is TERM_FOCUS
+///
+/// for :abbr mode is INSERT + CMDLINE
+/// for :iabbr mode is INSERT
+/// for :cabbr mode is CMDLINE
+/// ```
+///
+/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|.
+/// @param arg C-string containing the arguments of the map/abbrev
+/// command, i.e. everything except the initial `:[X][nore]map`.
+/// - Cannot be a read-only string; it will be modified.
+/// @param mode Bitflags representing the mode in which to set the mapping.
+/// See @ref get_map_mode.
+/// @param is_abbrev True if setting an abbreviation, false otherwise.
+///
+/// @return 0 on success. On failure, will return one of the following:
+/// - 1 for invalid arguments
+/// - 2 for no match
+/// - 4 for out of mem (deprecated, WON'T HAPPEN)
+/// - 5 for entry not unique
+///
+int do_map(int maptype, char_u *arg, int mode, bool is_abbrev)
+{
+ MapArguments parsed_args;
+ int result = str_to_mapargs(arg, maptype == 1, &parsed_args);
+ switch (result) {
+ case 0:
+ break;
+ case 1:
+ result = 1; // invalid arguments
+ goto free_and_return;
+ default:
+ assert(false && "Unknown return code from str_to_mapargs!");
+ result = -1;
+ goto free_and_return;
+ } // switch
+
+ result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf);
+
+free_and_return:
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ return result;
+}
+
/*
* Delete one entry from the abbrlist or maphash[].
* "mpp" is a pointer to the m_next field of the PREVIOUS entry!
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 4f548d975a..a40ea7730a 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,6 +5,7 @@
#include "nvim/types.h"
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/vim.h"
/// Values for "noremap" argument of ins_typebuf()
///
@@ -23,6 +24,39 @@ typedef enum {
FLUSH_INPUT // flush typebuf and inchar() input
} flush_buffers_T;
+/// All possible |:map-arguments| usable in a |:map| command.
+///
+/// The <special> argument has no effect on mappings and is excluded from this
+/// struct declaration. |noremap| is included, since it behaves like a map
+/// argument when used in a mapping.
+///
+/// @see mapblock_T
+struct map_arguments {
+ bool buffer;
+ bool expr;
+ bool noremap;
+ bool nowait;
+ bool script;
+ bool silent;
+ bool unique;
+
+ /// The {lhs} of the mapping.
+ ///
+ /// vim limits this to MAXMAPLEN characters, allowing us to use a static
+ /// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
+ /// that {lhs} was too long and truncated.
+ char_u lhs[MAXMAPLEN + 1];
+ size_t lhs_len;
+
+ char_u *rhs; /// The {rhs} of the mapping.
+ size_t rhs_len;
+ bool rhs_is_noop; /// True when the {orig_rhs} is <nop>.
+
+ char_u *orig_rhs; /// The original text of the {rhs}.
+ size_t orig_rhs_len;
+};
+typedef struct map_arguments MapArguments;
+
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */
diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h
index acc7e3a92c..629301e8fe 100644
--- a/src/nvim/gettext.h
+++ b/src/nvim/gettext.h
@@ -11,6 +11,11 @@
# define N_(x) x
# endif
# define NGETTEXT(x, xs, n) ngettext(x, xs, n)
+// On a Mac, gettext's libintl.h defines "setlocale" to be replaced by
+// "libintl_setlocal" which leads to wrong return values. #9789
+# if defined(__APPLE__) && defined(setlocale)
+# undef setlocale
+# endif
#else
# define _(x) ((char *)(x))
# define N_(x) x
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 004b3252da..ec14ada3d2 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -227,6 +227,7 @@ EXTERN dict_T vimvardict; /* Dictionary with v: variables */
EXTERN dict_T globvardict; /* Dictionary with g: variables */
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
EXTERN int did_emsg_syntax; /* did_emsg set because of a
syntax error */
EXTERN int called_emsg; /* always set by emsg() */
@@ -754,7 +755,6 @@ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
EXTERN int need_highlight_changed INIT(= true);
-EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
@@ -788,8 +788,6 @@ EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
-// for CursorMoved event
-EXTERN pos_T last_cursormoved INIT(= { 0, 0, 0 });
EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */
EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
@@ -933,6 +931,7 @@ 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_duparg2[] INIT(= N_("E983: Duplicate argument: %s"));
EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s"));
EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range"));
EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command"));
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 4c5fca6d39..3ba02be32d 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -160,14 +160,19 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_needs_update = false;
// determine window specific background set in 'winhighlight'
+ bool float_win = wp->w_floating && !wp->w_float_config.external;
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
wp->w_hl_ids[HLF_INACTIVE], true);
+ } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) {
+ wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
+ wp->w_hl_ids[HLF_NFLOAT], true);
} else if (wp->w_hl_id_normal > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, true);
} else {
- wp->w_hl_attr_normal = 0;
+ wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
+
if (wp != curwin) {
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 1da33bfea5..746d2c2dfc 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -90,6 +90,7 @@ typedef enum {
, HLF_0 // Whitespace
, HLF_INACTIVE // NormalNC: Normal text in non-current windows
, HLF_MSGSEP // message separator line
+ , HLF_NFLOAT // Floating window
, HLF_COUNT // MUST be the last one
} hlf_T;
@@ -142,6 +143,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_0] = "Whitespace",
[HLF_INACTIVE] = "NormalNC",
[HLF_MSGSEP] = "MsgSeparator",
+ [HLF_NFLOAT] = "NormalFloat",
});
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 625d6baa17..8b6fd6c705 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -16,11 +16,10 @@
#include <inttypes.h>
#include <fcntl.h>
-#include "nvim/vim.h"
+#include "nvim/buffer.h"
#include "nvim/ascii.h"
#include "nvim/if_cscope.h"
#include "nvim/charset.h"
-#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/message.h"
#include "nvim/memory.h"
@@ -29,7 +28,6 @@
#include "nvim/quickfix.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/event/stream.h"
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index ade5487ec8..9145813525 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -163,6 +163,7 @@ static const struct key_name_entry {
{ K_DEL, "Del" },
{ K_DEL, "Delete" }, // Alternative name
{ K_KDEL, "kDel" },
+ { K_KDEL, "KPPeriod" }, // libtermkey name
{ K_UP, "Up" },
{ K_DOWN, "Down" },
{ K_LEFT, "Left" },
@@ -171,6 +172,14 @@ static const struct key_name_entry {
{ K_XDOWN, "xDown" },
{ K_XLEFT, "xLeft" },
{ K_XRIGHT, "xRight" },
+ { K_KUP, "kUp" },
+ { K_KUP, "KP8" },
+ { K_KDOWN, "kDown" },
+ { K_KDOWN, "KP2" },
+ { K_KLEFT, "kLeft" },
+ { K_KLEFT, "KP4" },
+ { K_KRIGHT, "kRight" },
+ { K_KRIGHT, "KP6" },
{ K_F1, "F1" },
{ K_F2, "F2" },
@@ -223,25 +232,41 @@ static const struct key_name_entry {
{ K_INS, "Insert" },
{ K_INS, "Ins" }, // Alternative name
{ K_KINS, "kInsert" },
+ { K_KINS, "KP0" },
{ K_HOME, "Home" },
{ K_KHOME, "kHome" },
+ { K_KHOME, "KP7" },
{ K_XHOME, "xHome" },
{ K_ZHOME, "zHome" },
{ K_END, "End" },
{ K_KEND, "kEnd" },
+ { K_KEND, "KP1" },
{ K_XEND, "xEnd" },
{ K_ZEND, "zEnd" },
{ K_PAGEUP, "PageUp" },
{ K_PAGEDOWN, "PageDown" },
{ K_KPAGEUP, "kPageUp" },
+ { K_KPAGEUP, "KP9" },
{ K_KPAGEDOWN, "kPageDown" },
+ { K_KPAGEDOWN, "KP3" },
+ { K_KORIGIN, "kOrigin" },
+ { K_KORIGIN, "KP5" },
{ K_KPLUS, "kPlus" },
+ { K_KPLUS, "KPPlus" },
{ K_KMINUS, "kMinus" },
+ { K_KMINUS, "KPMinus" },
{ K_KDIVIDE, "kDivide" },
+ { K_KDIVIDE, "KPDiv" },
{ K_KMULTIPLY, "kMultiply" },
+ { K_KMULTIPLY, "KPMult" },
{ K_KENTER, "kEnter" },
+ { K_KENTER, "KPEnter" },
{ K_KPOINT, "kPoint" },
+ { K_KCOMMA, "kComma" },
+ { K_KCOMMA, "KPComma" },
+ { K_KEQUAL, "kEqual" },
+ { K_KEQUAL, "KPEquals" },
{ K_K0, "k0" },
{ K_K1, "k1" },
@@ -663,7 +688,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
*modp = modifiers;
*srcp = end_of_name;
return key;
- }
+ } // else { ELOG("unknown key: '%s'", src); }
}
}
return 0;
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index baf8963aa8..7f0483826d 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -256,9 +256,13 @@ enum key_extra {
#define K_ZERO TERMCAP2KEY(KS_ZERO, KE_FILLER)
#define K_UP TERMCAP2KEY('k', 'u')
+#define K_KUP TERMCAP2KEY('K', 'u') // keypad up
#define K_DOWN TERMCAP2KEY('k', 'd')
+#define K_KDOWN TERMCAP2KEY('K', 'd') // keypad down
#define K_LEFT TERMCAP2KEY('k', 'l')
+#define K_KLEFT TERMCAP2KEY('K', 'l') // keypad left
#define K_RIGHT TERMCAP2KEY('k', 'r')
+#define K_KRIGHT TERMCAP2KEY('K', 'r') // keypad right
#define K_S_UP TERMCAP2KEY(KS_EXTRA, KE_S_UP)
#define K_S_DOWN TERMCAP2KEY(KS_EXTRA, KE_S_DOWN)
#define K_S_LEFT TERMCAP2KEY('#', '4')
@@ -284,7 +288,7 @@ enum key_extra {
#define K_XLEFT TERMCAP2KEY(KS_EXTRA, KE_XLEFT)
#define K_XRIGHT TERMCAP2KEY(KS_EXTRA, KE_XRIGHT)
-#define K_F1 TERMCAP2KEY('k', '1') /* function keys */
+#define K_F1 TERMCAP2KEY('k', '1') // function keys
#define K_F2 TERMCAP2KEY('k', '2')
#define K_F3 TERMCAP2KEY('k', '3')
#define K_F4 TERMCAP2KEY('k', '4')
@@ -331,7 +335,7 @@ enum key_extra {
#define K_S_XF3 TERMCAP2KEY(KS_EXTRA, KE_S_XF3)
#define K_S_XF4 TERMCAP2KEY(KS_EXTRA, KE_S_XF4)
-#define K_S_F1 TERMCAP2KEY(KS_EXTRA, KE_S_F1) /* shifted func. keys */
+#define K_S_F1 TERMCAP2KEY(KS_EXTRA, KE_S_F1) // shifted func. keys
#define K_S_F2 TERMCAP2KEY(KS_EXTRA, KE_S_F2)
#define K_S_F3 TERMCAP2KEY(KS_EXTRA, KE_S_F3)
#define K_S_F4 TERMCAP2KEY(KS_EXTRA, KE_S_F4)
@@ -356,35 +360,39 @@ enum key_extra {
#define K_DEL TERMCAP2KEY('k', 'D')
#define K_KDEL TERMCAP2KEY(KS_EXTRA, KE_KDEL)
#define K_HOME TERMCAP2KEY('k', 'h')
-#define K_KHOME TERMCAP2KEY('K', '1') /* keypad home (upper left) */
+#define K_KHOME TERMCAP2KEY('K', '1') // keypad home (upper left)
#define K_XHOME TERMCAP2KEY(KS_EXTRA, KE_XHOME)
#define K_ZHOME TERMCAP2KEY(KS_EXTRA, KE_ZHOME)
#define K_END TERMCAP2KEY('@', '7')
-#define K_KEND TERMCAP2KEY('K', '4') /* keypad end (lower left) */
+#define K_KEND TERMCAP2KEY('K', '4') // keypad end (lower left)
#define K_XEND TERMCAP2KEY(KS_EXTRA, KE_XEND)
#define K_ZEND TERMCAP2KEY(KS_EXTRA, KE_ZEND)
#define K_PAGEUP TERMCAP2KEY('k', 'P')
#define K_PAGEDOWN TERMCAP2KEY('k', 'N')
-#define K_KPAGEUP TERMCAP2KEY('K', '3') /* keypad pageup (upper R.) */
-#define K_KPAGEDOWN TERMCAP2KEY('K', '5') /* keypad pagedown (lower R.) */
-
-#define K_KPLUS TERMCAP2KEY('K', '6') /* keypad plus */
-#define K_KMINUS TERMCAP2KEY('K', '7') /* keypad minus */
-#define K_KDIVIDE TERMCAP2KEY('K', '8') /* keypad / */
-#define K_KMULTIPLY TERMCAP2KEY('K', '9') /* keypad * */
-#define K_KENTER TERMCAP2KEY('K', 'A') /* keypad Enter */
-#define K_KPOINT TERMCAP2KEY('K', 'B') /* keypad . or ,*/
-
-#define K_K0 TERMCAP2KEY('K', 'C') /* keypad 0 */
-#define K_K1 TERMCAP2KEY('K', 'D') /* keypad 1 */
-#define K_K2 TERMCAP2KEY('K', 'E') /* keypad 2 */
-#define K_K3 TERMCAP2KEY('K', 'F') /* keypad 3 */
-#define K_K4 TERMCAP2KEY('K', 'G') /* keypad 4 */
-#define K_K5 TERMCAP2KEY('K', 'H') /* keypad 5 */
-#define K_K6 TERMCAP2KEY('K', 'I') /* keypad 6 */
-#define K_K7 TERMCAP2KEY('K', 'J') /* keypad 7 */
-#define K_K8 TERMCAP2KEY('K', 'K') /* keypad 8 */
-#define K_K9 TERMCAP2KEY('K', 'L') /* keypad 9 */
+#define K_KPAGEUP TERMCAP2KEY('K', '3') // keypad pageup (upper R.)
+#define K_KPAGEDOWN TERMCAP2KEY('K', '5') // keypad pagedown (lower R.)
+#define K_KORIGIN TERMCAP2KEY('K', '2') // keypad center
+
+#define K_KPLUS TERMCAP2KEY('K', '6') // keypad plus
+#define K_KMINUS TERMCAP2KEY('K', '7') // keypad minus
+#define K_KDIVIDE TERMCAP2KEY('K', '8') // keypad /
+#define K_KMULTIPLY TERMCAP2KEY('K', '9') // keypad *
+#define K_KENTER TERMCAP2KEY('K', 'A') // keypad Enter
+#define K_KPOINT TERMCAP2KEY('K', 'B') // keypad . or ,
+
+#define K_K0 TERMCAP2KEY('K', 'C') // keypad 0
+#define K_K1 TERMCAP2KEY('K', 'D') // keypad 1
+#define K_K2 TERMCAP2KEY('K', 'E') // keypad 2
+#define K_K3 TERMCAP2KEY('K', 'F') // keypad 3
+#define K_K4 TERMCAP2KEY('K', 'G') // keypad 4
+#define K_K5 TERMCAP2KEY('K', 'H') // keypad 5
+#define K_K6 TERMCAP2KEY('K', 'I') // keypad 6
+#define K_K7 TERMCAP2KEY('K', 'J') // keypad 7
+#define K_K8 TERMCAP2KEY('K', 'K') // keypad 8
+#define K_K9 TERMCAP2KEY('K', 'L') // keypad 9
+
+#define K_KCOMMA TERMCAP2KEY('K', 'M') // keypad comma
+#define K_KEQUAL TERMCAP2KEY('K', 'N') // keypad equal
#define K_MOUSE TERMCAP2KEY(KS_MOUSE, KE_FILLER)
#define K_MENU TERMCAP2KEY(KS_MENU, KE_FILLER)
diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h
index e2688064a8..704aa26010 100644
--- a/src/nvim/lib/kbtree.h
+++ b/src/nvim/lib/kbtree.h
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
+#include <assert.h>
#include "nvim/memory.h"
@@ -317,7 +318,7 @@
#define __KB_ITR(name, key_t, kbnode_t) \
static inline void kb_itr_first_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- itr->p = 0; \
+ itr->p = NULL; \
if (b->n_keys == 0) return; \
itr->p = itr->stack; \
itr->p->x = b->root; itr->p->i = 0; \
@@ -329,30 +330,37 @@
} \
static inline int kb_itr_next_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- if (itr->p < itr->stack) return 0; \
+ if (itr->p == NULL) return 0; \
for (;;) { \
++itr->p->i; \
+ assert(itr->p->i <= 21); \
while (itr->p->x && itr->p->i <= itr->p->x->n) { \
itr->p[1].i = 0; \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
++itr->p; \
} \
+ if (itr->p == itr->stack) { \
+ itr->p = NULL; \
+ return 0; \
+ } \
--itr->p; \
- if (itr->p < itr->stack) return 0; \
if (itr->p->x && itr->p->i < itr->p->x->n) return 1; \
} \
} \
static inline int kb_itr_prev_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- if (itr->p < itr->stack) return 0; \
+ if (itr->p == NULL) return 0; \
for (;;) { \
while (itr->p->x && itr->p->i >= 0) { \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
itr->p[1].i = itr->p[1].x ? itr->p[1].x->n : -1; \
++itr->p; \
} \
+ if (itr->p == itr->stack) { \
+ itr->p = NULL; \
+ return 0; \
+ } \
--itr->p; \
- if (itr->p < itr->stack) return 0; \
--itr->p->i; \
if (itr->p->x && itr->p->i >= 0) return 1; \
} \
@@ -371,9 +379,11 @@
itr->p->i = i; \
if (i >= 0 && r == 0) return 1; \
++itr->p->i; \
+ assert(itr->p->i <= 21); \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[i + 1] : 0; \
++itr->p; \
} \
+ itr->p->i = 0; \
return 0; \
} \
static inline int kb_itr_get_##name(kbtree_##name##_t *b, key_t k, kbitr_##name##_t *itr) \
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 8066b6e828..a2f83d4d09 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -1,6 +1,13 @@
// 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
+//
+// Log module
+//
+// How Linux printk() handles recursion, buffering, etc:
+// https://lwn.net/Articles/780556/
+//
+
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
@@ -99,9 +106,14 @@ void log_unlock(void)
uv_mutex_unlock(&mutex);
}
-/// @param context description of a shared context or subsystem
-/// @param func_name function name, or NULL
-/// @param line_num source line number, or -1
+/// Logs a message to $NVIM_LOG_FILE.
+///
+/// @param log_level Log level (see log.h)
+/// @param context Description of a shared context or subsystem
+/// @param func_name Function name, or NULL
+/// @param line_num Source line number, or -1
+/// @param eol Append linefeed "\n"
+/// @param fmt printf-style format string
bool logmsg(int log_level, const char *context, const char *func_name,
int line_num, bool eol, const char *fmt, ...)
FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7)
@@ -163,7 +175,8 @@ end:
FILE *open_log_file(void)
{
static bool opening_log_file = false;
- // check if it's a recursive call
+ // Disallow recursion. (This only matters for log_path_init; for logmsg and
+ // friends we use a mutex: log_lock).
if (opening_log_file) {
do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Cannot LOG() recursively.");
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 93069893cf..72b97736fc 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -24,6 +24,10 @@
#include "nvim/undo.h"
#include "nvim/ascii.h"
+#ifdef WIN32
+#include "nvim/os/os.h"
+#endif
+
#include "nvim/lua/executor.h"
#include "nvim/lua/converter.h"
@@ -118,6 +122,14 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "debug");
lua_pop(lstate, 1);
+#ifdef WIN32
+ // os.getenv
+ lua_getglobal(lstate, "os");
+ lua_pushcfunction(lstate, &nlua_getenv);
+ lua_setfield(lstate, -2, "getenv");
+ lua_pop(lstate, 1);
+#endif
+
// vim
if (luaL_dostring(lstate, (char *)&vim_module[0])) {
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
@@ -297,7 +309,7 @@ nlua_print_error:
return 0;
}
-/// debug.debug implementation: interaction with user while debugging
+/// debug.debug: interaction with user while debugging.
///
/// @param lstate Lua interpreter state.
int nlua_debug(lua_State *lstate)
@@ -337,6 +349,19 @@ int nlua_debug(lua_State *lstate)
return 0;
}
+#ifdef WIN32
+/// os.getenv: override os.getenv to maintain coherency. #9681
+///
+/// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ.
+///
+/// @param lstate Lua interpreter state.
+static int nlua_getenv(lua_State *lstate)
+{
+ lua_pushstring(lstate, os_getenv(luaL_checkstring(lstate, 1)));
+ return 1;
+}
+#endif
+
/// Evaluate lua string
///
/// Used for luaeval().
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index b0d0bfc74b..9cd8e232d5 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -1,3 +1,39 @@
+-- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib)
+--
+-- Lua code lives in one of three places:
+-- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the
+-- `inspect` and `lpeg` modules.
+-- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests.
+-- 3. src/nvim/lua/: Compiled-into Nvim itself.
+--
+-- Guideline: "If in doubt, put it in the runtime".
+--
+-- Most functions should live directly on `vim.`, not sub-modules. The only
+-- "forbidden" names are those claimed by legacy `if_lua`:
+-- $ vim
+-- :lua for k,v in pairs(vim) do print(k) end
+-- buffer
+-- open
+-- window
+-- lastline
+-- firstline
+-- type
+-- line
+-- eval
+-- dict
+-- beep
+-- list
+-- command
+--
+-- Reference (#6580):
+-- - https://github.com/luafun/luafun
+-- - https://github.com/rxi/lume
+-- - http://leafo.net/lapis/reference/utilities.html
+-- - https://github.com/torch/paths
+-- - https://github.com/bakpakin/Fennel (pretty print, repl)
+-- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
+
+
-- Internal-only until comments in #8107 are addressed.
-- Returns:
-- {errcode}, {output}
@@ -118,79 +154,24 @@ local function _update_package_paths()
last_nvim_paths = cur_nvim_paths
end
-local function gsplit(s, sep, plain)
- assert(type(s) == "string")
- assert(type(sep) == "string")
- assert(type(plain) == "boolean" or type(plain) == "nil")
-
- local start = 1
- local done = false
-
- local function pass(i, j, ...)
- if i then
- assert(j+1 > start, "Infinite loop detected")
- local seg = s:sub(start, i - 1)
- start = j + 1
- return seg, ...
- else
- done = true
- return s:sub(start)
- end
- end
-
- return function()
- if done then
- return
- end
- if sep == '' then
- if start == #s then
- done = true
- end
- return pass(start+1, start)
- end
- return pass(s:find(sep, start, plain))
- end
-end
-
-local function split(s,sep,plain)
- local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end
- return t
-end
-
+--- Trim whitespace (Lua pattern "%%s") from both sides of a string.
+---
+--@see https://www.lua.org/pil/20.2.html
+--@param s String to trim
+--@returns String with whitespace removed from its beginning and end
local function trim(s)
- assert(type(s) == "string", "Only strings can be trimmed")
- local result = s:gsub("^%s+", ""):gsub("%s+$", "")
- return result
-end
-
-local deepcopy
-
-local function id(v)
- return v
-end
-
-local deepcopy_funcs = {
- table = function(orig)
- local copy = {}
- for k, v in pairs(orig) do
- copy[deepcopy(k)] = deepcopy(v)
- end
- return copy
- end,
- number = id,
- string = id,
- ['nil'] = id,
- boolean = id,
-}
-
-deepcopy = function(orig)
- return deepcopy_funcs[type(orig)](orig)
+ assert(type(s) == 'string', 'Only strings can be trimmed')
+ return s:match('^%s*(.*%S)') or ''
end
-local function __index(table, key)
- if key == "inspect" then
- table.inspect = require("vim.inspect")
- return table.inspect
+local function __index(t, key)
+ if key == 'inspect' then
+ t.inspect = require('vim.inspect')
+ return t.inspect
+ elseif require('vim.shared')[key] ~= nil then
+ -- Expose all `vim.shared` functions on the `vim` module.
+ t[key] = require('vim.shared')[key]
+ return t[key]
end
end
@@ -200,9 +181,6 @@ local module = {
_os_proc_info = _os_proc_info,
_system = _system,
trim = trim,
- split = split,
- gsplit = gsplit,
- deepcopy = deepcopy,
}
setmetatable(module, {
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 5dffca95a2..4e1c7dff57 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -114,12 +114,12 @@ typedef struct {
char *listen_addr; // --listen {address}
} mparm_T;
-/* Values for edit_type. */
-#define EDIT_NONE 0 /* no edit type yet */
-#define EDIT_FILE 1 /* file name argument[s] given, use argument list */
-#define EDIT_STDIN 2 /* read file from stdin */
-#define EDIT_TAG 3 /* tag name argument given, use tagname */
-#define EDIT_QF 4 /* start in quickfix mode */
+// Values for edit_type.
+#define EDIT_NONE 0 // no edit type yet
+#define EDIT_FILE 1 // file name argument[s] given, use argument list
+#define EDIT_STDIN 2 // read file from stdin
+#define EDIT_TAG 3 // tag name argument given, use tagname
+#define EDIT_QF 4 // start in quickfix mode
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "main.c.generated.h"
@@ -547,10 +547,11 @@ int main(int argc, char **argv)
if (params.n_commands > 0)
exe_commands(&params);
+ starting = 0;
+
RedrawingDisabled = 0;
redraw_all_later(NOT_VALID);
- no_wait_return = FALSE;
- starting = 0;
+ no_wait_return = false;
// 'autochdir' has been postponed.
do_autochdir();
@@ -806,6 +807,7 @@ static void command_line_scan(mparm_T *parmp)
if (exmode_active) {
// "nvim -e -" silent mode
silent_mode = true;
+ parmp->no_swap_file = true;
} else {
if (parmp->edit_type != EDIT_NONE
&& parmp->edit_type != EDIT_FILE
@@ -869,6 +871,9 @@ static void command_line_scan(mparm_T *parmp)
} else if (STRNICMP(argv[0] + argv_idx, "startuptime", 11) == 0) {
want_argument = true;
argv_idx += 11;
+ } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
+ parmp->use_vimrc = "NONE";
+ set_option_value("shadafile", 0L, "NONE", 0);
} else {
if (argv[0][argv_idx])
mainerr(err_opt_unknown, argv[0]);
@@ -989,6 +994,7 @@ static void command_line_scan(mparm_T *parmp)
case 's': {
if (exmode_active) { // "-es" silent (batch) Ex-mode
silent_mode = true;
+ parmp->no_swap_file = true;
} else { // "-s {scriptin}" read from script file
want_argument = true;
}
@@ -1126,7 +1132,7 @@ static void command_line_scan(mparm_T *parmp)
}
case 'i': { // "-i {shada}" use for shada
- used_shada_file = argv[0];
+ set_option_value("shadafile", 0L, argv[0], 0);
break;
}
@@ -1514,10 +1520,11 @@ static void create_windows(mparm_T *parmp)
dorewind = FALSE;
curbuf = curwin->w_buffer;
if (curbuf->b_ml.ml_mfp == NULL) {
- /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
- if (p_fdls >= 0)
+ // Set 'foldlevel' to 'foldlevelstart' if it's not negative..
+ if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
- /* When getting the ATTENTION prompt here, use a dialog */
+ }
+ // When getting the ATTENTION prompt here, use a dialog.
swap_exists_action = SEA_DIALOG;
set_buflisted(TRUE);
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 9b6f57a56f..90da27cf9f 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -1,12 +1,12 @@
// 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
-///
-/// map.c: khash.h wrapper
-///
-/// NOTE: Callers must manage memory (allocate) for keys and values.
-/// khash.h does not make its own copy of the key or value.
-///
+//
+// map.c: khash.h wrapper
+//
+// NOTE: Callers must manage memory (allocate) for keys and values.
+// khash.h does not make its own copy of the key or value.
+//
#include <stdlib.h>
#include <stdbool.h>
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 602648c27c..5c9367ab01 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -214,7 +214,7 @@ pos_T *movemark(int count)
pos_T *pos;
xfmark_T *jmp;
- cleanup_jumplist();
+ cleanup_jumplist(curwin, true);
if (curwin->w_jumplistlen == 0) /* nothing to jump to */
return (pos_T *)NULL;
@@ -781,13 +781,11 @@ void ex_jumps(exarg_T *eap)
int i;
char_u *name;
- cleanup_jumplist();
- /* Highlight title */
+ cleanup_jumplist(curwin, true);
+ // Highlight title
MSG_PUTS_TITLE(_("\n jump line col file/text"));
for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) {
if (curwin->w_jumplist[i].fmark.mark.lnum != 0) {
- if (curwin->w_jumplist[i].fmark.fnum == 0)
- fname2fnum(&curwin->w_jumplist[i]);
name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
if (name == NULL) /* file name not available */
continue;
@@ -1069,19 +1067,24 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
{ \
posp->lnum += lnum_amount; \
assert(col_amount > INT_MIN && col_amount <= INT_MAX); \
- if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
+ if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \
posp->col = 0; \
- else \
+ } else if (posp->col < spaces_removed) { \
+ posp->col = (int)col_amount + spaces_removed; \
+ } else { \
posp->col += (colnr_T)col_amount; \
+ } \
} \
}
-/*
- * Adjust marks in line "lnum" at column "mincol" and further: add
- * "lnum_amount" to the line number and add "col_amount" to the column
- * position.
- */
-void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount)
+// Adjust marks in line "lnum" at column "mincol" and further: add
+// "lnum_amount" to the line number and add "col_amount" to the column
+// position.
+// "spaces_removed" is the number of spaces that were removed, matters when the
+// cursor is inside them.
+void mark_col_adjust(
+ linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount,
+ int spaces_removed)
{
int i;
int fnum = curbuf->b_fnum;
@@ -1151,41 +1154,69 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a
}
}
-/*
- * When deleting lines, this may create duplicate marks in the
- * jumplist. They will be removed here for the current window.
- */
-void cleanup_jumplist(void)
+// When deleting lines, this may create duplicate marks in the
+// jumplist. They will be removed here for the specified window.
+// When "loadfiles" is true first ensure entries have the "fnum" field set
+// (this may be a bit slow).
+void cleanup_jumplist(win_T *wp, bool loadfiles)
{
int i;
- int from, to;
- to = 0;
- for (from = 0; from < curwin->w_jumplistlen; ++from) {
- if (curwin->w_jumplistidx == from)
- curwin->w_jumplistidx = to;
- for (i = from + 1; i < curwin->w_jumplistlen; ++i)
- if (curwin->w_jumplist[i].fmark.fnum
- == curwin->w_jumplist[from].fmark.fnum
- && curwin->w_jumplist[from].fmark.fnum != 0
- && curwin->w_jumplist[i].fmark.mark.lnum
- == curwin->w_jumplist[from].fmark.mark.lnum)
+ if (loadfiles) {
+ // If specified, load all the files from the jump list. This is
+ // needed to properly clean up duplicate entries, but will take some
+ // time.
+ for (i = 0; i < wp->w_jumplistlen; i++) {
+ if ((wp->w_jumplist[i].fmark.fnum == 0)
+ && (wp->w_jumplist[i].fmark.mark.lnum != 0)) {
+ fname2fnum(&wp->w_jumplist[i]);
+ }
+ }
+ }
+
+ int to = 0;
+ for (int from = 0; from < wp->w_jumplistlen; from++) {
+ if (wp->w_jumplistidx == from) {
+ wp->w_jumplistidx = to;
+ }
+ for (i = from + 1; i < wp->w_jumplistlen; i++) {
+ if (wp->w_jumplist[i].fmark.fnum
+ == wp->w_jumplist[from].fmark.fnum
+ && wp->w_jumplist[from].fmark.fnum != 0
+ && wp->w_jumplist[i].fmark.mark.lnum
+ == wp->w_jumplist[from].fmark.mark.lnum) {
break;
- if (i >= curwin->w_jumplistlen) { // no duplicate
+ }
+ }
+ if (i >= wp->w_jumplistlen) { // no duplicate
if (to != from) {
- // Not using curwin->w_jumplist[to++] = curwin->w_jumplist[from] because
+ // Not using wp->w_jumplist[to++] = wp->w_jumplist[from] because
// this way valgrind complains about overlapping source and destination
// in memcpy() call. (clang-3.6.0, debug build with -DEXITFREE).
- curwin->w_jumplist[to] = curwin->w_jumplist[from];
+ wp->w_jumplist[to] = wp->w_jumplist[from];
}
to++;
} else {
- xfree(curwin->w_jumplist[from].fname);
+ xfree(wp->w_jumplist[from].fname);
+ }
+ }
+ if (wp->w_jumplistidx == wp->w_jumplistlen) {
+ wp->w_jumplistidx = to;
+ }
+ wp->w_jumplistlen = to;
+
+ // When pointer is below last jump, remove the jump if it matches the current
+ // line. This avoids useless/phantom jumps. #9805
+ if (wp->w_jumplistlen
+ && wp->w_jumplistidx == wp->w_jumplistlen) {
+ const xfmark_T *fm_last = &wp->w_jumplist[wp->w_jumplistlen - 1];
+ if (fm_last->fmark.fnum == curbuf->b_fnum
+ && fm_last->fmark.mark.lnum == wp->w_cursor.lnum) {
+ xfree(fm_last->fname);
+ wp->w_jumplistlen--;
+ wp->w_jumplistidx--;
}
}
- if (curwin->w_jumplistidx == curwin->w_jumplistlen)
- curwin->w_jumplistidx = to;
- curwin->w_jumplistlen = to;
}
/*
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 5ed2b4c564..8cc91146cc 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -555,6 +555,24 @@ size_t mb_string2cells(const char_u *str)
return clen;
}
+/// Get the number of cells occupied by string `str` with maximum length `size`
+///
+/// @param str The source string, may not be NULL, must be a NUL-terminated
+/// string.
+/// @param size maximum length of string. It will terminate on earlier NUL.
+/// @return The number of cells occupied by string `str`
+size_t mb_string2cells_len(const char_u *str, size_t size)
+{
+ size_t clen = 0;
+
+ for (const char_u *p = str; *p != NUL && p < str+size;
+ p += utf_ptr2len_len(p, size+(p-str))) {
+ clen += utf_ptr2cells(p);
+ }
+
+ return clen;
+}
+
/// Convert a UTF-8 byte sequence to a wide character
///
/// If the sequence is illegal or truncated by a NUL then the first byte is
@@ -1045,75 +1063,78 @@ int utf_class_tab(const int c, const uint64_t *const chartab)
unsigned int first;
unsigned int last;
unsigned int class;
- } classes[] =
- {
- {0x037e, 0x037e, 1}, /* Greek question mark */
- {0x0387, 0x0387, 1}, /* Greek ano teleia */
- {0x055a, 0x055f, 1}, /* Armenian punctuation */
- {0x0589, 0x0589, 1}, /* Armenian full stop */
- {0x05be, 0x05be, 1},
- {0x05c0, 0x05c0, 1},
- {0x05c3, 0x05c3, 1},
- {0x05f3, 0x05f4, 1},
- {0x060c, 0x060c, 1},
- {0x061b, 0x061b, 1},
- {0x061f, 0x061f, 1},
- {0x066a, 0x066d, 1},
- {0x06d4, 0x06d4, 1},
- {0x0700, 0x070d, 1}, /* Syriac punctuation */
- {0x0964, 0x0965, 1},
- {0x0970, 0x0970, 1},
- {0x0df4, 0x0df4, 1},
- {0x0e4f, 0x0e4f, 1},
- {0x0e5a, 0x0e5b, 1},
- {0x0f04, 0x0f12, 1},
- {0x0f3a, 0x0f3d, 1},
- {0x0f85, 0x0f85, 1},
- {0x104a, 0x104f, 1}, /* Myanmar punctuation */
- {0x10fb, 0x10fb, 1}, /* Georgian punctuation */
- {0x1361, 0x1368, 1}, /* Ethiopic punctuation */
- {0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
- {0x1680, 0x1680, 0},
- {0x169b, 0x169c, 1},
- {0x16eb, 0x16ed, 1},
- {0x1735, 0x1736, 1},
- {0x17d4, 0x17dc, 1}, /* Khmer punctuation */
- {0x1800, 0x180a, 1}, /* Mongolian punctuation */
- {0x2000, 0x200b, 0}, /* spaces */
- {0x200c, 0x2027, 1}, /* punctuation and symbols */
- {0x2028, 0x2029, 0},
- {0x202a, 0x202e, 1}, /* punctuation and symbols */
- {0x202f, 0x202f, 0},
- {0x2030, 0x205e, 1}, /* punctuation and symbols */
- {0x205f, 0x205f, 0},
- {0x2060, 0x27ff, 1}, /* punctuation and symbols */
- {0x2070, 0x207f, 0x2070}, /* superscript */
- {0x2080, 0x2094, 0x2080}, /* subscript */
- {0x20a0, 0x27ff, 1}, /* all kinds of symbols */
- {0x2800, 0x28ff, 0x2800}, /* braille */
- {0x2900, 0x2998, 1}, /* arrows, brackets, etc. */
- {0x29d8, 0x29db, 1},
- {0x29fc, 0x29fd, 1},
- {0x2e00, 0x2e7f, 1}, /* supplemental punctuation */
- {0x3000, 0x3000, 0}, /* ideographic space */
- {0x3001, 0x3020, 1}, /* ideographic punctuation */
- {0x3030, 0x3030, 1},
- {0x303d, 0x303d, 1},
- {0x3040, 0x309f, 0x3040}, /* Hiragana */
- {0x30a0, 0x30ff, 0x30a0}, /* Katakana */
- {0x3300, 0x9fff, 0x4e00}, /* CJK Ideographs */
- {0xac00, 0xd7a3, 0xac00}, /* Hangul Syllables */
- {0xf900, 0xfaff, 0x4e00}, /* CJK Ideographs */
- {0xfd3e, 0xfd3f, 1},
- {0xfe30, 0xfe6b, 1}, /* punctuation forms */
- {0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
- {0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
- {0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
- {0xff5b, 0xff65, 1}, /* half/fullwidth ASCII */
- {0x20000, 0x2a6df, 0x4e00}, /* CJK Ideographs */
- {0x2a700, 0x2b73f, 0x4e00}, /* CJK Ideographs */
- {0x2b740, 0x2b81f, 0x4e00}, /* CJK Ideographs */
- {0x2f800, 0x2fa1f, 0x4e00}, /* CJK Ideographs */
+ } classes[] = {
+ { 0x037e, 0x037e, 1 }, // Greek question mark
+ { 0x0387, 0x0387, 1 }, // Greek ano teleia
+ { 0x055a, 0x055f, 1 }, // Armenian punctuation
+ { 0x0589, 0x0589, 1 }, // Armenian full stop
+ { 0x05be, 0x05be, 1 },
+ { 0x05c0, 0x05c0, 1 },
+ { 0x05c3, 0x05c3, 1 },
+ { 0x05f3, 0x05f4, 1 },
+ { 0x060c, 0x060c, 1 },
+ { 0x061b, 0x061b, 1 },
+ { 0x061f, 0x061f, 1 },
+ { 0x066a, 0x066d, 1 },
+ { 0x06d4, 0x06d4, 1 },
+ { 0x0700, 0x070d, 1 }, // Syriac punctuation
+ { 0x0964, 0x0965, 1 },
+ { 0x0970, 0x0970, 1 },
+ { 0x0df4, 0x0df4, 1 },
+ { 0x0e4f, 0x0e4f, 1 },
+ { 0x0e5a, 0x0e5b, 1 },
+ { 0x0f04, 0x0f12, 1 },
+ { 0x0f3a, 0x0f3d, 1 },
+ { 0x0f85, 0x0f85, 1 },
+ { 0x104a, 0x104f, 1 }, // Myanmar punctuation
+ { 0x10fb, 0x10fb, 1 }, // Georgian punctuation
+ { 0x1361, 0x1368, 1 }, // Ethiopic punctuation
+ { 0x166d, 0x166e, 1 }, // Canadian Syl. punctuation
+ { 0x1680, 0x1680, 0 },
+ { 0x169b, 0x169c, 1 },
+ { 0x16eb, 0x16ed, 1 },
+ { 0x1735, 0x1736, 1 },
+ { 0x17d4, 0x17dc, 1 }, // Khmer punctuation
+ { 0x1800, 0x180a, 1 }, // Mongolian punctuation
+ { 0x2000, 0x200b, 0 }, // spaces
+ { 0x200c, 0x2027, 1 }, // punctuation and symbols
+ { 0x2028, 0x2029, 0 },
+ { 0x202a, 0x202e, 1 }, // punctuation and symbols
+ { 0x202f, 0x202f, 0 },
+ { 0x2030, 0x205e, 1 }, // punctuation and symbols
+ { 0x205f, 0x205f, 0 },
+ { 0x2060, 0x27ff, 1 }, // punctuation and symbols
+ { 0x2070, 0x207f, 0x2070 }, // superscript
+ { 0x2080, 0x2094, 0x2080 }, // subscript
+ { 0x20a0, 0x27ff, 1 }, // all kinds of symbols
+ { 0x2800, 0x28ff, 0x2800 }, // braille
+ { 0x2900, 0x2998, 1 }, // arrows, brackets, etc.
+ { 0x29d8, 0x29db, 1 },
+ { 0x29fc, 0x29fd, 1 },
+ { 0x2e00, 0x2e7f, 1 }, // supplemental punctuation
+ { 0x3000, 0x3000, 0 }, // ideographic space
+ { 0x3001, 0x3020, 1 }, // ideographic punctuation
+ { 0x3030, 0x3030, 1 },
+ { 0x303d, 0x303d, 1 },
+ { 0x3040, 0x309f, 0x3040 }, // Hiragana
+ { 0x30a0, 0x30ff, 0x30a0 }, // Katakana
+ { 0x3300, 0x9fff, 0x4e00 }, // CJK Ideographs
+ { 0xac00, 0xd7a3, 0xac00 }, // Hangul Syllables
+ { 0xf900, 0xfaff, 0x4e00 }, // CJK Ideographs
+ { 0xfd3e, 0xfd3f, 1 },
+ { 0xfe30, 0xfe6b, 1 }, // punctuation forms
+ { 0xff00, 0xff0f, 1 }, // half/fullwidth ASCII
+ { 0xff1a, 0xff20, 1 }, // half/fullwidth ASCII
+ { 0xff3b, 0xff40, 1 }, // half/fullwidth ASCII
+ { 0xff5b, 0xff65, 1 }, // half/fullwidth ASCII
+ { 0x1d000, 0x1d24f, 1 }, // Musical notation
+ { 0x1d400, 0x1d7ff, 1 }, // Mathematical Alphanumeric Symbols
+ { 0x1f000, 0x1f2ff, 1 }, // Game pieces; enclosed characters
+ { 0x1f300, 0x1f9ff, 1 }, // Many symbol blocks
+ { 0x20000, 0x2a6df, 0x4e00 }, // CJK Ideographs
+ { 0x2a700, 0x2b73f, 0x4e00 }, // CJK Ideographs
+ { 0x2b740, 0x2b81f, 0x4e00 }, // CJK Ideographs
+ { 0x2f800, 0x2fa1f, 0x4e00 }, // CJK Ideographs
};
int bot = 0;
int top = ARRAY_SIZE(classes) - 1;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 662eda3c7c..a071314453 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -71,6 +71,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/os/process.h"
#include "nvim/os/input.h"
#ifndef UNIX /* it's in os/unix_defs.h for Unix */
@@ -348,7 +349,7 @@ int ml_open(buf_T *buf)
/*
* Allocate first data block and create an empty line 1.
*/
- hp = ml_new_data(mfp, FALSE, 1);
+ hp = ml_new_data(mfp, false, 1);
if (hp->bh_bnum != 2) {
IEMSG(_("E298: Didn't get block nr 2?"));
goto error;
@@ -763,7 +764,7 @@ void ml_recover(void)
long error;
int cannot_open;
linenr_T line_count;
- int has_error;
+ bool has_error;
int idx;
int top;
int txt_start;
@@ -983,8 +984,9 @@ void ml_recover(void)
* Now that we are sure that the file is going to be recovered, clear the
* contents of the current buffer.
*/
- while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
- ml_delete((linenr_T)1, FALSE);
+ while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
+ ml_delete((linenr_T)1, false);
+ }
/*
* Try reading the original file to obtain the values of 'fileformat',
@@ -1033,8 +1035,8 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
- (colnr_T)0, TRUE);
- } else { /* there is a block */
+ (colnr_T)0, true);
+ } else { // there is a block
pp = hp->bh_data;
if (pp->pb_id == PTR_ID) { /* it is a pointer block */
/* check line count when using pointer block first time */
@@ -1044,15 +1046,15 @@ void ml_recover(void)
if (line_count != 0) {
++error;
ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
}
if (pp->pb_count == 0) {
ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
- (colnr_T)0, TRUE);
- ++error;
- } else if (idx < (int)pp->pb_count) { /* go a block deeper */
+ (colnr_T)0, true);
+ error++;
+ } else if (idx < (int)pp->pb_count) { // go a block deeper
if (pp->pb_pointer[idx].pe_bnum < 0) {
/*
* Data block with negative block number.
@@ -1072,7 +1074,7 @@ void ml_recover(void)
if (cannot_open) {
++error;
ml_append(lnum++, (char_u *)_("???LINES MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
++idx; /* get same block again for next index */
continue;
@@ -1102,23 +1104,21 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
} else {
- /*
- * it is a data block
- * Append all the lines in this block
- */
- has_error = FALSE;
- /*
- * check length of block
- * if wrong, use length in pointer block
- */
+ // it is a data block
+ // Append all the lines in this block
+ has_error = false;
+ // check length of block
+ // if wrong, use length in pointer block
if (page_count * mfp->mf_page_size != dp->db_txt_end) {
- ml_append(lnum++,
- (char_u *)_("??? from here until ???END lines may be messed up"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may be messed up"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
dp->db_txt_end = page_count * mfp->mf_page_size;
}
@@ -1130,12 +1130,13 @@ void ml_recover(void)
* if wrong, use count in data block
*/
if (line_count != dp->db_line_count) {
- ml_append(lnum++,
- (char_u *)_(
- "??? from here until ???END lines may have been inserted/deleted"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may have been inserted/deleted"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
}
for (i = 0; i < dp->db_line_count; ++i) {
@@ -1146,11 +1147,11 @@ void ml_recover(void)
++error;
} else
p = (char_u *)dp + txt_start;
- ml_append(lnum++, p, (colnr_T)0, TRUE);
+ ml_append(lnum++, p, (colnr_T)0, true);
+ }
+ if (has_error) {
+ ml_append(lnum++, (char_u *)_("???END"), (colnr_T)0, true);
}
- if (has_error)
- ml_append(lnum++, (char_u *)_("???END"),
- (colnr_T)0, TRUE);
}
}
}
@@ -1201,7 +1202,7 @@ void ml_recover(void)
*/
while (curbuf->b_ml.ml_line_count > lnum
&& !(curbuf->b_ml.ml_flags & ML_EMPTY))
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
+ ml_delete(curbuf->b_ml.ml_line_count, false);
curbuf->b_flags |= BF_RECOVERED;
recoverymode = FALSE;
@@ -1453,14 +1454,47 @@ static char *make_percent_swname(const char *dir, char *name)
return d;
}
-#ifdef UNIX
static bool process_still_running;
-#endif
-/*
- * Give information about an existing swap file.
- * Returns timestamp (0 when unknown).
- */
+/// Return information found in swapfile "fname" in dictionary "d".
+/// This is used by the swapinfo() function.
+void get_b0_dict(const char *fname, dict_T *d)
+{
+ int fd;
+ struct block0 b0;
+
+ if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) {
+ if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ if (ml_check_b0_id(&b0) == FAIL) {
+ tv_dict_add_str(d, S_LEN("error"), "Not a swap file");
+ } else if (b0_magic_wrong(&b0)) {
+ tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch");
+ } else {
+ // We have swap information.
+ tv_dict_add_str_len(d, S_LEN("version"), (char *)b0.b0_version, 10);
+ tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname,
+ B0_UNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname,
+ B0_HNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("fname"), (char *)b0.b0_fname,
+ B0_FNAME_SIZE_ORG);
+
+ tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid));
+ tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime));
+ tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0);
+ tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
+ }
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot read file");
+ }
+ close(fd);
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot open file");
+ }
+}
+
+/// Give information about an existing swap file.
+/// Returns timestamp (0 when unknown).
static time_t swapfile_info(char_u *fname)
{
assert(fname != NULL);
@@ -1530,12 +1564,10 @@ static time_t swapfile_info(char_u *fname)
if (char_to_long(b0.b0_pid) != 0L) {
MSG_PUTS(_("\n process ID: "));
msg_outnum(char_to_long(b0.b0_pid));
-#if defined(UNIX)
- if (kill((pid_t)char_to_long(b0.b0_pid), 0) == 0) {
+ if (os_proc_running((int)char_to_long(b0.b0_pid))) {
MSG_PUTS(_(" (STILL RUNNING)"));
process_still_running = true;
}
-#endif
}
if (b0_magic_wrong(&b0)) {
@@ -1552,6 +1584,51 @@ static time_t swapfile_info(char_u *fname)
return x;
}
+/// Returns TRUE if the swap file looks OK and there are no changes, thus it
+/// can be safely deleted.
+static time_t swapfile_unchanged(char *fname)
+{
+ struct block0 b0;
+ int ret = true;
+
+ // Swap file must exist.
+ if (!os_path_exists((char_u *)fname)) {
+ return false;
+ }
+
+ // must be able to read the first block
+ int fd = os_open(fname, O_RDONLY, 0);
+ if (fd < 0) {
+ return false;
+ }
+ if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0)) {
+ close(fd);
+ return false;
+ }
+
+ // the ID and magic number must be correct
+ if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0)) {
+ ret = false;
+ }
+
+ // must be unchanged
+ if (b0.b0_dirty) {
+ ret = false;
+ }
+
+ // process must be known and not running.
+ long pid = char_to_long(b0.b0_pid);
+ if (pid == 0L || os_proc_running((int)pid)) {
+ ret = false;
+ }
+
+ // TODO(bram): Should we check if the swap file was created on the current
+ // system? And the current user?
+
+ close(fd);
+ return ret;
+}
+
static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
FUNC_ATTR_NONNULL_ALL
{
@@ -1737,7 +1814,7 @@ char_u *
ml_get_buf (
buf_T *buf,
linenr_T lnum,
- int will_change /* line will be changed */
+ bool will_change // line will be changed
)
{
bhdr_T *hp;
@@ -1823,12 +1900,11 @@ int ml_line_alloced(void)
*
* return FAIL for failure, OK otherwise
*/
-int
-ml_append (
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+int ml_append(
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
/* When starting up, we might still need to create the memfile */
@@ -1844,13 +1920,12 @@ ml_append (
* Like ml_append() but for an arbitrary buffer. The buffer must already have
* a memline.
*/
-int
-ml_append_buf (
+int ml_append_buf(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
if (buf->b_ml.ml_mfp == NULL)
@@ -1861,14 +1936,13 @@ ml_append_buf (
return ml_append_int(buf, lnum, line, len, newfile, FALSE);
}
-static int
-ml_append_int (
+static int ml_append_int(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of line, including NUL, or 0 */
- int newfile, /* flag, see above */
- int mark /* mark the new line */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of line, including NUL, or 0
+ bool newfile, // flag, see above
+ int mark // mark the new line
)
{
int i;
@@ -2351,13 +2425,13 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy)
///
/// @param message Show "--No lines in buffer--" message.
/// @return FAIL for failure, OK otherwise
-int ml_delete(linenr_T lnum, int message)
+int ml_delete(linenr_T lnum, bool message)
{
ml_flush_line(curbuf);
return ml_delete_int(curbuf, lnum, message);
}
-static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)
+static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
{
bhdr_T *hp;
memfile_T *mfp;
@@ -2677,17 +2751,15 @@ static void ml_flush_line(buf_T *buf)
/* The else case is already covered by the insert and delete */
ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
} else {
- /*
- * Cannot do it in one data block: Delete and append.
- * Append first, because ml_delete_int() cannot delete the
- * last line in a buffer, which causes trouble for a buffer
- * that has only one line.
- * Don't forget to copy the mark!
- */
- /* How about handling errors??? */
- (void)ml_append_int(buf, lnum, new_line, new_len, FALSE,
- (dp->db_index[idx] & DB_MARKED));
- (void)ml_delete_int(buf, lnum, FALSE);
+ // Cannot do it in one data block: Delete and append.
+ // Append first, because ml_delete_int() cannot delete the
+ // last line in a buffer, which causes trouble for a buffer
+ // that has only one line.
+ // Don't forget to copy the mark!
+ // How about handling errors???
+ (void)ml_append_int(buf, lnum, new_line, new_len, false,
+ (dp->db_index[idx] & DB_MARKED));
+ (void)ml_delete_int(buf, lnum, false);
}
}
xfree(new_line);
@@ -2701,7 +2773,7 @@ static void ml_flush_line(buf_T *buf)
/*
* create a new, empty, data block
*/
-static bhdr_T *ml_new_data(memfile_T *mfp, int negative, int page_count)
+static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count)
{
assert(page_count >= 0);
bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count);
@@ -3353,17 +3425,24 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
-#ifdef UNIX
process_still_running = false;
-#endif
- /*
- * If there is a SwapExists autocommand and we can handle
- * the response, trigger it. It may return 0 to ask the
- * user anyway.
- */
- if (swap_exists_action != SEA_NONE
- && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf))
- choice = do_swapexists(buf, (char_u *) fname);
+ // It's safe to delete the swap file if all these are true:
+ // - the edited file exists
+ // - the swap file has no changes and looks OK
+ if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
+ choice = 4;
+ if (p_verbose > 0) {
+ verb_msg(_("Found a swap file that is not useful, deleting it"));
+ }
+ }
+
+ // If there is a SwapExists autocommand and we can handle the
+ // response, trigger it. It may return 0 to ask the user anyway.
+ if (choice == 0
+ && swap_exists_action != SEA_NONE
+ && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) {
+ choice = do_swapexists(buf, (char_u *)fname);
+ }
if (choice == 0) {
// Show info about the existing swap file.
@@ -3395,21 +3474,18 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"),
(char_u *)name,
-# if defined(UNIX)
process_still_running
? (char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Quit\n&Abort") :
-# endif
(char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
-# if defined(UNIX)
- if (process_still_running && choice >= 4)
- choice++; /* Skip missing "Delete it" button */
-# endif
+ if (process_still_running && choice >= 4) {
+ choice++; // Skip missing "Delete it" button.
+ }
xfree(name);
// pretend screen didn't scroll, need redraw anyway
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index aea297fce2..472481bb30 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -171,7 +171,7 @@ ex_menu(exarg_T *eap)
if (enable != kNone) {
// Change sensitivity of the menu.
// For the PopUp menu, remove a menu for each mode separately.
- // Careful: menu_nable_recurse() changes menu_path.
+ // Careful: menu_enable_recurse() changes menu_path.
if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus
menu_path = (char_u *)"";
}
@@ -180,11 +180,11 @@ ex_menu(exarg_T *eap)
for (i = 0; i < MENU_INDEX_TIP; ++i)
if (modes & (1 << i)) {
p = popup_mode_name(menu_path, i);
- menu_nable_recurse(root_menu, p, MENU_ALL_MODES, enable);
+ menu_enable_recurse(root_menu, p, MENU_ALL_MODES, enable);
xfree(p);
}
}
- menu_nable_recurse(root_menu, menu_path, modes, enable);
+ menu_enable_recurse(root_menu, menu_path, modes, enable);
} else if (unmenu) {
/*
* Delete menu(s).
@@ -485,7 +485,10 @@ erret:
* Set the (sub)menu with the given name to enabled or disabled.
* Called recursively.
*/
-static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable)
+static int menu_enable_recurse(vimmenu_T *menu,
+ char_u *name,
+ int modes,
+ int enable)
{
char_u *p;
@@ -503,13 +506,14 @@ static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enab
EMSG(_(e_notsubmenu));
return FAIL;
}
- if (menu_nable_recurse(menu->children, p, modes, enable)
- == FAIL)
+ if (menu_enable_recurse(menu->children, p, modes, enable) == FAIL) {
return FAIL;
- } else if (enable)
+ }
+ } else if (enable) {
menu->enabled |= modes;
- else
+ } else {
menu->enabled &= ~modes;
+ }
/*
* When name is empty, we are doing all menu items for the given
@@ -770,15 +774,12 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
if (menu_name_equal(name, menu)) {
// Found menu
if (*p != NUL && menu->children == NULL) {
- if (*p != NUL) {
EMSG(_(e_notsubmenu));
return NULL;
- } else if ((menu->modes & modes) == 0x0) {
- EMSG(_(e_othermode));
- return NULL;
- }
- }
- if (*p == NUL) { // found a full match
+ } else if ((menu->modes & modes) == 0x0) {
+ EMSG(_(e_othermode));
+ return NULL;
+ } else if (*p == NUL) { // found a full match
return menu;
}
break;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index b4aa333a48..cb83d6482c 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -133,15 +133,11 @@ int msg(char_u *s)
return msg_attr_keep(s, 0, false, false);
}
-/*
- * Like msg() but keep it silent when 'verbosefile' is set.
- */
-int verb_msg(char_u *s)
+/// Like msg() but keep it silent when 'verbosefile' is set.
+int verb_msg(char *s)
{
- int n;
-
verbose_enter();
- n = msg_attr_keep(s, 0, false, false);
+ int n = msg_attr_keep((char_u *)s, 0, false, false);
verbose_leave();
return n;
@@ -384,7 +380,7 @@ int smsg(char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg(IObuff);
}
@@ -395,11 +391,22 @@ int smsg_attr(int attr, char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg_attr((const char *)IObuff, attr);
}
+int smsg_attr_keep(int attr, char *s, ...)
+ FUNC_ATTR_PRINTF(2, 3)
+{
+ va_list arglist;
+
+ va_start(arglist, s);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
+ va_end(arglist);
+ return msg_attr_keep(IObuff, attr, true, false);
+}
+
/*
* Remember the last sourcing name/lnum used in an error message, so that it
* isn't printed each time when it didn't change.
@@ -662,7 +669,7 @@ bool emsgf_multiline(const char *const fmt, ...)
}
va_start(ap, fmt);
- vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
va_end(ap);
ret = emsg_multiline(errbuf, true);
@@ -678,7 +685,7 @@ static bool emsgfv(const char *fmt, va_list ap)
return true;
}
- vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
return emsg((const char_u *)errbuf);
}
@@ -726,7 +733,7 @@ void msg_schedule_emsgf(const char *const fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap);
va_end(ap);
char *s = xstrdup((char *)IObuff);
@@ -1826,7 +1833,7 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
va_list ap;
va_start(ap, fmt);
- const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL);
+ const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
va_end(ap);
msg_scroll = true;
@@ -1873,7 +1880,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
}
// Concat pieces with the same highlight
ga_concat_len(&msg_ext_last_chunk, (char *)str,
- strnlen((char *)str, maxlen));
+ strnlen((char *)str, maxlen)); // -V781
return;
}
@@ -3001,6 +3008,8 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
} else {
keep_msg_attr = 0;
}
+ msg_ext_set_kind("wmsg");
+
if (msg_attr((const char *)message, keep_msg_attr) && msg_scrolled == 0) {
set_keep_msg(message, keep_msg_attr);
}
@@ -3341,6 +3350,7 @@ void display_confirm_msg(void)
// Avoid that 'q' at the more prompt truncates the message here.
confirm_msg_used++;
if (confirm_msg != NULL) {
+ msg_ext_set_kind("confirm");
msg_puts_attr((const char *)confirm_msg, HL_ATTR(HLF_M));
}
confirm_msg_used--;
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index a8cfc2d700..0008409731 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -710,9 +710,6 @@ open_line (
less_cols_off++;
}
}
- if (*p_extra != NUL) {
- did_ai = false; // append some text, don't truncate now
- }
/* columns for marks adjusted for removed columns */
less_cols = (int)(p_extra - saved_line);
@@ -748,9 +745,9 @@ open_line (
if (dir == BACKWARD)
--curwin->w_cursor.lnum;
if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) {
- if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, FALSE)
- == FAIL)
+ if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
goto theend;
+ }
// Postpone calling changed_lines(), because it would mess up folding
// with markers.
// Skip mark_adjust when adding a line after the last one, there can't
@@ -848,10 +845,11 @@ open_line (
/* Move marks after the line break to the new line. */
if (flags & OPENLINE_MARKFIX)
mark_col_adjust(curwin->w_cursor.lnum,
- curwin->w_cursor.col + less_cols_off,
- 1L, (long)-less_cols);
- } else
+ curwin->w_cursor.col + less_cols_off,
+ 1L, (long)-less_cols, 0);
+ } else {
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
+ }
}
/*
@@ -1749,8 +1747,8 @@ del_lines (
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
break;
- ml_delete(first, TRUE);
- ++n;
+ ml_delete(first, true);
+ n++;
/* If we delete the last line in the file, stop */
if (first > curbuf->b_ml.ml_line_count)
@@ -2177,7 +2175,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
/* when the cursor line is changed always trigger CursorMoved */
if (lnum <= curwin->w_cursor.lnum
&& lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum)
- last_cursormoved.lnum = 0;
+ curwin->w_last_cursormoved.lnum = 0;
}
/*
@@ -2250,6 +2248,7 @@ change_warning (
if (msg_row == Rows - 1)
msg_col = col;
msg_source(HL_ATTR(HLF_W));
+ msg_ext_set_kind("wmsg");
MSG_PUTS_ATTR(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST);
set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
@@ -2580,6 +2579,8 @@ void beep_flush(void)
// val is one of the BO_ values, e.g., BO_OPER
void vim_beep(unsigned val)
{
+ called_vim_beep = true;
+
if (emsg_silent == 0) {
if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
if (p_vb) {
@@ -2589,8 +2590,9 @@ void vim_beep(unsigned val)
}
}
- /* When 'verbose' is set and we are sourcing a script or executing a
- * function give the user a hint where the beep comes from. */
+ // When 'debug' contains "beep" produce a message. If we are sourcing
+ // a script or executing a function give the user a hint where the beep
+ // comes from.
if (vim_strchr(p_debug, 'e') != NULL) {
msg_source(HL_ATTR(HLF_W));
msg_attr(_("Beep!"), HL_ATTR(HLF_W));
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 7aa7f922c1..e076543614 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -692,7 +692,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ (int)wp->w_p_fdc
- + (signcolumn_on(wp) ? win_signcol_width(wp) : 0);
+ + (win_signcol_count(wp) * win_signcol_width(wp));
}
int curwin_col_off(void)
@@ -1524,9 +1524,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Count screen lines that are below the window. */
scrolled += loff.height;
if (loff.lnum == curwin->w_botline
- && boff.fill == 0
- )
+ && loff.fill == 0) {
scrolled -= curwin->w_empty_rows;
+ }
}
if (boff.lnum < curbuf->b_ml.ml_line_count) {
@@ -2001,10 +2001,7 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
return;
}
-/* #define KEEP_SCREEN_LINE */
-/*
- * Scroll 'scroll' lines up or down.
- */
+// Scroll 'scroll' lines up or down.
void halfpage(bool flag, linenr_T Prenum)
{
long scrolled = 0;
@@ -2039,13 +2036,11 @@ void halfpage(bool flag, linenr_T Prenum)
++curwin->w_topline;
curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
-#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
++curwin->w_cursor.lnum;
curwin->w_valid &=
~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
-#endif
}
curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
scrolled += i;
@@ -2070,10 +2065,7 @@ void halfpage(bool flag, linenr_T Prenum)
}
}
-#ifndef KEEP_SCREEN_LINE
- /*
- * When hit bottom of the file: move cursor down.
- */
+ // When hit bottom of the file: move cursor down.
if (n > 0) {
if (hasAnyFolding(curwin)) {
while (--n >= 0
@@ -2086,18 +2078,6 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_cursor.lnum += n;
check_cursor_lnum();
}
-#else
- /* try to put the cursor in the same screen line */
- while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
- && curwin->w_cursor.lnum < curwin->w_botline - 1) {
- scrolled -= plines(curwin->w_cursor.lnum);
- if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
- break;
- (void)hasFolding(curwin->w_cursor.lnum, NULL,
- &curwin->w_cursor.lnum);
- ++curwin->w_cursor.lnum;
- }
-#endif
} else {
/*
* scroll the text down
@@ -2119,17 +2099,13 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
VALID_BOTLINE|VALID_BOTLINE_AP);
scrolled += i;
-#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum > 1) {
--curwin->w_cursor.lnum;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
-#endif
}
-#ifndef KEEP_SCREEN_LINE
- /*
- * When hit top of the file: move cursor up.
- */
+
+ // When hit top of the file: move cursor up.
if (n > 0) {
if (curwin->w_cursor.lnum <= (linenr_T)n)
curwin->w_cursor.lnum = 1;
@@ -2142,18 +2118,6 @@ void halfpage(bool flag, linenr_T Prenum)
} else
curwin->w_cursor.lnum -= n;
}
-#else
- /* try to put the cursor in the same screen line */
- scrolled += n; /* move cursor when topline is 1 */
- while (curwin->w_cursor.lnum > curwin->w_topline
- && (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline)) {
- scrolled -= plines(curwin->w_cursor.lnum - 1);
- if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
- break;
- --curwin->w_cursor.lnum;
- foldAdjustCursor();
- }
-#endif
}
/* Move cursor to first line of closed fold. */
foldAdjustCursor();
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 76fbe407c2..3438949e2d 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -131,7 +131,7 @@ Object rpc_send_call(uint64_t id,
channel_incref(channel);
RpcState *rpc = &channel->rpc;
- uint64_t request_id = rpc->next_request_id++;
+ uint32_t request_id = rpc->next_request_id++;
// Send the msgpack-rpc request
send_request(channel, request_id, method_name, args);
@@ -281,23 +281,26 @@ static void parse_msgpack(Channel *channel)
// A not so uncommon cause for this might be deserializing objects with
// a high nesting level: msgpack will break when its internal parse stack
// size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default)
- send_error(channel, 0, "Invalid msgpack payload. "
- "This error can also happen when deserializing "
- "an object with high level of nesting");
+ send_error(channel, kMessageTypeRequest, 0,
+ "Invalid msgpack payload. "
+ "This error can also happen when deserializing "
+ "an object with high level of nesting");
}
}
+/// Handles requests and notifications received on the channel.
static void handle_request(Channel *channel, msgpack_object *request)
FUNC_ATTR_NONNULL_ALL
{
- uint64_t request_id;
+ uint32_t request_id;
Error error = ERROR_INIT;
- msgpack_rpc_validate(&request_id, request, &error);
+ MessageType type = msgpack_rpc_validate(&request_id, request, &error);
if (ERROR_SET(&error)) {
// Validation failed, send response with error
if (channel_write(channel,
serialize_response(channel->id,
+ type,
request_id,
&error,
NIL,
@@ -311,6 +314,7 @@ static void handle_request(Channel *channel, msgpack_object *request)
api_clear_error(&error);
return;
}
+ assert(type == kMessageTypeRequest || type == kMessageTypeNotification);
MsgpackRpcRequestHandler handler;
msgpack_object *method = msgpack_rpc_method(request);
@@ -326,13 +330,14 @@ static void handle_request(Channel *channel, msgpack_object *request)
}
if (ERROR_SET(&error)) {
- send_error(channel, request_id, error.msg);
+ send_error(channel, type, request_id, error.msg);
api_clear_error(&error);
api_free_array(args);
return;
}
RequestEvent *evdata = xmalloc(sizeof(RequestEvent));
+ evdata->type = type;
evdata->channel = channel;
evdata->handler = handler;
evdata->args = args;
@@ -343,39 +348,41 @@ static void handle_request(Channel *channel, msgpack_object *request)
if (is_get_mode && !input_blocking()) {
// Defer the event to a special queue used by os/input.c. #6247
- multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
+ multiqueue_put(ch_before_blocking_events, request_event, 1, evdata);
} else {
// Invoke immediately.
- on_request_event((void **)&evdata);
+ request_event((void **)&evdata);
}
} else {
- multiqueue_put(channel->events, on_request_event, 1, evdata);
+ multiqueue_put(channel->events, request_event, 1, evdata);
DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr);
}
}
-static void on_request_event(void **argv)
+/// Handles a message, depending on the type:
+/// - Request: invokes method and writes the response (or error).
+/// - Notification: invokes method (emits `nvim_error_event` on error).
+static void request_event(void **argv)
{
RequestEvent *e = argv[0];
Channel *channel = e->channel;
MsgpackRpcRequestHandler handler = e->handler;
- Array args = e->args;
- uint64_t request_id = e->request_id;
Error error = ERROR_INIT;
- Object result = handler.fn(channel->id, args, &error);
- if (request_id != NO_RESPONSE) {
- // send the response
+ Object result = handler.fn(channel->id, e->args, &error);
+ if (e->type == kMessageTypeRequest || ERROR_SET(&error)) {
+ // Send the response.
msgpack_packer response;
msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write);
channel_write(channel, serialize_response(channel->id,
- request_id,
+ e->type,
+ e->request_id,
&error,
result,
&out_buffer));
} else {
api_free_object(result);
}
- api_free_array(args);
+ api_free_array(e->args);
channel_decref(channel);
xfree(e);
api_clear_error(&error);
@@ -430,20 +437,21 @@ static void internal_read_event(void **argv)
wstream_release_wbuffer(buffer);
}
-static void send_error(Channel *channel, uint64_t id, char *err)
+static void send_error(Channel *chan, MessageType type, uint32_t id, char *err)
{
Error e = ERROR_INIT;
api_set_error(&e, kErrorTypeException, "%s", err);
- channel_write(channel, serialize_response(channel->id,
- id,
- &e,
- NIL,
- &out_buffer));
+ channel_write(chan, serialize_response(chan->id,
+ type,
+ id,
+ &e,
+ NIL,
+ &out_buffer));
api_clear_error(&e);
}
static void send_request(Channel *channel,
- uint64_t id,
+ uint32_t id,
const char *name,
Array args)
{
@@ -576,7 +584,7 @@ static bool is_rpc_response(msgpack_object *obj)
static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel)
{
- uint64_t response_id = obj->via.array.ptr[1].via.u64;
+ uint32_t response_id = (uint32_t)obj->via.array.ptr[1].via.u64;
if (kv_size(channel->rpc.call_stack) == 0) {
return false;
}
@@ -614,7 +622,7 @@ static void call_set_error(Channel *channel, char *msg, int loglevel)
}
static WBuffer *serialize_request(uint64_t channel_id,
- uint64_t request_id,
+ uint32_t request_id,
const String method,
Array args,
msgpack_sbuffer *sbuffer,
@@ -634,14 +642,15 @@ static WBuffer *serialize_request(uint64_t channel_id,
}
static WBuffer *serialize_response(uint64_t channel_id,
- uint64_t response_id,
+ MessageType type,
+ uint32_t response_id,
Error *err,
Object arg,
msgpack_sbuffer *sbuffer)
{
msgpack_packer pac;
msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
- if (ERROR_SET(err) && response_id == NO_RESPONSE) {
+ if (ERROR_SET(err) && type == kMessageTypeNotification) {
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(err->type));
ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index bfa7f7b87c..6ef8c027f0 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -13,23 +13,24 @@
typedef struct Channel Channel;
typedef struct {
- uint64_t request_id;
+ uint32_t request_id;
bool returned, errored;
Object result;
} ChannelCallFrame;
typedef struct {
+ MessageType type;
Channel *channel;
MsgpackRpcRequestHandler handler;
Array args;
- uint64_t request_id;
+ uint32_t request_id;
} RequestEvent;
typedef struct {
PMap(cstr_t) *subscribed_events;
bool closed;
msgpack_unpacker *unpacker;
- uint64_t next_request_id;
+ uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
Dictionary info;
} RpcState;
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 19cc31f6a6..3925dc546a 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -489,7 +489,7 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
}
/// Serializes a msgpack-rpc request or notification(id == 0)
-void msgpack_rpc_serialize_request(uint64_t request_id,
+void msgpack_rpc_serialize_request(uint32_t request_id,
const String method,
Array args,
msgpack_packer *pac)
@@ -499,7 +499,7 @@ void msgpack_rpc_serialize_request(uint64_t request_id,
msgpack_pack_int(pac, request_id ? 0 : 2);
if (request_id) {
- msgpack_pack_uint64(pac, request_id);
+ msgpack_pack_uint32(pac, request_id);
}
msgpack_rpc_from_string(method, pac);
@@ -507,7 +507,7 @@ void msgpack_rpc_serialize_request(uint64_t request_id,
}
/// Serializes a msgpack-rpc response
-void msgpack_rpc_serialize_response(uint64_t response_id,
+void msgpack_rpc_serialize_response(uint32_t response_id,
Error *err,
Object arg,
msgpack_packer *pac)
@@ -515,7 +515,7 @@ void msgpack_rpc_serialize_response(uint64_t response_id,
{
msgpack_pack_array(pac, 4);
msgpack_pack_int(pac, 1);
- msgpack_pack_uint64(pac, response_id);
+ msgpack_pack_uint32(pac, response_id);
if (ERROR_SET(err)) {
// error represented by a [type, message] array
@@ -561,58 +561,57 @@ static msgpack_object *msgpack_rpc_msg_id(msgpack_object *req)
return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER ? obj : NULL;
}
-void msgpack_rpc_validate(uint64_t *response_id,
- msgpack_object *req,
- Error *err)
+MessageType msgpack_rpc_validate(uint32_t *response_id, msgpack_object *req,
+ Error *err)
{
- // response id not known yet
-
- *response_id = NO_RESPONSE;
+ *response_id = 0;
// Validate the basic structure of the msgpack-rpc payload
if (req->type != MSGPACK_OBJECT_ARRAY) {
api_set_error(err, kErrorTypeValidation, "Message is not an array");
- return;
+ return kMessageTypeUnknown;
}
if (req->via.array.size == 0) {
api_set_error(err, kErrorTypeValidation, "Message is empty");
- return;
+ return kMessageTypeUnknown;
}
if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
api_set_error(err, kErrorTypeValidation, "Message type must be an integer");
- return;
+ return kMessageTypeUnknown;
}
- uint64_t type = req->via.array.ptr[0].via.u64;
+ MessageType type = (MessageType)req->via.array.ptr[0].via.u64;
if (type != kMessageTypeRequest && type != kMessageTypeNotification) {
api_set_error(err, kErrorTypeValidation, "Unknown message type");
- return;
+ return kMessageTypeUnknown;
}
if ((type == kMessageTypeRequest && req->via.array.size != 4)
|| (type == kMessageTypeNotification && req->via.array.size != 3)) {
api_set_error(err, kErrorTypeValidation,
"Request array size must be 4 (request) or 3 (notification)");
- return;
+ return type;
}
if (type == kMessageTypeRequest) {
msgpack_object *id_obj = msgpack_rpc_msg_id(req);
if (!id_obj) {
api_set_error(err, kErrorTypeValidation, "ID must be a positive integer");
- return;
+ return type;
}
- *response_id = id_obj->via.u64;
+ *response_id = (uint32_t)id_obj->via.u64;
}
if (!msgpack_rpc_method(req)) {
api_set_error(err, kErrorTypeValidation, "Method must be a string");
- return;
+ return type;
}
if (!msgpack_rpc_args(req)) {
api_set_error(err, kErrorTypeValidation, "Parameters must be an array");
- return;
+ return type;
}
+
+ return type;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f12abd362f..ca586cca29 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1187,12 +1187,12 @@ static void normal_check_cursor_moved(NormalState *s)
{
// Trigger CursorMoved if the cursor moved.
if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
- && !equalpos(last_cursormoved, curwin->w_cursor)) {
+ && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) {
if (has_event(EVENT_CURSORMOVED)) {
apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
}
- last_cursormoved = curwin->w_cursor;
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
}
@@ -4642,6 +4642,9 @@ static void nv_clear(cmdarg_T *cap)
if (!checkclearop(cap->oap)) {
/* Clear all syntax states to force resyncing. */
syn_stack_free_all(curwin->w_s);
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_s->b_syn_slow = false;
+ }
redraw_later(CLEAR);
}
}
@@ -6526,8 +6529,14 @@ static void n_start_visual_mode(int c)
*/
static void nv_window(cmdarg_T *cap)
{
- if (!checkclearop(cap->oap))
- do_window(cap->nchar, cap->count0, NUL); /* everything is in window.c */
+ if (cap->nchar == ':') {
+ // "CTRL-W :" is the same as typing ":"; useful in a terminal window
+ cap->cmdchar = ':';
+ cap->nchar = NUL;
+ nv_colon(cap);
+ } else if (!checkclearop(cap->oap)) {
+ do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
+ }
}
/*
@@ -8001,7 +8010,6 @@ static void nv_event(cmdarg_T *cap)
// lists or dicts being used.
may_garbage_collect = false;
multiqueue_process_events(main_loop.events);
- cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
finish_op = false;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 99dee939fc..216bab4dda 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -153,6 +153,10 @@ int get_op_type(int char1, int char2)
if (opchars[i][0] == char1 && opchars[i][1] == char2) {
break;
}
+ if (i == (int)(ARRAY_SIZE(opchars) - 1)) {
+ internal_error("get_op_type()");
+ break;
+ }
}
return i;
}
@@ -1408,8 +1412,10 @@ int op_delete(oparg_T *oap)
free_register(&y_regs[9]); /* free register "9 */
for (n = 9; n > 1; n--)
y_regs[n] = y_regs[n - 1];
- y_previous = &y_regs[1];
- y_regs[1].y_array = NULL; /* set register "1 to empty */
+ if (!is_append_register(oap->regname)) {
+ y_previous = &y_regs[1];
+ }
+ y_regs[1].y_array = NULL; // set register "1 to empty
reg = &y_regs[1];
op_yank_reg(oap, false, reg, false);
}
@@ -2605,17 +2611,16 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
static bool recursive = false;
if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
- // No autocommand was defined
- // or we yanked from this autocommand.
+ // No autocommand was defined, or we yanked from this autocommand.
return;
}
recursive = true;
- // set v:event to a dictionary with information about the yank
+ // Set the v:event dictionary with information about the yank.
dict_T *dict = get_vim_var_dict(VV_EVENT);
- // the yanked text
+ // The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
@@ -2623,17 +2628,21 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
- // the register type
+ // Register type.
char buf[NUMBUFLEN+2];
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
tv_dict_add_str(dict, S_LEN("regtype"), buf);
- // name of requested register or the empty string for an unnamed operation.
+ // Name of requested register, or empty string for unnamed operation.
buf[0] = (char)oap->regname;
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("regname"), buf);
- // kind of operation (yank/delete/change)
+ // Motion type: inclusive or exclusive.
+ tv_dict_add_special(dict, S_LEN("inclusive"),
+ oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse);
+
+ // Kind of operation: yank, delete, change).
buf[0] = (char)get_op_char(oap->op_type);
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("operator"), buf);
@@ -2786,8 +2795,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
if (!curbuf->terminal) {
- // Autocommands may be executed when saving lines for undo, which may make
- // y_array invalid. Start undo now to avoid that.
+ // Autocommands may be executed when saving lines for undo. This might
+ // make y_array invalid, so we start undo now to avoid that.
if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
return;
}
@@ -2992,9 +3001,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
/* add a new line */
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
if (ml_append(curbuf->b_ml.ml_line_count, (char_u *)"",
- (colnr_T)1, FALSE) == FAIL)
+ (colnr_T)1, false) == FAIL) {
break;
- ++nr_lines;
+ }
+ nr_lines++;
}
/* get the old line and advance to the position to insert at */
oldp = get_cursor_line_ptr();
@@ -3181,8 +3191,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
newp = (char_u *) xmalloc((size_t)(STRLEN(ptr) + totlen + 1));
STRCPY(newp, y_array[y_size - 1]);
STRCAT(newp, ptr);
- /* insert second line */
- ml_append(lnum, newp, (colnr_T)0, FALSE);
+ // insert second line
+ ml_append(lnum, newp, (colnr_T)0, false);
xfree(newp);
oldp = ml_get(lnum);
@@ -3704,10 +3714,16 @@ int do_join(size_t count,
cend -= spaces[t];
memset(cend, ' ', (size_t)(spaces[t]));
}
+
+ // If deleting more spaces than adding, the cursor moves no more than
+ // what is added if it is inside these spaces.
+ const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
+
mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
- (long)(cend - newp + spaces[t] - (curr - curr_start)));
- if (t == 0)
+ (long)(cend - newp - spaces_removed), spaces_removed);
+ if (t == 0) {
break;
+ }
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
if (remove_comments)
curr += comments[t - 1];
@@ -4135,14 +4151,14 @@ format_lines (
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len);
+ (long)-next_leader_len, 0);
} else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
int indent = (int)getwhitecols_curline();
if (indent > 0) {
(void)del_bytes(indent, FALSE, FALSE);
mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent);
+ (colnr_T)0, 0L, (long)-indent, 0);
}
}
curwin->w_cursor.lnum--;
@@ -5601,6 +5617,9 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (explicit_cb_reg) {
target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
+ clipboard_needs_update = false;
+ }
goto end;
} else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 2346c84b54..743f6c8311 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -98,10 +98,10 @@
#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
-/* WV_ and BV_ values get typecasted to this for the "indir" field */
+// WV_ and BV_ values get typecasted to this for the "indir" field
typedef enum {
PV_NONE = 0,
- PV_MAXVAL = 0xffff /* to avoid warnings for value out of range */
+ PV_MAXVAL = 0xffff // to avoid warnings for value out of range
} idopt_T;
/*
@@ -175,7 +175,7 @@ static int p_udf;
static long p_wm;
static char_u *p_keymap;
-/* Saved values for when 'bin' is set. */
+// Saved values for when 'bin' is set.
static int p_et_nobin;
static int p_ml_nobin;
static long p_tw_nobin;
@@ -202,28 +202,28 @@ typedef struct vimoption {
# define SCRIPTID_INIT , 0
} vimoption_T;
-#define VI_DEFAULT 0 /* def_val[VI_DEFAULT] is Vi default value */
-#define VIM_DEFAULT 1 /* def_val[VIM_DEFAULT] is Vim default value */
+#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value
+#define VIM_DEFAULT 1 // def_val[VIM_DEFAULT] is Vim default value
/*
* Flags
*/
-#define P_BOOL 0x01U /* the option is boolean */
-#define P_NUM 0x02U /* the option is numeric */
-#define P_STRING 0x04U /* the option is a string */
-#define P_ALLOCED 0x08U /* the string option is in allocated memory,
- must use free_string_option() when
- assigning new value. Not set if default is
- the same. */
-#define P_EXPAND 0x10U /* environment expansion. NOTE: P_EXPAND can
- never be used for local or hidden options */
-#define P_NODEFAULT 0x40U /* don't set to default value */
-#define P_DEF_ALLOCED 0x80U /* default value is in allocated memory, must
- use free() when assigning new value */
-#define P_WAS_SET 0x100U /* option has been set/reset */
-#define P_NO_MKRC 0x200U /* don't include in :mkvimrc output */
-#define P_VI_DEF 0x400U /* Use Vi default for Vim */
-#define P_VIM 0x800U /* Vim option */
+#define P_BOOL 0x01U // the option is boolean
+#define P_NUM 0x02U // the option is numeric
+#define P_STRING 0x04U // the option is a string
+#define P_ALLOCED 0x08U // the string option is in allocated memory,
+ // must use free_string_option() when
+ // assigning new value. Not set if default is
+ // the same.
+#define P_EXPAND 0x10U // environment expansion. NOTE: P_EXPAND can
+ // never be used for local or hidden options
+#define P_NODEFAULT 0x40U // don't set to default value
+#define P_DEF_ALLOCED 0x80U // default value is in allocated memory, must
+ // use free() when assigning new value
+#define P_WAS_SET 0x100U // option has been set/reset
+#define P_NO_MKRC 0x200U // don't include in :mkvimrc output
+#define P_VI_DEF 0x400U // Use Vi default for Vim
+#define P_VIM 0x800U // Vim option
// when option changed, what to display:
#define P_RSTAT 0x1000U ///< redraw status lines
@@ -284,7 +284,6 @@ static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", NULL };
static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
-static char *(p_wop_values[]) = { "tagfile", NULL };
static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos",
"mac", NULL };
@@ -306,7 +305,19 @@ static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
"noinsert", "noselect", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_scl_values[]) = { "yes", "no", "auto", NULL };
+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", NULL };
+
+/// All possible flags for 'shm'.
+static char_u SHM_ALL[] = {
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI,
+ SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER,
+ SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU,
+ SHM_RECORDING, SHM_FILEINFO,
+ 0,
+};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -421,13 +432,17 @@ static inline char *add_colon_dirs(char *dest, const char *const val,
return dest;
}
-/// Add directory to a comma-separated list of directories
+/// Adds directory `dest` to a comma-separated list of directories.
///
-/// In the added directory comma is escaped.
+/// Commas in the added directory are escaped.
+///
+/// Windows: Appends "nvim-data" instead of "nvim" if `type` is kXDGDataHome.
+///
+/// @see get_xdg_home
///
/// @param[in,out] dest Destination comma-separated array.
/// @param[in] dir Directory to append.
-/// @param[in] append_nvim If true, append "nvim" as the very first suffix.
+/// @param[in] type Decides whether to append "nvim" (Win: or "nvim-data").
/// @param[in] suf1 If not NULL, suffix appended to destination. Prior to it
/// directory separator is appended. Suffix must not contain
/// commas.
@@ -441,7 +456,7 @@ static inline char *add_colon_dirs(char *dest, const char *const val,
///
/// @return (dest + appended_characters_length)
static inline char *add_dir(char *dest, const char *const dir,
- const size_t dir_len, const bool append_nvim,
+ const size_t dir_len, const XDGVarType type,
const char *const suf1, const size_t len1,
const char *const suf2, const size_t len2)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
@@ -450,12 +465,19 @@ static inline char *add_dir(char *dest, const char *const dir,
return dest;
}
dest = strcpy_comma_escaped(dest, dir, dir_len);
+ bool append_nvim = (type == kXDGDataHome || type == kXDGConfigHome);
if (append_nvim) {
if (!after_pathsep(dest - 1, dest)) {
*dest++ = PATHSEP;
}
+#if defined(WIN32)
+ size_t size = (type == kXDGDataHome ? sizeof("nvim-data") - 1 : NVIM_SIZE);
+ memmove(dest, (type == kXDGDataHome ? "nvim-data" : "nvim"), size);
+ dest += size;
+#else
memmove(dest, "nvim", NVIM_SIZE);
dest += NVIM_SIZE;
+#endif
if (suf1 != NULL) {
*dest++ = PATHSEP;
memmove(dest, suf1, len1);
@@ -471,7 +493,10 @@ static inline char *add_dir(char *dest, const char *const dir,
return dest;
}
-/// Set &runtimepath to default value
+/// Sets &runtimepath to default value.
+///
+/// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing
+/// configuration and data files in the same path. #4403
static void set_runtimepath_default(void)
{
size_t rtp_size = 0;
@@ -488,8 +513,13 @@ static void set_runtimepath_default(void)
if (data_home != NULL) {
data_len = strlen(data_home);
if (data_len != 0) {
+#if defined(WIN32)
+ size_t nvim_size = (sizeof("nvim-data") - 1);
+#else
+ size_t nvim_size = NVIM_SIZE;
+#endif
rtp_size += ((data_len + memcnt(data_home, ',', data_len)
- + NVIM_SIZE + 1 + SITE_SIZE + 1
+ + nvim_size + 1 + SITE_SIZE + 1
+ !after_pathsep(data_home, data_home + data_len)) * 2
+ AFTER_SIZE + 1);
}
@@ -518,21 +548,22 @@ static void set_runtimepath_default(void)
}
char *const rtp = xmalloc(rtp_size);
char *rtp_cur = rtp;
- rtp_cur = add_dir(rtp_cur, config_home, config_len, true, NULL, 0, NULL, 0);
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, kXDGConfigHome,
+ NULL, 0, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, config_dirs, NULL, 0, NULL, 0, true);
- rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
- NULL, 0);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, kXDGDataHome,
+ "site", SITE_SIZE, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE, NULL, 0,
true);
- rtp_cur = add_dir(rtp_cur, vimruntime, vimruntime_len, false, NULL, 0,
- NULL, 0);
+ rtp_cur = add_dir(rtp_cur, vimruntime, vimruntime_len, kXDGNone,
+ NULL, 0, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE,
"after", AFTER_SIZE, false);
- rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
- "after", AFTER_SIZE);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, kXDGDataHome,
+ "site", SITE_SIZE, "after", AFTER_SIZE);
rtp_cur = add_colon_dirs(rtp_cur, config_dirs, "after", AFTER_SIZE, NULL, 0,
false);
- rtp_cur = add_dir(rtp_cur, config_home, config_len, true,
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, kXDGConfigHome,
"after", AFTER_SIZE, NULL, 0);
// Strip trailing comma.
rtp_cur[-1] = NUL;
@@ -562,8 +593,8 @@ void set_init_1(void)
langmap_init();
- /* Be nocompatible */
- p_cp = FALSE;
+ // Be nocompatible
+ p_cp = false;
/*
* Find default value for 'shell' option.
@@ -590,23 +621,25 @@ void set_init_1(void)
garray_T ga;
ga_init(&ga, 1, 100);
- for (size_t n = 0; n < ARRAY_SIZE(names); ++n) {
+ for (size_t n = 0; n < ARRAY_SIZE(names); n++) {
bool mustfree = true;
char *p;
# ifdef UNIX
if (*names[n] == NUL) {
p = "/tmp";
mustfree = false;
- }
- else
+ } else
# endif
- p = vim_getenv(names[n]);
+ {
+ p = vim_getenv(names[n]);
+ }
if (p != NULL && *p != NUL) {
// First time count the NUL, otherwise count the ','.
len = (int)strlen(p) + 3;
ga_grow(&ga, len);
- if (!GA_EMPTY(&ga))
+ if (!GA_EMPTY(&ga)) {
STRCAT(ga.ga_data, ",");
+ }
STRCAT(ga.ga_data, p);
add_pathsep(ga.ga_data);
STRCAT(ga.ga_data, "*");
@@ -627,19 +660,20 @@ void set_init_1(void)
int i;
int j;
- /* Initialize the 'cdpath' option's default value. */
+ // Initialize the 'cdpath' option's default value.
cdpath = (char_u *)vim_getenv("CDPATH");
if (cdpath != NULL) {
buf = xmalloc(2 * STRLEN(cdpath) + 2);
{
- buf[0] = ','; /* start with ",", current dir first */
+ buf[0] = ','; // start with ",", current dir first
j = 1;
- for (i = 0; cdpath[i] != NUL; ++i) {
- if (vim_ispathlistsep(cdpath[i]))
+ for (i = 0; cdpath[i] != NUL; i++) {
+ if (vim_ispathlistsep(cdpath[i])) {
buf[j++] = ',';
- else {
- if (cdpath[i] == ' ' || cdpath[i] == ',')
+ } else {
+ if (cdpath[i] == ' ' || cdpath[i] == ',') {
buf[j++] = '\\';
+ }
buf[j++] = cdpath[i];
}
}
@@ -648,15 +682,16 @@ void set_init_1(void)
if (opt_idx >= 0) {
options[opt_idx].def_val[VI_DEFAULT] = buf;
options[opt_idx].flags |= P_DEF_ALLOCED;
- } else
- xfree(buf); /* cannot happen */
+ } else {
+ xfree(buf); // cannot happen
+ }
}
xfree(cdpath);
}
}
#if defined(MSWIN) || defined(MAC)
- /* Set print encoding on platforms that don't default to latin1 */
+ // Set print encoding on platforms that don't default to latin1
set_string_default("printencoding", "hp-roman8", false);
#endif
@@ -701,19 +736,19 @@ void set_init_1(void)
curbuf->b_p_initialized = true;
- curbuf->b_p_ar = -1; /* no local 'autoread' value */
+ curbuf->b_p_ar = -1; // no local 'autoread' value
curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL;
check_buf_options(curbuf);
check_win_options(curwin);
check_options();
- /* Set all options to their Vim default */
+ // Set all options to their Vim default
set_options_default(OPT_FREE);
// set 'laststatus'
last_status(false);
- /* Must be before option_expand(), because that one needs vim_isIDc() */
+ // Must be before option_expand(), because that one needs vim_isIDc()
didset_options();
// Use the current chartab for the generic chartab. This is not in
@@ -748,14 +783,15 @@ void set_init_1(void)
* and Vim. When this changes, add some code here! Also need to
* split P_DEF_ALLOCED in two.
*/
- if (options[opt_idx].flags & P_DEF_ALLOCED)
+ if (options[opt_idx].flags & P_DEF_ALLOCED) {
xfree(options[opt_idx].def_val[VI_DEFAULT]);
- options[opt_idx].def_val[VI_DEFAULT] = (char_u *) p;
+ }
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *)p;
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
- save_file_ff(curbuf); /* Buffer is unchanged */
+ save_file_ff(curbuf); // Buffer is unchanged
/* Detect use of mlterm.
* Mlterm is a terminal emulator akin to xterm that has some special
@@ -787,7 +823,7 @@ void set_init_1(void)
(void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
#endif
- /* Set the default for 'helplang'. */
+ // Set the default for 'helplang'.
set_helplang_default(get_mess_lang());
}
@@ -795,20 +831,20 @@ void set_init_1(void)
* Set an option to its default value.
* This does not take care of side effects!
*/
-static void
-set_option_default (
+static void
+set_option_default(
int opt_idx,
- int opt_flags, /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
- int compatible /* use Vi default value */
+ int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
+ int compatible // use Vi default value
)
{
- char_u *varp; /* pointer to variable for current option */
- int dvi; /* index in def_val[] */
+ char_u *varp; // pointer to variable for current option
+ int dvi; // index in def_val[]
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
uint32_t flags = options[opt_idx].flags;
- if (varp != NULL) { /* skip hidden option, nothing to do for it */
+ if (varp != NULL) { // skip hidden option, nothing to do for it
dvi = ((flags & P_VI_DEF) || compatible) ? VI_DEFAULT : VIM_DEFAULT;
if (flags & P_STRING) {
/* Use set_string_option_direct() for local options to handle
@@ -834,20 +870,22 @@ set_option_default (
*(long *)varp;
}
}
- } else { /* P_BOOL */
+ } else { // P_BOOL
*(int *)varp = (int)(intptr_t)options[opt_idx].def_val[dvi];
#ifdef UNIX
- /* 'modeline' defaults to off for root */
- if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID)
- *(int *)varp = FALSE;
+ // 'modeline' defaults to off for root
+ if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) {
+ *(int *)varp = false;
+ }
#endif
- /* May also set global value for local option. */
- if (both)
+ // May also set global value for local option.
+ if (both) {
*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
*(int *)varp;
+ }
}
- /* The default value is not insecure. */
+ // The default value is not insecure.
uint32_t *flagsp = insecure_flag(opt_idx, opt_flags);
*flagsp = *flagsp & ~P_INSECURE;
}
@@ -858,9 +896,9 @@ set_option_default (
/*
* Set all options (except terminal options) to their default value.
*/
-static void
-set_options_default (
- int opt_flags /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
+static void
+set_options_default(
+ int opt_flags // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
)
{
for (int i = 0; options[i].fullname; i++) {
@@ -869,7 +907,7 @@ set_options_default (
}
}
- /* The 'scroll' option must be computed for all windows. */
+ // The 'scroll' option must be computed for all windows.
FOR_ALL_TAB_WINDOWS(tp, wp) {
win_comp_scroll(wp);
}
@@ -1056,8 +1094,9 @@ void set_helplang_default(const char *lang)
}
int idx = findoption("hlg");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
- if (options[idx].flags & P_ALLOCED)
+ if (options[idx].flags & P_ALLOCED) {
free_string_option(p_hlg);
+ }
p_hlg = (char_u *)xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) {
@@ -1102,24 +1141,21 @@ void set_title_defaults(void)
}
}
-/*
- * Parse 'arg' for option settings.
- *
- * 'arg' may be IObuff, but only when no errors can be present and option
- * does not need to be expanded with option_expand().
- * "opt_flags":
- * 0 for ":set"
- * OPT_GLOBAL for ":setglobal"
- * OPT_LOCAL for ":setlocal" and a modeline
- * OPT_MODELINE for a modeline
- * OPT_WINONLY to only set window-local options
- * OPT_NOWIN to skip setting window-local options
- *
- * returns FAIL if an error is detected, OK otherwise
- */
-int
-do_set (
- char_u *arg, /* option string (may be written to!) */
+// Parse 'arg' for option settings.
+//
+// 'arg' may be IObuff, but only when no errors can be present and option
+// does not need to be expanded with option_expand().
+// "opt_flags":
+// 0 for ":set"
+// OPT_GLOBAL for ":setglobal"
+// OPT_LOCAL for ":setlocal" and a modeline
+// OPT_MODELINE for a modeline
+// OPT_WINONLY to only set window-local options
+// OPT_NOWIN to skip setting window-local options
+//
+// returns FAIL if an error is detected, OK otherwise
+int do_set(
+ char_u *arg, // option string (may be written to!)
int opt_flags
)
{
@@ -1127,30 +1163,30 @@ do_set (
char_u *errmsg;
char_u errbuf[80];
char_u *startarg;
- int prefix; /* 1: nothing, 0: "no", 2: "inv" in front of name */
- char_u nextchar; /* next non-white char after option name */
- int afterchar; /* character just after option name */
+ int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name
+ char_u nextchar; // next non-white char after option name
+ int afterchar; // character just after option name
int len;
int i;
varnumber_T value;
int key;
- uint32_t flags; /* flags for current option */
- char_u *varp = NULL; /* pointer to variable for current option */
- int did_show = FALSE; /* already showed one value */
- int adding; /* "opt+=arg" */
- int prepending; /* "opt^=arg" */
- int removing; /* "opt-=arg" */
+ uint32_t flags; // flags for current option
+ char_u *varp = NULL; // pointer to variable for current option
+ int did_show = false; // already showed one value
+ int adding; // "opt+=arg"
+ int prepending; // "opt^=arg"
+ int removing; // "opt-=arg"
int cp_val = 0;
if (*arg == NUL) {
showoptions(0, opt_flags);
- did_show = TRUE;
+ did_show = true;
goto theend;
}
- while (*arg != NUL) { /* loop to process all options */
+ while (*arg != NUL) { // loop to process all options
errmsg = NULL;
- startarg = arg; /* remember for error message */
+ startarg = arg; // remember for error message
if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
@@ -1169,7 +1205,7 @@ do_set (
redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
- did_show = TRUE;
+ did_show = true;
}
} else {
prefix = 1;
@@ -1181,17 +1217,18 @@ do_set (
arg += 3;
}
- /* find end of name */
+ // find end of name
key = 0;
if (*arg == '<') {
opt_idx = -1;
- /* look out for <t_>;> */
- if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4])
+ // look out for <t_>;>
+ if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) {
len = 5;
- else {
+ } else {
len = 1;
- while (arg[len] != NUL && arg[len] != '>')
- ++len;
+ while (arg[len] != NUL && arg[len] != '>') {
+ len++;
+ }
}
if (arg[len] != '>') {
errmsg = e_invarg;
@@ -1220,43 +1257,45 @@ do_set (
}
}
- /* remember character after option name */
+ // remember character after option name
afterchar = arg[len];
- /* skip white space, allow ":set ai ?" */
- while (ascii_iswhite(arg[len]))
- ++len;
+ // skip white space, allow ":set ai ?"
+ while (ascii_iswhite(arg[len])) {
+ len++;
+ }
- adding = FALSE;
- prepending = FALSE;
- removing = FALSE;
+ adding = false;
+ prepending = false;
+ removing = false;
if (arg[len] != NUL && arg[len + 1] == '=') {
if (arg[len] == '+') {
- adding = TRUE; /* "+=" */
- ++len;
+ adding = true; // "+="
+ len++;
} else if (arg[len] == '^') {
- prepending = TRUE; /* "^=" */
- ++len;
+ prepending = true; // "^="
+ len++;
} else if (arg[len] == '-') {
- removing = TRUE; /* "-=" */
- ++len;
+ removing = true; // "-="
+ len++;
}
}
nextchar = arg[len];
- if (opt_idx == -1 && key == 0) { /* found a mismatch: skip */
+ if (opt_idx == -1 && key == 0) { // found a mismatch: skip
errmsg = (char_u *)N_("E518: Unknown option");
goto skip;
}
if (opt_idx >= 0) {
- if (options[opt_idx].var == NULL) { /* hidden option: skip */
- /* Only give an error message when requesting the value of
- * a hidden option, ignore setting it. */
+ if (options[opt_idx].var == NULL) { // hidden option: skip
+ // Only give an error message when requesting the value of
+ // a hidden option, ignore setting it.
if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
- || nextchar == '?'))
+ || nextchar == '?')) {
errmsg = (char_u *)_(e_unsupportedoption);
+ }
goto skip;
}
@@ -1272,28 +1311,30 @@ do_set (
&& (opt_idx < 0 || options[opt_idx].var != VAR_WIN))
goto skip;
- /* Skip all options that are window-local (used for :vimgrep). */
+ // Skip all options that are window-local (used for :vimgrep).
if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
- && options[opt_idx].var == VAR_WIN)
+ && options[opt_idx].var == VAR_WIN) {
goto skip;
+ }
- /* Disallow changing some options from modelines. */
+ // Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
if (flags & (P_SECURE | P_NO_ML)) {
errmsg = (char_u *)_("E520: Not allowed in a modeline");
goto skip;
}
- /* In diff mode some options are overruled. This avoids that
- * 'foldmethod' becomes "marker" instead of "diff" and that
- * "wrap" gets set. */
+ // In diff mode some options are overruled. This avoids that
+ // 'foldmethod' becomes "marker" instead of "diff" and that
+ // "wrap" gets set.
if (curwin->w_p_diff
- && opt_idx >= 0 /* shut up coverity warning */
+ && opt_idx >= 0 // shut up coverity warning
&& (options[opt_idx].indir == PV_FDM
- || options[opt_idx].indir == PV_WRAP))
+ || options[opt_idx].indir == PV_WRAP)) {
goto skip;
+ }
}
- /* Disallow changing some options in the sandbox */
+ // Disallow changing some options in the sandbox
if (sandbox != 0 && (flags & P_SECURE)) {
errmsg = (char_u *)_(e_sandbox);
goto skip;
@@ -1303,11 +1344,11 @@ do_set (
arg += len;
cp_val = p_cp;
if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') {
- if (arg[3] == 'm') { /* "opt&vim": set to Vim default */
- cp_val = FALSE;
+ if (arg[3] == 'm') { // "opt&vim": set to Vim default
+ cp_val = false;
arg += 3;
- } else { /* "opt&vi": set to Vi default */
- cp_val = TRUE;
+ } else { // "opt&vi": set to Vi default
+ cp_val = true;
arg += 2;
}
}
@@ -1329,11 +1370,11 @@ do_set (
/*
* print value
*/
- if (did_show)
- msg_putchar('\n'); /* cursor below last one */
- else {
- gotocmdline(TRUE); /* cursor at status line */
- did_show = TRUE; /* remember that we did a line */
+ if (did_show) {
+ msg_putchar('\n'); // cursor below last one
+ } else {
+ gotocmdline(true); // cursor at status line
+ did_show = true; // remember that we did a line
}
if (opt_idx >= 0) {
showoneopt(&options[opt_idx], opt_flags);
@@ -1357,7 +1398,10 @@ do_set (
&& nextchar != NUL && !ascii_iswhite(afterchar))
errmsg = e_trailing;
} else {
- if (flags & P_BOOL) { /* boolean */
+ int value_is_replaced = !prepending && !adding && !removing;
+ int value_checked = false;
+
+ if (flags & P_BOOL) { // boolean
if (nextchar == '=' || nextchar == ':') {
errmsg = e_invarg;
goto skip;
@@ -1368,20 +1412,21 @@ do_set (
* ":set opt&": reset to default value
* ":set opt<": reset to global value
*/
- if (nextchar == '!')
+ if (nextchar == '!') {
value = *(int *)(varp) ^ 1;
- else if (nextchar == '&')
+ } else if (nextchar == '&') {
value = (int)(intptr_t)options[opt_idx].def_val[
- ((flags & P_VI_DEF) || cp_val)
- ? VI_DEFAULT : VIM_DEFAULT];
- else if (nextchar == '<') {
- /* For 'autoread' -1 means to use global value. */
+ ((flags & P_VI_DEF) || cp_val)
+ ? VI_DEFAULT : VIM_DEFAULT];
+ } else if (nextchar == '<') {
+ // For 'autoread' -1 means to use global value.
if ((int *)varp == &curbuf->b_p_ar
- && opt_flags == OPT_LOCAL)
+ && opt_flags == OPT_LOCAL) {
value = -1;
- else
+ } else {
value = *(int *)get_varp_scope(&(options[opt_idx]),
- OPT_GLOBAL);
+ OPT_GLOBAL);
+ }
} else {
/*
* ":set invopt": invert
@@ -1391,10 +1436,11 @@ do_set (
errmsg = e_trailing;
goto skip;
}
- if (prefix == 2) /* inv */
+ if (prefix == 2) { // inv
value = *(int *)(varp) ^ 1;
- else
+ } else {
value = prefix;
+ }
}
errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value,
@@ -1406,16 +1452,14 @@ do_set (
goto skip;
}
- if (flags & P_NUM) { /* numeric */
- /*
- * Different ways to set a number option:
- * & set to default value
- * < set to global value
- * <xx> accept special key codes for 'wildchar'
- * c accept any non-digit for 'wildchar'
- * [-]0-9 set number
- * other error
- */
+ if (flags & P_NUM) { // numeric
+ // Different ways to set a number option:
+ // & set to default value
+ // < set to global value
+ // <xx> accept special key codes for 'wildchar'
+ // c accept any non-digit for 'wildchar'
+ // [-]0-9 set number
+ // other error
arg++;
if (nextchar == '&') {
value = (long)(intptr_t)options[opt_idx].def_val[
@@ -1520,10 +1564,10 @@ do_set (
}
} else if (nextchar == '<') { // set to global val
newval = vim_strsave(*(char_u **)get_varp_scope(
- &(options[opt_idx]), OPT_GLOBAL));
- new_value_alloced = TRUE;
+ &(options[opt_idx]), OPT_GLOBAL));
+ new_value_alloced = true;
} else {
- ++arg; /* jump to after the '=' or ':' */
+ arg++; // jump to after the '=' or ':'
/*
* Set 'keywordprg' to ":help" if an empty
@@ -1571,18 +1615,24 @@ do_set (
&& ascii_isdigit(*arg)) {
*errbuf = NUL;
i = getdigits_int(&arg);
- if (i & 1)
+ if (i & 1) {
STRCAT(errbuf, "b,");
- if (i & 2)
+ }
+ if (i & 2) {
STRCAT(errbuf, "s,");
- if (i & 4)
+ }
+ if (i & 4) {
STRCAT(errbuf, "h,l,");
- if (i & 8)
+ }
+ if (i & 8) {
STRCAT(errbuf, "<,>,");
- if (i & 16)
+ }
+ if (i & 16) {
STRCAT(errbuf, "[,],");
- if (*errbuf != NUL) /* remove trailing , */
+ }
+ if (*errbuf != NUL) { // remove trailing ,
errbuf[STRLEN(errbuf) - 1] = NUL;
+ }
save_arg = arg;
arg = errbuf;
}
@@ -1593,7 +1643,7 @@ do_set (
else if ( *arg == '>'
&& (varp == (char_u *)&p_dir
|| varp == (char_u *)&p_bdir)) {
- ++arg;
+ arg++;
}
/*
@@ -1601,10 +1651,11 @@ do_set (
* Can't use set_string_option_direct(), because
* we need to remove the backslashes.
*/
- /* get a bit too much */
+ // get a bit too much
newlen = (unsigned)STRLEN(arg) + 1;
- if (adding || prepending || removing)
+ if (adding || prepending || removing) {
newlen += (unsigned)STRLEN(origval) + 1;
+ }
newval = xmalloc(newlen);
s = newval;
@@ -1626,10 +1677,10 @@ do_set (
&& arg[2] != '\\')))
#endif
)
- ++arg; /* remove backslash */
+ arg++; // remove backslash
if (has_mbyte
&& (i = (*mb_ptr2len)(arg)) > 1) {
- /* copy multibyte char */
+ // copy multibyte char
memmove(s, arg, (size_t)i);
arg += i;
s += i;
@@ -1649,8 +1700,9 @@ do_set (
if (s != NULL) {
xfree(newval);
newlen = (unsigned)STRLEN(s) + 1;
- if (adding || prepending || removing)
+ if (adding || prepending || removing) {
newlen += (unsigned)STRLEN(origval) + 1;
+ }
newval = xmalloc(newlen);
STRCPY(newval, s);
}
@@ -1658,11 +1710,11 @@ do_set (
/* locate newval[] in origval[] when removing it
* and when adding to avoid duplicates */
- i = 0; /* init for GCC */
+ i = 0; // init for GCC
if (removing || (flags & P_NODUP)) {
i = (int)STRLEN(newval);
bs = 0;
- for (s = origval; *s; ++s) {
+ for (s = origval; *s; s++) {
if ((!(flags & P_COMMA)
|| s == origval
|| (s[-1] == ',' && !(bs & 1)))
@@ -1685,8 +1737,8 @@ do_set (
// do not add if already there
if ((adding || prepending) && *s) {
- prepending = FALSE;
- adding = FALSE;
+ prepending = false;
+ adding = false;
STRCPY(newval, origval);
}
}
@@ -1712,8 +1764,9 @@ do_set (
i = (int)STRLEN(newval);
STRMOVE(newval + i + comma, origval);
}
- if (comma)
+ if (comma) {
newval[i] = ',';
+ }
}
/* Remove newval[] from origval[]. (Note: "i" has
@@ -1721,16 +1774,17 @@ do_set (
if (removing) {
STRCPY(newval, origval);
if (*s) {
- /* may need to remove a comma */
+ // may need to remove a comma
if (flags & P_COMMA) {
if (s == origval) {
- /* include comma after string */
- if (s[i] == ',')
- ++i;
+ // include comma after string
+ if (s[i] == ',') {
+ i++;
+ }
} else {
- /* include comma before string */
- --s;
- ++i;
+ // include comma before string
+ s--;
+ i++;
}
}
STRMOVE(newval + (s - origval), s + i);
@@ -1760,9 +1814,10 @@ do_set (
}
}
- if (save_arg != NULL) /* number for 'whichwrap' */
+ if (save_arg != NULL) { // number for 'whichwrap'
arg = save_arg;
- new_value_alloced = TRUE;
+ }
+ new_value_alloced = true;
}
// Set the new value.
@@ -1776,12 +1831,32 @@ do_set (
// buffer is closed by autocommands.
saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0;
- // Handle side effects, and set the global value for
- // ":set" on local options. Note: when setting 'syntax'
- // or 'filetype' autocommands may be triggered that can
- // cause havoc.
- errmsg = did_set_string_option(opt_idx, (char_u **)varp,
- new_value_alloced, oldval, errbuf, opt_flags);
+ {
+ uint32_t *p = insecure_flag(opt_idx, opt_flags);
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a
+ // modeline or in secure mode, then deal with side
+ // effects in secure mode. Also when the value was
+ // set with the P_INSECURE flag and is not
+ // completely replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (!value_is_replaced && (*p & P_INSECURE))) {
+ secure = 1;
+ }
+
+ // Handle side effects, and set the global value
+ // for ":set" on local options. Note: when setting
+ // 'syntax' or 'filetype' autocommands may be
+ // triggered that can cause havoc.
+ errmsg = did_set_string_option(opt_idx, (char_u **)varp,
+ new_value_alloced, oldval,
+ errbuf, sizeof(errbuf),
+ opt_flags, &value_checked);
+
+ secure = secure_saved;
+ }
if (errmsg == NULL) {
if (!starting) {
@@ -1807,9 +1882,9 @@ do_set (
}
}
- if (opt_idx >= 0)
- did_set_option(opt_idx, opt_flags,
- !prepending && !adding && !removing);
+ if (opt_idx >= 0) {
+ did_set_option(opt_idx, opt_flags, value_is_replaced, value_checked);
+ }
}
skip:
@@ -1819,13 +1894,16 @@ skip:
* - skip blanks
* - skip one "=val" argument (for hidden options ":set gfn =xx")
*/
- for (i = 0; i < 2; ++i) {
- while (*arg != NUL && !ascii_iswhite(*arg))
- if (*arg++ == '\\' && *arg != NUL)
- ++arg;
+ for (i = 0; i < 2; i++) {
+ while (*arg != NUL && !ascii_iswhite(*arg)) {
+ if (*arg++ == '\\' && *arg != NUL) {
+ arg++;
+ }
+ }
arg = skipwhite(arg);
- if (*arg != '=')
+ if (*arg != '=') {
break;
+ }
}
}
@@ -1833,18 +1911,18 @@ skip:
STRLCPY(IObuff, _(errmsg), IOSIZE);
i = (int)STRLEN(IObuff) + 2;
if (i + (arg - startarg) < IOSIZE) {
- /* append the argument with the error */
+ // append the argument with the error
STRCAT(IObuff, ": ");
assert(arg >= startarg);
memmove(IObuff + i, startarg, (size_t)(arg - startarg));
IObuff[i + (arg - startarg)] = NUL;
}
- /* make sure all characters are printable */
+ // make sure all characters are printable
trans_characters(IObuff, IOSIZE);
- ++no_wait_return; /* wait_return done later */
- emsg(IObuff); /* show error highlighted */
- --no_wait_return;
+ no_wait_return++; // wait_return done later
+ emsg(IObuff); // show error highlighted
+ no_wait_return--;
return FAIL;
}
@@ -1854,27 +1932,26 @@ skip:
theend:
if (silent_mode && did_show) {
- /* After displaying option values in silent mode. */
- silent_mode = FALSE;
- info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
+ // After displaying option values in silent mode.
+ silent_mode = false;
+ info_message = true; // use mch_msg(), not mch_errmsg()
msg_putchar('\n');
ui_flush();
- silent_mode = TRUE;
- info_message = FALSE; /* use mch_msg(), not mch_errmsg() */
+ silent_mode = true;
+ info_message = false; // use mch_msg(), not mch_errmsg()
}
return OK;
}
-/*
- * Call this when an option has been given a new value through a user command.
- * Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
- */
-static void
-did_set_option (
+// Call this when an option has been given a new value through a user command.
+// Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
+static void did_set_option(
int opt_idx,
- int opt_flags, /* possibly with OPT_MODELINE */
- int new_value /* value was replaced completely */
+ int opt_flags, // possibly with OPT_MODELINE
+ int new_value, // value was replaced completely
+ int value_checked // value was checked to be safe, no need to
+ // set P_INSECURE
)
{
options[opt_idx].flags |= P_WAS_SET;
@@ -1883,20 +1960,22 @@ did_set_option (
* set the P_INSECURE flag. Otherwise, if a new value is stored reset the
* flag. */
uint32_t *p = insecure_flag(opt_idx, opt_flags);
- if (secure
- || sandbox != 0
- || (opt_flags & OPT_MODELINE))
+ if (!value_checked && (secure
+ || sandbox != 0
+ || (opt_flags & OPT_MODELINE))) {
*p = *p | P_INSECURE;
- else if (new_value)
+ } else if (new_value) {
*p = *p & ~P_INSECURE;
+ }
}
-static char_u *illegal_char(char_u *errbuf, int c)
+static char_u *illegal_char(char_u *errbuf, size_t errbuflen, int c)
{
- if (errbuf == NULL)
+ if (errbuf == NULL) {
return (char_u *)"";
- sprintf((char *)errbuf, _("E539: Illegal character <%s>"),
- (char *)transchar(c));
+ }
+ vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"),
+ (char *)transchar(c));
return errbuf;
}
@@ -1906,10 +1985,12 @@ static char_u *illegal_char(char_u *errbuf, int c)
*/
static int string_to_key(char_u *arg)
{
- if (*arg == '<')
+ if (*arg == '<') {
return find_key_option(arg + 1);
- if (*arg == '^')
+ }
+ if (*arg == '^') {
return Ctrl_chr(arg[1]);
+ }
return *arg;
}
@@ -1921,26 +2002,24 @@ static char_u *check_cedit(void)
{
int n;
- if (*p_cedit == NUL)
+ if (*p_cedit == NUL) {
cedit_key = -1;
- else {
+ } else {
n = string_to_key(p_cedit);
- if (vim_isprintc(n))
+ if (vim_isprintc(n)) {
return e_invarg;
+ }
cedit_key = n;
}
return NULL;
}
-/*
- * When changing 'title', 'titlestring', 'icon' or 'iconstring', call
- * maketitle() to create and display it.
- * When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
- * the old value back.
- */
-static void
-did_set_title (
- int icon /* Did set icon instead of title */
+// When changing 'title', 'titlestring', 'icon' or 'iconstring', call
+// maketitle() to create and display it.
+// When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
+// the old value back.
+static void did_set_title(
+ int icon // Did set icon instead of title
)
{
if (starting != NO_SCREEN) {
@@ -1949,14 +2028,11 @@ did_set_title (
}
}
-/*
- * set_options_bin - called when 'bin' changes value.
- */
-void
-set_options_bin (
+// set_options_bin - called when 'bin' changes value.
+void set_options_bin(
int oldval,
int newval,
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL and/or OPT_GLOBAL
)
{
/*
@@ -1964,7 +2040,7 @@ set_options_bin (
* copied when 'bin is set and restored when 'bin' is reset.
*/
if (newval) {
- if (!oldval) { /* switched on */
+ if (!oldval) { // switched on
if (!(opt_flags & OPT_GLOBAL)) {
curbuf->b_p_tw_nobin = curbuf->b_p_tw;
curbuf->b_p_wm_nobin = curbuf->b_p_wm;
@@ -1980,19 +2056,19 @@ set_options_bin (
}
if (!(opt_flags & OPT_GLOBAL)) {
- curbuf->b_p_tw = 0; /* no automatic line wrap */
- curbuf->b_p_wm = 0; /* no automatic line wrap */
- curbuf->b_p_ml = 0; /* no modelines */
- curbuf->b_p_et = 0; /* no expandtab */
+ curbuf->b_p_tw = 0; // no automatic line wrap
+ curbuf->b_p_wm = 0; // no automatic line wrap
+ curbuf->b_p_ml = 0; // no modelines
+ curbuf->b_p_et = 0; // no expandtab
}
if (!(opt_flags & OPT_LOCAL)) {
p_tw = 0;
p_wm = 0;
- p_ml = FALSE;
- p_et = FALSE;
- p_bin = TRUE; /* needed when called for the "-b" argument */
+ p_ml = false;
+ p_et = false;
+ p_bin = true; // needed when called for the "-b" argument
}
- } else if (oldval) { /* switched off */
+ } else if (oldval) { // switched off
if (!(opt_flags & OPT_GLOBAL)) {
curbuf->b_p_tw = curbuf->b_p_tw_nobin;
curbuf->b_p_wm = curbuf->b_p_wm_nobin;
@@ -2020,8 +2096,9 @@ int get_shada_parameter(int type)
char_u *p;
p = find_shada_parameter(type);
- if (p != NULL && ascii_isdigit(*p))
+ if (p != NULL && ascii_isdigit(*p)) {
return atoi((char *)p);
+ }
return -1;
}
@@ -2034,14 +2111,17 @@ char_u *find_shada_parameter(int type)
{
char_u *p;
- for (p = p_shada; *p; ++p) {
- if (*p == type)
+ for (p = p_shada; *p; p++) {
+ if (*p == type) {
return p + 1;
- if (*p == 'n') /* 'n' is always the last one */
+ }
+ if (*p == 'n') { // 'n' is always the last one
break;
- p = vim_strchr(p, ','); /* skip until next ',' */
- if (p == NULL) /* hit the end without finding parameter */
+ }
+ p = vim_strchr(p, ','); // skip until next ','
+ if (p == NULL) { // hit the end without finding parameter
break;
+ }
}
return NULL;
}
@@ -2054,9 +2134,10 @@ char_u *find_shada_parameter(int type)
*/
static char_u *option_expand(int opt_idx, char_u *val)
{
- /* if option doesn't need expansion nothing to do */
- if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL)
+ // if option doesn't need expansion nothing to do
+ if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) {
return NULL;
+ }
if (val == NULL) {
val = *(char_u **)options[opt_idx].var;
@@ -2075,11 +2156,12 @@ static char_u *option_expand(int opt_idx, char_u *val)
* For 'spellsuggest' expand after "file:".
*/
expand_env_esc(val, NameBuff, MAXPATHL,
- (char_u **)options[opt_idx].var == &p_tags, FALSE,
- (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
- NULL);
- if (STRCMP(NameBuff, val) == 0) /* they are the same */
+ (char_u **)options[opt_idx].var == &p_tags, false,
+ (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
+ NULL);
+ if (STRCMP(NameBuff, val) == 0) { // they are the same
return NULL;
+ }
return NameBuff;
}
@@ -2090,7 +2172,7 @@ static char_u *option_expand(int opt_idx, char_u *val)
*/
static void didset_options(void)
{
- /* initialize the table for 'iskeyword' et.al. */
+ // initialize the table for 'iskeyword' et.al.
(void)init_chartab();
(void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
@@ -2139,9 +2221,11 @@ void check_options(void)
{
int opt_idx;
- for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++)
- if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL)
+ for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
+ if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) {
check_string_option((char_u **)get_varp(&(options[opt_idx])));
+ }
+ }
}
/*
@@ -2207,28 +2291,29 @@ void check_buf_options(buf_T *buf)
*/
void free_string_option(char_u *p)
{
- if (p != empty_option)
+ if (p != empty_option) {
xfree(p);
+ }
}
void clear_string_option(char_u **pp)
{
- if (*pp != empty_option)
+ if (*pp != empty_option) {
xfree(*pp);
+ }
*pp = empty_option;
}
static void check_string_option(char_u **pp)
{
- if (*pp == NULL)
+ if (*pp == NULL) {
*pp = empty_option;
+ }
}
-/*
- * Return TRUE when option "opt" was set from a modeline or in secure mode.
- * Return FALSE when it wasn't.
- * Return -1 for an unknown option.
- */
+/// Return true when option "opt" was set from a modeline or in secure mode.
+/// Return false when it wasn't.
+/// Return -1 for an unknown option.
int was_set_insecurely(char_u *opt, int opt_flags)
{
int idx = findoption((const char *)opt);
@@ -2257,7 +2342,7 @@ static uint32_t *insecure_flag(int opt_idx, int opt_flags)
case PV_INEX: return &curbuf->b_p_inex_flags;
}
- /* Nothing special, return global flags field. */
+ // Nothing special, return global flags field.
return &options[opt_idx].flags;
}
@@ -2265,9 +2350,10 @@ static uint32_t *insecure_flag(int opt_idx, int opt_flags)
/*
* Redraw the window title and/or tab page text later.
*/
-static void redraw_titles(void) {
- need_maketitle = TRUE;
- redraw_tabline = TRUE;
+static void redraw_titles(void)
+{
+ need_maketitle = true;
+ redraw_tabline = true;
}
static int shada_idx = -1;
@@ -2279,12 +2365,12 @@ static int shada_idx = -1;
* When "set_sid" is zero set the scriptID to current_SID. When "set_sid" is
* SID_NONE don't set the scriptID. Otherwise set the scriptID to "set_sid".
*/
-void
-set_string_option_direct (
+void
+set_string_option_direct(
char_u *name,
int opt_idx,
char_u *val,
- int opt_flags, /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
int set_sid
)
{
@@ -2302,22 +2388,25 @@ set_string_option_direct (
}
}
- if (options[idx].var == NULL) /* can't set hidden option */
+ if (options[idx].var == NULL) { // can't set hidden option
return;
+ }
assert((void *) options[idx].var != (void *) &p_shada);
s = vim_strsave(val);
{
varp = (char_u **)get_varp_scope(&(options[idx]),
- both ? OPT_LOCAL : opt_flags);
- if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED))
+ both ? OPT_LOCAL : opt_flags);
+ if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED)) {
free_string_option(*varp);
+ }
*varp = s;
- /* For buffer/window local option may also set the global value. */
- if (both)
+ // For buffer/window local option may also set the global value.
+ if (both) {
set_string_option_global(idx, varp);
+ }
options[idx].flags |= P_ALLOCED;
@@ -2336,19 +2425,20 @@ set_string_option_direct (
/*
* Set global value for string option when it's a local option.
*/
-static void
-set_string_option_global (
- int opt_idx, /* option index */
- char_u **varp /* pointer to option variable */
+static void
+set_string_option_global(
+ int opt_idx, // option index
+ char_u **varp // pointer to option variable
)
{
char_u **p, *s;
- /* the global value is always allocated */
- if (options[opt_idx].var == VAR_WIN)
+ // the global value is always allocated
+ if (options[opt_idx].var == VAR_WIN) {
p = (char_u **)GLOBAL_WO(varp);
- else
+ } else {
p = (char_u **)options[opt_idx].var;
+ }
if (options[opt_idx].indir != PV_NONE && p != varp) {
s = vim_strsave(*varp);
free_string_option(*p);
@@ -2385,10 +2475,12 @@ static char *set_string_option(const int opt_idx, const char *const value,
char *const saved_oldval = xstrdup(oldval);
char *const saved_newval = xstrdup(s);
+ int value_checked = false;
char *const r = (char *)did_set_string_option(
- opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags);
+ opt_idx, (char_u **)varp, (int)true, (char_u *)oldval,
+ NULL, 0, opt_flags, &value_checked);
if (r == NULL) {
- did_set_option(opt_idx, opt_flags, true);
+ did_set_option(opt_idx, opt_flags, true, value_checked);
}
// call autocommand after handling side effects
@@ -2419,28 +2511,24 @@ static bool valid_filetype(char_u *val)
return true;
}
-#ifdef _MSC_VER
-// MSVC optimizations are disabled for this function because it
-// incorrectly generates an empty string for SHM_ALL.
-#pragma optimize("", off)
-#endif
-/*
- * Handle string options that need some action to perform when changed.
- * Returns NULL for success, or an error message for an error.
- */
+/// Handle string options that need some action to perform when changed.
+/// Returns NULL for success, or an error message for an error.
static char_u *
-did_set_string_option (
- int opt_idx, /* index in options[] table */
- char_u **varp, /* pointer to the option variable */
- int new_value_alloced, /* new value was allocated */
- char_u *oldval, /* previous value of the option */
- char_u *errbuf, /* buffer for errors, or NULL */
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+did_set_string_option(
+ int opt_idx, // index in options[] table
+ char_u **varp, // pointer to the option variable
+ int new_value_alloced, // new value was allocated
+ char_u *oldval, // previous value of the option
+ char_u *errbuf, // buffer for errors, or NULL
+ size_t errbuflen, // length of errors buffer
+ int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
+ int *value_checked // value was checked to be safe, no
+ // need to set P_INSECURE
)
{
char_u *errmsg = NULL;
char_u *s, *p;
- int did_chartab = FALSE;
+ int did_chartab = false;
char_u **gvarp;
bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
bool value_changed = false;
@@ -2449,7 +2537,7 @@ did_set_string_option (
* two values for all local options. */
gvarp = (char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- /* Disallow changing some options from secure mode */
+ // Disallow changing some options from secure mode
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
errmsg = e_secure;
@@ -2462,9 +2550,7 @@ did_set_string_option (
// path separator (slash and/or backslash), wildcards and characters that
// are often illegal in a file name. Be more permissive if "secure" is off.
errmsg = e_invarg;
- }
- /* 'backupcopy' */
- else if (gvarp == &p_bkc) {
+ } else if (gvarp == &p_bkc) { // 'backupcopy'
char_u *bkc = p_bkc;
unsigned int *flags = &bkc_flags;
@@ -2489,17 +2575,15 @@ did_set_string_option (
errmsg = e_invarg;
}
}
- }
- /* 'backupext' and 'patchmode' */
- else if (varp == &p_bex || varp == &p_pm) {
+ } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
- *p_pm == '.' ? p_pm + 1 : p_pm) == 0)
+ *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
errmsg = (char_u *)N_("E589: 'backupext' and 'patchmode' are equal");
- }
- /* 'breakindentopt' */
- else if (varp == &curwin->w_p_briopt) {
- if (briopt_check(curwin) == FAIL)
+ }
+ } else if (varp == &curwin->w_p_briopt) { // 'breakindentopt'
+ if (briopt_check(curwin) == FAIL) {
errmsg = e_invarg;
+ }
} else if (varp == &p_isi
|| varp == &(curbuf->b_p_isk)
|| varp == &p_isp
@@ -2508,63 +2592,58 @@ did_set_string_option (
// If the new option is invalid, use old value. 'lisp' option: refill
// g_chartab[] for '-' char
if (init_chartab() == FAIL) {
- did_chartab = TRUE; /* need to restore it below */
- errmsg = e_invarg; /* error in value */
+ did_chartab = true; // need to restore it below
+ errmsg = e_invarg; // error in value
}
- }
- /* 'helpfile' */
- else if (varp == &p_hf) {
- /* May compute new values for $VIM and $VIMRUNTIME */
+ } else if (varp == &p_hf) { // 'helpfile'
+ // May compute new values for $VIM and $VIMRUNTIME
if (didset_vim) {
vim_setenv("VIM", "");
- didset_vim = FALSE;
+ didset_vim = false;
}
if (didset_vimruntime) {
vim_setenv("VIMRUNTIME", "");
- didset_vimruntime = FALSE;
+ didset_vimruntime = false;
}
- }
- /* 'colorcolumn' */
- else if (varp == &curwin->w_p_cc)
+ } else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
errmsg = check_colorcolumn(curwin);
-
- /* 'helplang' */
- else if (varp == &p_hlg) {
- /* Check for "", "ab", "ab,cd", etc. */
+ } else if (varp == &p_hlg) { // 'helplang'
+ // Check for "", "ab", "ab,cd", etc.
for (s = p_hlg; *s != NUL; s += 3) {
if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
errmsg = e_invarg;
break;
}
- if (s[2] == NUL)
+ if (s[2] == NUL) {
break;
+ }
}
} else if (varp == &p_hl) {
// 'highlight'
if (strcmp((char *)(*varp), HIGHLIGHT_INIT) != 0) {
errmsg = e_unsupportedoption;
}
- }
- /* 'nrformats' */
- else if (gvarp == &p_nf) {
- if (check_opt_strings(*varp, p_nf_values, TRUE) != OK)
+ } else if (gvarp == &p_nf) { // 'nrformats'
+ if (check_opt_strings(*varp, p_nf_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_ssop) { // 'sessionoptions'
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK)
+ if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
errmsg = e_invarg;
+ }
if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
- /* Don't allow both "sesdir" and "curdir". */
+ // Don't allow both "sesdir" and "curdir".
(void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
errmsg = e_invarg;
}
} else if (varp == &p_vop) { // 'viewoptions'
- if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK)
+ if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'scrollopt' */
- else if (varp == &p_sbo) {
- if (check_opt_strings(p_sbo, p_scbopt_values, TRUE) != OK)
+ }
+ } else if (varp == &p_sbo) { // 'scrollopt'
+ if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_ambw || (int *)varp == &p_emoji) {
// 'ambiwidth'
if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
@@ -2583,13 +2662,11 @@ did_set_string_option (
ambw_end:
{} // clint prefers {} over ; as an empty statement
}
- }
- /* 'background' */
- else if (varp == &p_bg) {
- if (check_opt_strings(p_bg, p_bg_values, FALSE) == OK) {
+ } else if (varp == &p_bg) { // 'background'
+ if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
int dark = (*p_bg == 'd');
- init_highlight(FALSE, FALSE);
+ init_highlight(false, false);
if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
// The color scheme must have set 'background' back to another
@@ -2599,31 +2676,28 @@ ambw_end:
free_string_option(p_bg);
p_bg = vim_strsave((char_u *)(dark ? "dark" : "light"));
check_string_option(&p_bg);
- init_highlight(FALSE, FALSE);
+ init_highlight(false, false);
}
} else
errmsg = e_invarg;
- }
- /* 'wildmode' */
- else if (varp == &p_wim) {
- if (check_opt_wim() == FAIL)
+ } else if (varp == &p_wim) { // 'wildmode'
+ if (check_opt_wim() == FAIL) {
errmsg = e_invarg;
- }
- /* 'wildoptions' */
- else if (varp == &p_wop) {
- if (check_opt_strings(p_wop, p_wop_values, TRUE) != OK)
+ }
+ // 'wildoptions'
+ } else if (varp == &p_wop) {
+ if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'winaltkeys' */
- else if (varp == &p_wak) {
+ }
+ } else if (varp == &p_wak) { // 'winaltkeys'
if (*p_wak == NUL
- || check_opt_strings(p_wak, p_wak_values, FALSE) != OK)
+ || check_opt_strings(p_wak, p_wak_values, false) != OK) {
errmsg = e_invarg;
- }
- /* 'eventignore' */
- else if (varp == &p_ei) {
- if (check_ei() == FAIL)
+ }
+ } else if (varp == &p_ei) { // 'eventignore'
+ if (check_ei() == FAIL) {
errmsg = e_invarg;
+ }
// 'encoding', 'fileencoding' and 'makeencoding'
} else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
if (gvarp == &p_fenc) {
@@ -2642,7 +2716,7 @@ ambw_end:
}
if (errmsg == NULL) {
- /* canonize the value, so that STRCMP() can be used on it */
+ // canonize the value, so that STRCMP() can be used on it
p = enc_canonize(*varp);
xfree(*varp);
*varp = p;
@@ -2654,7 +2728,7 @@ ambw_end:
}
}
} else if (varp == &p_penc) {
- /* Canonize printencoding if VIM standard one */
+ // Canonize printencoding if VIM standard one
p = enc_canonize(p_penc);
xfree(p_penc);
p_penc = p;
@@ -2662,22 +2736,37 @@ ambw_end:
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'keymap' has
+ // been checked to be safe.
+ secure = 0;
+
// load or unload key mapping tables
errmsg = keymap_init();
+
+ secure = secure_save;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
if (errmsg == NULL) {
if (*curbuf->b_p_keymap != NUL) {
- /* Installed a new keymap, switch on using it. */
+ // Installed a new keymap, switch on using it.
curbuf->b_p_iminsert = B_IMODE_LMAP;
- if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT)
+ if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
curbuf->b_p_imsearch = B_IMODE_LMAP;
+ }
} else {
- /* Cleared the keymap, may reset 'iminsert' and 'imsearch'. */
- if (curbuf->b_p_iminsert == B_IMODE_LMAP)
+ // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
+ if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
curbuf->b_p_iminsert = B_IMODE_NONE;
- if (curbuf->b_p_imsearch == B_IMODE_LMAP)
+ }
+ if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
+ }
}
if ((opt_flags & OPT_LOCAL) == 0) {
set_iminsert_global();
@@ -2685,41 +2774,37 @@ ambw_end:
}
status_redraw_curbuf();
}
- }
- /* 'fileformat' */
- else if (gvarp == &p_ff) {
- if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL))
+ } else if (gvarp == &p_ff) { // 'fileformat'
+ if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
errmsg = e_modifiable;
- else if (check_opt_strings(*varp, p_ff_values, FALSE) != OK)
+ } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
errmsg = e_invarg;
- else {
+ } else {
redraw_titles();
- /* update flag in swap file */
+ // update flag in swap file
ml_setflags(curbuf);
/* Redraw needed when switching to/from "mac": a CR in the text
* will be displayed differently. */
- if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm')
+ if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
redraw_curbuf_later(NOT_VALID);
+ }
}
- }
- /* 'fileformats' */
- else if (varp == &p_ffs) {
- if (check_opt_strings(p_ffs, p_ff_values, TRUE) != OK) {
+ } else if (varp == &p_ffs) { // 'fileformats'
+ if (check_opt_strings(p_ffs, p_ff_values, true) != OK) {
errmsg = e_invarg;
}
- }
-
- /* 'matchpairs' */
- else if (gvarp == &p_mps) {
+ } else if (gvarp == &p_mps) { // 'matchpairs'
if (has_mbyte) {
- for (p = *varp; *p != NUL; ++p) {
+ for (p = *varp; *p != NUL; p++) {
int x2 = -1;
int x3 = -1;
- if (*p != NUL)
+ if (*p != NUL) {
p += mb_ptr2len(p);
- if (*p != NUL)
+ }
+ if (*p != NUL) {
x2 = *p++;
+ }
if (*p != NUL) {
x3 = utf_ptr2char(p);
p += mb_ptr2len(p);
@@ -2728,42 +2813,45 @@ ambw_end:
errmsg = e_invarg;
break;
}
- if (*p == NUL)
+ if (*p == NUL) {
break;
+ }
}
} else {
- /* Check for "x:y,x:y" */
+ // Check for "x:y,x:y"
for (p = *varp; *p != NUL; p += 4) {
if (p[1] != ':' || p[2] == NUL || (p[3] != NUL && p[3] != ',')) {
errmsg = e_invarg;
break;
}
- if (p[3] == NUL)
+ if (p[3] == NUL) {
break;
+ }
}
}
- }
- /* 'comments' */
- else if (gvarp == &p_com) {
+ } else if (gvarp == &p_com) { // 'comments'
for (s = *varp; *s; ) {
while (*s && *s != ':') {
if (vim_strchr((char_u *)COM_ALL, *s) == NULL
&& !ascii_isdigit(*s) && *s != '-') {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
- ++s;
+ s++;
}
- if (*s++ == NUL)
+ if (*s++ == NUL) {
errmsg = (char_u *)N_("E524: Missing colon");
- else if (*s == ',' || *s == NUL)
+ } else if (*s == ',' || *s == NUL) {
errmsg = (char_u *)N_("E525: Zero length string");
- if (errmsg != NULL)
+ }
+ if (errmsg != NULL) {
break;
+ }
while (*s && *s != ',') {
- if (*s == '\\' && s[1] != NUL)
- ++s;
- ++s;
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
+ }
+ s++;
}
s = skip_to_option_part(s);
}
@@ -2771,17 +2859,14 @@ ambw_end:
errmsg = set_chars_option(curwin, varp);
} else if (varp == &curwin->w_p_fcs) { // 'fillchars'
errmsg = set_chars_option(curwin, varp);
- }
- /* 'cedit' */
- else if (varp == &p_cedit) {
+ } else if (varp == &p_cedit) { // 'cedit'
errmsg = check_cedit();
- }
- /* 'verbosefile' */
- else if (varp == &p_vfile) {
+ } else if (varp == &p_vfile) { // 'verbosefile'
verbose_stop();
- if (*p_vfile != NUL && verbose_open() == FAIL)
+ if (*p_vfile != NUL && verbose_open() == FAIL) {
errmsg = e_invarg;
- /* 'shada' */
+ }
+ // 'shada'
} else if (varp == &p_shada) {
// TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
// option.
@@ -2795,133 +2880,115 @@ ambw_end:
// of the function and the set of P_ALLOCED at the end of the fuction.
free_oldval = (options[opt_idx].flags & P_ALLOCED);
for (s = p_shada; *s; ) {
- /* Check it's a valid character */
+ // Check it's a valid character
if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
- if (*s == 'n') { /* name is always last one */
+ if (*s == 'n') { // name is always last one
break;
- } else if (*s == 'r') { /* skip until next ',' */
- while (*++s && *s != ',')
- ;
+ } else if (*s == 'r') { // skip until next ','
+ while (*++s && *s != ',') {}
} else if (*s == '%') {
- /* optional number */
- while (ascii_isdigit(*++s))
- ;
- } else if (*s == '!' || *s == 'h' || *s == 'c')
- ++s; /* no extra chars */
- else { /* must have a number */
- while (ascii_isdigit(*++s))
- ;
+ // optional number
+ while (ascii_isdigit(*++s)) {}
+ } else if (*s == '!' || *s == 'h' || *s == 'c') {
+ s++; // no extra chars
+ } else { // must have a number
+ while (ascii_isdigit(*++s)) {}
if (!ascii_isdigit(*(s - 1))) {
if (errbuf != NULL) {
- sprintf((char *)errbuf,
- _("E526: Missing number after <%s>"),
- transchar_byte(*(s - 1)));
+ vim_snprintf((char *)errbuf, errbuflen,
+ _("E526: Missing number after <%s>"),
+ transchar_byte(*(s - 1)));
errmsg = errbuf;
} else
errmsg = (char_u *)"";
break;
}
}
- if (*s == ',')
- ++s;
- else if (*s) {
- if (errbuf != NULL)
+ if (*s == ',') {
+ s++;
+ } else if (*s) {
+ if (errbuf != NULL) {
errmsg = (char_u *)N_("E527: Missing comma");
- else
+ } else {
errmsg = (char_u *)"";
+ }
break;
}
}
- if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0)
+ if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
errmsg = (char_u *)N_("E528: Must specify a ' value");
- }
- /* 'showbreak' */
- else if (varp == &p_sbr) {
+ }
+ } else if (varp == &p_sbr) { // 'showbreak'
for (s = p_sbr; *s; ) {
- if (ptr2cells(s) != 1)
+ if (ptr2cells(s) != 1) {
errmsg = (char_u *)N_("E595: contains unprintable or wide character");
+ }
MB_PTR_ADV(s);
}
- }
-
- // 'guicursor'
- else if (varp == &p_guicursor) {
+ } else if (varp == &p_guicursor) { // 'guicursor'
errmsg = parse_shape_opt(SHAPE_CURSOR);
- }
-
- else if (varp == &p_popt)
+ } else if (varp == &p_popt) {
errmsg = parse_printoptions();
- else if (varp == &p_pmfn)
+ } else if (varp == &p_pmfn) {
errmsg = parse_printmbfont();
-
- /* 'langmap' */
- else if (varp == &p_langmap)
+ } else if (varp == &p_langmap) { // 'langmap'
langmap_set();
-
- /* 'breakat' */
- else if (varp == &p_breakat)
+ } else if (varp == &p_breakat) { // 'breakat'
fill_breakat_flags();
-
- /* 'titlestring' and 'iconstring' */
- else if (varp == &p_titlestring || varp == &p_iconstring) {
+ } else if (varp == &p_titlestring || varp == &p_iconstring) {
+ // 'titlestring' and 'iconstring'
int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
- /* NULL => statusline syntax */
- if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL)
+ // NULL => statusline syntax
+ if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
stl_syntax |= flagval;
- else
+ } else {
stl_syntax &= ~flagval;
+ }
did_set_title(varp == &p_iconstring);
- }
-
- /* 'selection' */
- else if (varp == &p_sel) {
+ } else if (varp == &p_sel) { // 'selection'
if (*p_sel == NUL
- || check_opt_strings(p_sel, p_sel_values, FALSE) != OK)
+ || check_opt_strings(p_sel, p_sel_values, false) != OK) {
errmsg = e_invarg;
- }
- /* 'selectmode' */
- else if (varp == &p_slm) {
- if (check_opt_strings(p_slm, p_slm_values, TRUE) != OK)
+ }
+ } else if (varp == &p_slm) { // 'selectmode'
+ if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
errmsg = e_invarg;
- }
- /* 'keymodel' */
- else if (varp == &p_km) {
- if (check_opt_strings(p_km, p_km_values, TRUE) != OK)
+ }
+ } else if (varp == &p_km) { // 'keymodel'
+ if (check_opt_strings(p_km, p_km_values, true) != OK) {
errmsg = e_invarg;
- else {
+ } else {
km_stopsel = (vim_strchr(p_km, 'o') != NULL);
km_startsel = (vim_strchr(p_km, 'a') != NULL);
}
- }
- /* 'mousemodel' */
- else if (varp == &p_mousem) {
- if (check_opt_strings(p_mousem, p_mousem_values, FALSE) != OK)
+ } else if (varp == &p_mousem) { // 'mousemodel'
+ if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_swb) { // 'switchbuf'
- if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK)
+ if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'debug' */
- else if (varp == &p_debug) {
- if (check_opt_strings(p_debug, p_debug_values, TRUE) != OK)
+ }
+ } else if (varp == &p_debug) { // 'debug'
+ if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_dy) { // 'display'
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK)
+ if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
errmsg = e_invarg;
- else
+ } else {
(void)init_chartab();
-
- }
- /* 'eadirection' */
- else if (varp == &p_ead) {
- if (check_opt_strings(p_ead, p_ead_values, FALSE) != OK)
+ }
+ } else if (varp == &p_ead) { // 'eadirection'
+ if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_cb) { // 'clipboard'
if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
errmsg = e_invarg;
@@ -2931,78 +2998,77 @@ ambw_end:
// When 'spelllang' or 'spellfile' is set and there is a window for this
// buffer in which 'spell' is set load the wordlists.
errmsg = did_set_spell_option(varp == &(curwin->w_s->b_p_spf));
- }
- /* When 'spellcapcheck' is set compile the regexp program. */
- else if (varp == &(curwin->w_s->b_p_spc)) {
+ } else if (varp == &(curwin->w_s->b_p_spc)) {
+ // When 'spellcapcheck' is set compile the regexp program.
errmsg = compile_cap_prog(curwin->w_s);
- }
- /* 'spellsuggest' */
- else if (varp == &p_sps) {
- if (spell_check_sps() != OK)
+ } else if (varp == &p_sps) { // 'spellsuggest'
+ if (spell_check_sps() != OK) {
errmsg = e_invarg;
- }
- /* 'mkspellmem' */
- else if (varp == &p_msm) {
- if (spell_check_msm() != OK)
+ }
+ } else if (varp == &p_msm) { // 'mkspellmem'
+ if (spell_check_msm() != OK) {
errmsg = e_invarg;
- }
- /* When 'bufhidden' is set, check for valid value. */
- else if (gvarp == &p_bh) {
- if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, FALSE) != OK)
+ }
+ } else if (gvarp == &p_bh) {
+ // When 'bufhidden' is set, check for valid value.
+ if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
errmsg = e_invarg;
- }
- /* When 'buftype' is set, check for valid value. */
- else if (gvarp == &p_bt) {
+ }
+ } else if (gvarp == &p_bt) {
+ // When 'buftype' is set, check for valid value.
if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
|| (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
- || check_opt_strings(curbuf->b_p_bt, p_buftype_values, FALSE) != OK) {
+ || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
errmsg = e_invarg;
} else {
if (curwin->w_status_height) {
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
redraw_later(VALID);
}
curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
redraw_titles();
}
- }
- /* 'statusline' or 'rulerformat' */
- else if (gvarp == &p_stl || varp == &p_ruf) {
+ } else if (gvarp == &p_stl || varp == &p_ruf) {
+ // 'statusline' or 'rulerformat'
int wid;
- if (varp == &p_ruf) /* reset ru_wid first */
+ if (varp == &p_ruf) { // reset ru_wid first
ru_wid = 0;
+ }
s = *varp;
if (varp == &p_ruf && *s == '%') {
- /* set ru_wid if 'ruf' starts with "%99(" */
- if (*++s == '-') /* ignore a '-' */
+ // set ru_wid if 'ruf' starts with "%99("
+ if (*++s == '-') { // ignore a '-'
s++;
+ }
wid = getdigits_int(&s);
- if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL)
+ if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
ru_wid = wid;
- else
+ } else {
errmsg = check_stl_option(p_ruf);
- }
- /* check 'statusline' only if it doesn't start with "%!" */
- else if (varp == &p_ruf || s[0] != '%' || s[1] != '!')
+ }
+ } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
+ // check 'statusline' only if it doesn't start with "%!"
errmsg = check_stl_option(s);
- if (varp == &p_ruf && errmsg == NULL)
+ }
+ if (varp == &p_ruf && errmsg == NULL) {
comp_col();
- }
- /* check if it is a valid value for 'complete' -- Acevedo */
- else if (gvarp == &p_cpt) {
+ }
+ } else if (gvarp == &p_cpt) {
+ // check if it is a valid value for 'complete' -- Acevedo
for (s = *varp; *s; ) {
while (*s == ',' || *s == ' ')
s++;
- if (!*s)
+ if (!*s) {
break;
+ }
if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
if (*++s != NUL && *s != ',' && *s != ' ') {
if (s[-1] == 'k' || s[-1] == 's') {
- /* skip optional filename after 'k' and 's' */
+ // skip optional filename after 'k' and 's'
while (*s && *s != ',' && *s != ' ') {
if (*s == '\\' && s[1] != NUL) {
s++;
@@ -3011,9 +3077,9 @@ ambw_end:
}
} else {
if (errbuf != NULL) {
- sprintf((char *)errbuf,
- _("E535: Illegal character after <%c>"),
- *--s);
+ vim_snprintf((char *)errbuf, errbuflen,
+ _("E535: Illegal character after <%c>"),
+ *--s);
errmsg = errbuf;
} else
errmsg = (char_u *)"";
@@ -3021,9 +3087,7 @@ ambw_end:
}
}
}
- }
- /* 'completeopt' */
- else if (varp == &p_cot) {
+ } else if (varp == &p_cot) { // 'completeopt'
if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
errmsg = e_invarg;
} else {
@@ -3034,27 +3098,27 @@ ambw_end:
if (check_opt_strings(*varp, p_scl_values, false) != OK) {
errmsg = e_invarg;
}
- }
- /* 'pastetoggle': translate key codes like in a mapping */
- else if (varp == &p_pt) {
+ } else if (varp == &p_pt) {
+ // 'pastetoggle': translate key codes like in a mapping
if (*p_pt) {
(void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
CPO_TO_CPO_FLAGS);
if (p != NULL) {
- if (new_value_alloced)
+ if (new_value_alloced) {
free_string_option(p_pt);
+ }
p_pt = p;
- new_value_alloced = TRUE;
+ new_value_alloced = true;
}
}
- }
- /* 'backspace' */
- else if (varp == &p_bs) {
+ } else if (varp == &p_bs) { // 'backspace'
if (ascii_isdigit(*p_bs)) {
- if (*p_bs >'2' || p_bs[1] != NUL)
+ if (*p_bs >'2' || p_bs[1] != NUL) {
errmsg = e_invarg;
- } else if (check_opt_strings(p_bs, p_bs_values, TRUE) != OK)
+ }
+ } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_bo) {
if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
errmsg = e_invarg;
@@ -3078,64 +3142,59 @@ ambw_end:
errmsg = e_invarg;
}
} else if (varp == &p_cmp) { // 'casemap'
- if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK)
+ if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'diffopt' */
- else if (varp == &p_dip) {
- if (diffopt_changed() == FAIL)
+ }
+ } else if (varp == &p_dip) { // 'diffopt'
+ if (diffopt_changed() == FAIL) {
errmsg = e_invarg;
- }
- /* 'foldmethod' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) {
- if (check_opt_strings(*varp, p_fdm_values, FALSE) != OK
- || *curwin->w_p_fdm == NUL)
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) { // 'foldmethod'
+ if (check_opt_strings(*varp, p_fdm_values, false) != OK
+ || *curwin->w_p_fdm == NUL) {
errmsg = e_invarg;
- else {
+ } else {
foldUpdateAll(curwin);
- if (foldmethodIsDiff(curwin))
+ if (foldmethodIsDiff(curwin)) {
newFoldLevel();
+ }
}
- }
- /* 'foldexpr' */
- else if (varp == &curwin->w_p_fde) {
- if (foldmethodIsExpr(curwin))
+ } else if (varp == &curwin->w_p_fde) { // 'foldexpr'
+ if (foldmethodIsExpr(curwin)) {
foldUpdateAll(curwin);
- }
- /* 'foldmarker' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) {
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
p = vim_strchr(*varp, ',');
- if (p == NULL)
+ if (p == NULL) {
errmsg = (char_u *)N_("E536: comma required");
- else if (p == *varp || p[1] == NUL)
+ } else if (p == *varp || p[1] == NUL) {
errmsg = e_invarg;
- else if (foldmethodIsMarker(curwin))
+ } else if (foldmethodIsMarker(curwin)) {
foldUpdateAll(curwin);
- }
- /* 'commentstring' */
- else if (gvarp == &p_cms) {
- if (**varp != NUL && strstr((char *)*varp, "%s") == NULL)
+ }
+ } else if (gvarp == &p_cms) { // 'commentstring'
+ if (**varp != NUL && strstr((char *)(*varp), "%s") == NULL) {
errmsg = (char_u *)N_(
- "E537: 'commentstring' must be empty or contain %s");
+ "E537: 'commentstring' must be empty or contain %s");
+ }
} else if (varp == &p_fdo) { // 'foldopen'
- if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK)
+ if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'foldclose' */
- else if (varp == &p_fcl) {
- if (check_opt_strings(p_fcl, p_fcl_values, TRUE) != OK)
+ }
+ } else if (varp == &p_fcl) { // 'foldclose'
+ if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
errmsg = e_invarg;
- }
- /* 'foldignore' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) {
- if (foldmethodIsIndent(curwin))
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) { // 'foldignore'
+ if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
+ }
} else if (varp == &p_ve) { // 'virtualedit'
- if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK)
+ if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) {
errmsg = e_invarg;
- else if (STRCMP(p_ve, oldval) != 0) {
- /* Recompute cursor position in case the new 've' setting
- * changes something. */
+ } else if (STRCMP(p_ve, oldval) != 0) {
+ // Recompute cursor position in case the new 've' setting
+ // changes something.
validate_virtcol();
coladvance(curwin->w_virtcol);
}
@@ -3149,16 +3208,15 @@ ambw_end:
|| (p[2] != NUL && p[2] != ',')) {
errmsg = e_invarg;
break;
- } else if (p[2] == NUL)
+ } else if (p[2] == NUL) {
break;
- else
+ } else {
p += 3;
+ }
}
}
- }
- /* 'cinoptions' */
- else if (gvarp == &p_cino) {
- /* TODO: recognize errors */
+ } else if (gvarp == &p_cino) { // 'cinoptions'
+ // TODO(vim): recognize errors
parse_cino(curbuf);
// inccommand
} else if (varp == &p_icm) {
@@ -3170,12 +3228,20 @@ ambw_end:
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
} else if (gvarp == &p_syn) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
} else if (varp == &curwin->w_p_winhl) {
if (!parse_winhl_opt(curwin)) {
@@ -3199,11 +3265,12 @@ ambw_end:
p = (char_u *)MOUSE_ALL;
}
if (p != NULL) {
- for (s = *varp; *s; ++s)
+ for (s = *varp; *s; s++) {
if (vim_strchr(p, *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
+ }
}
}
@@ -3211,28 +3278,32 @@ ambw_end:
* If error detected, restore the previous value.
*/
if (errmsg != NULL) {
- if (new_value_alloced)
+ if (new_value_alloced) {
free_string_option(*varp);
+ }
*varp = oldval;
/*
* When resetting some values, need to act on it.
*/
- if (did_chartab)
+ if (did_chartab) {
(void)init_chartab();
+ }
} else {
- /* Remember where the option was set. */
+ // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID);
/*
* Free string options that are in allocated memory.
* Use "free_oldval", because recursiveness may change the flags under
* our fingers (esp. init_highlight()).
*/
- if (free_oldval)
+ if (free_oldval) {
free_string_option(oldval);
- if (new_value_alloced)
+ }
+ if (new_value_alloced) {
options[opt_idx].flags |= P_ALLOCED;
- else
+ } else {
options[opt_idx].flags &= ~P_ALLOCED;
+ }
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
&& ((int)options[opt_idx].indir & PV_BOTH)) {
@@ -3241,15 +3312,15 @@ ambw_end:
p = get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
free_string_option(*(char_u **)p);
*(char_u **)p = empty_option;
- }
- /* May set global value for local option. */
- else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL)
+ } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
+ // May set global value for local option.
set_string_option_global(opt_idx, varp);
+ }
/*
* Trigger the autocommand only after setting the flags.
*/
- /* When 'syntax' is set, load the syntax of that name */
+ // When 'syntax' is set, load the syntax of that name
if (varp == &(curbuf->b_p_syn)) {
static int syn_recursive = 0;
@@ -3265,6 +3336,11 @@ ambw_end:
// already set to this value.
if (!(opt_flags & OPT_MODELINE) || value_changed) {
static int ft_recursive = 0;
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
ft_recursive++;
did_filetype = true;
@@ -3277,15 +3353,17 @@ ambw_end:
if (varp != &(curbuf->b_p_ft)) {
varp = NULL;
}
+ secure = secure_save;
}
}
if (varp == &(curwin->w_s->b_p_spl)) {
char_u fname[200];
char_u *q = curwin->w_s->b_p_spl;
- /* Skip the first name if it is "cjk". */
- if (STRNCMP(q, "cjk,", 4) == 0)
+ // Skip the first name if it is "cjk".
+ if (STRNCMP(q, "cjk,", 4) == 0) {
q += 4;
+ }
/*
* Source the spell/LANG.vim in 'runtimepath'.
@@ -3293,12 +3371,16 @@ ambw_end:
* Use the first name in 'spelllang' up to '_region' or
* '.encoding'.
*/
- for (p = q; *p != NUL; ++p)
- if (vim_strchr((char_u *)"_.,", *p) != NULL)
+ for (p = q; *p != NUL; p++) {
+ if (!ASCII_ISALNUM(*p) && *p != '-') {
break;
- vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
- (int)(p - q), q);
- source_runtime(fname, DIP_ALL);
+ }
+ }
+ if (p > q) {
+ vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
+ (int)(p - q), q);
+ source_runtime(fname, DIP_ALL);
+ }
}
}
@@ -3312,28 +3394,22 @@ ambw_end:
if (curwin->w_curswant != MAXCOL
&& (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
check_redraw(options[opt_idx].flags);
return errmsg;
-}
-#ifdef _MSC_VER
-#pragma optimize("", on)
-#endif
+} // NOLINT(readability/fn_size)
-/*
- * Simple int comparison function for use with qsort()
- */
+/// Simple int comparison function for use with qsort()
static int int_cmp(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
-/*
- * Handle setting 'colorcolumn' or 'textwidth' in window "wp".
- * Returns error message, NULL if it's OK.
- */
+/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
+///
+/// @return error message, NULL if it's OK.
char_u *check_colorcolumn(win_T *wp)
{
char_u *s;
@@ -3342,19 +3418,22 @@ char_u *check_colorcolumn(win_T *wp)
int color_cols[256];
int j = 0;
- if (wp->w_buffer == NULL)
- return NULL; /* buffer was closed */
+ if (wp->w_buffer == NULL) {
+ return NULL; // buffer was closed
+ }
for (s = wp->w_p_cc; *s != NUL && count < 255; ) {
if (*s == '-' || *s == '+') {
- /* -N and +N: add to 'textwidth' */
+ // -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
- ++s;
- if (!ascii_isdigit(*s))
+ s++;
+ if (!ascii_isdigit(*s)) {
return e_invarg;
+ }
col = col * getdigits_int(&s);
- if (wp->w_buffer->b_p_tw == 0)
- goto skip; /* 'textwidth' not set, skip this item */
+ if (wp->w_buffer->b_p_tw == 0) {
+ goto skip; // 'textwidth' not set, skip this item
+ }
assert((col >= 0
&& wp->w_buffer->b_p_tw <= INT_MAX - col
&& wp->w_buffer->b_p_tw + col >= INT_MIN)
@@ -3362,39 +3441,46 @@ char_u *check_colorcolumn(win_T *wp)
&& wp->w_buffer->b_p_tw >= INT_MIN - col
&& wp->w_buffer->b_p_tw + col <= INT_MAX));
col += (int)wp->w_buffer->b_p_tw;
- if (col < 0)
+ if (col < 0) {
goto skip;
- } else if (ascii_isdigit(*s))
+ }
+ } else if (ascii_isdigit(*s)) {
col = getdigits_int(&s);
- else
+ } else {
return e_invarg;
- color_cols[count++] = col - 1; /* 1-based to 0-based */
+ }
+ color_cols[count++] = col - 1; // 1-based to 0-based
skip:
- if (*s == NUL)
+ if (*s == NUL) {
break;
- if (*s != ',')
+ }
+ if (*s != ',') {
return e_invarg;
- if (*++s == NUL)
- return e_invarg; /* illegal trailing comma as in "set cc=80," */
+ }
+ if (*++s == NUL) {
+ return e_invarg; // illegal trailing comma as in "set cc=80,"
+ }
}
xfree(wp->w_p_cc_cols);
- if (count == 0)
+ if (count == 0) {
wp->w_p_cc_cols = NULL;
- else {
+ } else {
wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
/* sort the columns for faster usage on screen redraw inside
* win_line() */
qsort(color_cols, count, sizeof(int), int_cmp);
- for (unsigned int i = 0; i < count; ++i)
- /* skip duplicates */
- if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i])
+ for (unsigned int i = 0; i < count; i++) {
+ // skip duplicates
+ if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
wp->w_p_cc_cols[j++] = color_cols[i];
- wp->w_p_cc_cols[j] = -1; /* end marker */
+ }
+ }
+ wp->w_p_cc_cols[j] = -1; // end marker
}
- return NULL; /* no error */
+ return NULL; // no error
}
@@ -3469,7 +3555,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
}
p = *varp;
while (*p) {
- for (i = 0; i < entries; ++i) {
+ for (i = 0; i < entries; i++) {
len = (int)STRLEN(tab[i].name);
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
@@ -3516,14 +3602,16 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
}
}
- if (i == entries)
+ if (i == entries) {
return e_invarg;
- if (*p == ',')
- ++p;
+ }
+ if (*p == ',') {
+ p++;
+ }
}
}
- return NULL; /* no error */
+ return NULL; // no error
}
/*
@@ -3537,11 +3625,13 @@ char_u *check_stl_option(char_u *s)
static char_u errbuf[80];
while (*s && itemcnt < STL_MAX_ITEM) {
- /* Check for valid keys after % sequences */
- while (*s && *s != '%')
+ // Check for valid keys after % sequences
+ while (*s && *s != '%') {
s++;
- if (!*s)
+ }
+ if (!*s) {
break;
+ }
s++;
if (*s != '%' && *s != ')') {
itemcnt++;
@@ -3552,16 +3642,20 @@ char_u *check_stl_option(char_u *s)
}
if (*s == ')') {
s++;
- if (--groupdepth < 0)
+ if (--groupdepth < 0) {
break;
+ }
continue;
}
- if (*s == '-')
+ if (*s == '-') {
s++;
- while (ascii_isdigit(*s))
+ }
+ while (ascii_isdigit(*s)) {
s++;
- if (*s == STL_USER_HL)
+ }
+ if (*s == STL_USER_HL) {
continue;
+ }
if (*s == '.') {
s++;
while (*s && ascii_isdigit(*s))
@@ -3572,20 +3666,23 @@ char_u *check_stl_option(char_u *s)
continue;
}
if (vim_strchr(STL_ALL, *s) == NULL) {
- return illegal_char(errbuf, *s);
+ return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
s++;
while (*s != '}' && *s)
s++;
- if (*s != '}')
+ if (*s != '}') {
return (char_u *)N_("E540: Unclosed expression sequence");
+ }
}
}
- if (itemcnt >= STL_MAX_ITEM)
+ if (itemcnt >= STL_MAX_ITEM) {
return (char_u *)N_("E541: too many items");
- if (groupdepth != 0)
+ }
+ if (groupdepth != 0) {
return (char_u *)N_("E542: unbalanced groups");
+ }
return NULL;
}
@@ -3622,15 +3719,15 @@ static char_u *compile_cap_prog(synblock_T *synblock)
regprog_T *rp = synblock->b_cap_prog;
char_u *re;
- if (*synblock->b_p_spc == NUL)
+ if (*synblock->b_p_spc == NUL) {
synblock->b_cap_prog = NULL;
- else {
- /* Prepend a ^ so that we only match at one column */
+ } else {
+ // Prepend a ^ so that we only match at one column
re = concat_str((char_u *)"^", synblock->b_p_spc);
synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
- synblock->b_cap_prog = rp; /* restore the previous program */
+ synblock->b_cap_prog = rp; // restore the previous program
return e_invarg;
}
}
@@ -3718,20 +3815,21 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
{
int old_value = *(int *)varp;
- /* Disallow changing some options from secure mode */
+ // Disallow changing some options from secure mode
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
return (char *)e_secure;
}
- *(int *)varp = value; /* set the new value */
- /* Remember where the option was set. */
+ *(int *)varp = value; // set the new value
+ // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID);
- /* May set global value for local option. */
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0)
+ // May set global value for local option.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value;
+ }
// Ensure that options set to p_force_on cannot be disabled.
if ((int *)varp == &p_force_on && p_force_on == false) {
@@ -3775,72 +3873,65 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
curbuf = save_curbuf;
}
} else if ((int *)varp == &curbuf->b_p_ro) {
- /* when 'readonly' is reset globally, also reset readonlymode */
- if (!curbuf->b_p_ro && (opt_flags & OPT_LOCAL) == 0)
- readonlymode = FALSE;
+ // when 'readonly' is reset globally, also reset readonlymode
+ if (!curbuf->b_p_ro && (opt_flags & OPT_LOCAL) == 0) {
+ readonlymode = false;
+ }
- /* when 'readonly' is set may give W10 again */
- if (curbuf->b_p_ro)
+ // when 'readonly' is set may give W10 again
+ if (curbuf->b_p_ro) {
curbuf->b_did_warn = false;
+ }
redraw_titles();
- }
- /* when 'modifiable' is changed, redraw the window title */
- else if ((int *)varp == &curbuf->b_p_ma) {
+ } else if ((int *)varp == &curbuf->b_p_ma) {
+ // when 'modifiable' is changed, redraw the window title
redraw_titles();
- }
- /* when 'endofline' is changed, redraw the window title */
- else if ((int *)varp == &curbuf->b_p_eol) {
+ } else if ((int *)varp == &curbuf->b_p_eol) {
+ // when 'endofline' is changed, redraw the window title
redraw_titles();
} else if ((int *)varp == &curbuf->b_p_fixeol) {
// when 'fixeol' is changed, redraw the window title
redraw_titles();
- }
- /* when 'bomb' is changed, redraw the window title and tab page text */
- else if ((int *)varp == &curbuf->b_p_bomb) {
+ } else if ((int *)varp == &curbuf->b_p_bomb) {
+ // when 'bomb' is changed, redraw the window title and tab page text
redraw_titles();
- }
- /* when 'bin' is set also set some other options */
- else if ((int *)varp == &curbuf->b_p_bin) {
+ } else if ((int *)varp == &curbuf->b_p_bin) {
+ // when 'bin' is set also set some other options
set_options_bin(old_value, curbuf->b_p_bin, opt_flags);
redraw_titles();
- }
- /* when 'buflisted' changes, trigger autocommands */
- else if ((int *)varp == &curbuf->b_p_bl && old_value != curbuf->b_p_bl) {
+ } else if ((int *)varp == &curbuf->b_p_bl && old_value != curbuf->b_p_bl) {
+ // when 'buflisted' changes, trigger autocommands
apply_autocmds(curbuf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE,
- NULL, NULL, TRUE, curbuf);
- }
- /* when 'swf' is set, create swapfile, when reset remove swapfile */
- else if ((int *)varp == (int *)&curbuf->b_p_swf) {
- if (curbuf->b_p_swf && p_uc)
- ml_open_file(curbuf); /* create the swap file */
- else
- /* no need to reset curbuf->b_may_swap, ml_open_file() will check
- * buf->b_p_swf */
- mf_close_file(curbuf, true); /* remove the swap file */
- }
- /* when 'terse' is set change 'shortmess' */
- else if ((int *)varp == &p_terse) {
+ NULL, NULL, true, curbuf);
+ } else if ((int *)varp == (int *)&curbuf->b_p_swf) {
+ // when 'swf' is set, create swapfile, when reset remove swapfile
+ if (curbuf->b_p_swf && p_uc) {
+ ml_open_file(curbuf); // create the swap file
+ } else {
+ // no need to reset curbuf->b_may_swap, ml_open_file() will check
+ // buf->b_p_swf
+ mf_close_file(curbuf, true); // remove the swap file
+ }
+ } else if ((int *)varp == &p_terse) {
+ // when 'terse' is set change 'shortmess'
char_u *p;
p = vim_strchr(p_shm, SHM_SEARCH);
- /* insert 's' in p_shm */
+ // insert 's' in p_shm
if (p_terse && p == NULL) {
STRCPY(IObuff, p_shm);
STRCAT(IObuff, "s");
set_string_option_direct((char_u *)"shm", -1, IObuff, OPT_FREE, 0);
- }
- /* remove 's' from p_shm */
- else if (!p_terse && p != NULL)
+ } else if (!p_terse && p != NULL) { // remove 's' from p_shm
STRMOVE(p, p + 1);
- }
- /* when 'paste' is set or reset also change other options */
- else if ((int *)varp == &p_paste) {
+ }
+ } else if ((int *)varp == &p_paste) {
+ // when 'paste' is set or reset also change other options
paste_option_changed();
- }
- /* when 'insertmode' is set from an autocommand need to do work here */
- else if ((int *)varp == &p_im) {
+ } else if ((int *)varp == &p_im) {
+ // when 'insertmode' is set from an autocommand need to do work here
if (p_im) {
if ((State & INSERT) == 0) {
need_start_insertmode = true;
@@ -3854,25 +3945,21 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
}
restart_edit = 0;
}
- }
- /* when 'ignorecase' is set or reset and 'hlsearch' is set, redraw */
- else if ((int *)varp == &p_ic && p_hls) {
+ } else if ((int *)varp == &p_ic && p_hls) {
+ // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
redraw_all_later(SOME_VALID);
- }
- /* when 'hlsearch' is set or reset: reset no_hlsearch */
- else if ((int *)varp == &p_hls) {
- SET_NO_HLSEARCH(FALSE);
- }
- /* when 'scrollbind' is set: snapshot the current position to avoid a jump
- * at the end of normal_cmd() */
- else if ((int *)varp == &curwin->w_p_scb) {
+ } else if ((int *)varp == &p_hls) {
+ // when 'hlsearch' is set or reset: reset no_hlsearch
+ SET_NO_HLSEARCH(false);
+ } else if ((int *)varp == &curwin->w_p_scb) {
+ // when 'scrollbind' is set: snapshot the current position to avoid a jump
+ // at the end of normal_cmd()
if (curwin->w_p_scb) {
- do_check_scrollbind(FALSE);
+ do_check_scrollbind(false);
curwin->w_scbind_pos = curwin->w_topline;
}
- }
- /* There can be only one window with 'previewwindow' set. */
- else if ((int *)varp == &curwin->w_p_pvw) {
+ } else if ((int *)varp == &curwin->w_p_pvw) {
+ // There can be only one window with 'previewwindow' set.
if (curwin->w_p_pvw) {
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (win->w_p_pvw && win != curwin) {
@@ -3910,17 +3997,17 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
pseps[0] = '\\';
}
- /* need to adjust the file name arguments and buffer names. */
+ // need to adjust the file name arguments and buffer names.
buflist_slash_adjust();
alist_slash_adjust();
scriptnames_slash_adjust();
}
#endif
-
- /* If 'wrap' is set, set w_leftcol to zero. */
else if ((int *)varp == &curwin->w_p_wrap) {
- if (curwin->w_p_wrap)
+ // If 'wrap' is set, set w_leftcol to zero.
+ if (curwin->w_p_wrap) {
curwin->w_leftcol = 0;
+ }
} else if ((int *)varp == &p_ea) {
if (p_ea && !old_value) {
win_equal(curwin, false, 0);
@@ -3928,22 +4015,18 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
} else if ((int *)varp == &p_acd) {
// Change directories when the 'acd' option is set now.
do_autochdir();
- }
- /* 'diff' */
- else if ((int *)varp == &curwin->w_p_diff) {
- /* May add or remove the buffer from the list of diff buffers. */
+ } else if ((int *)varp == &curwin->w_p_diff) { // 'diff'
+ // May add or remove the buffer from the list of diff buffers.
diff_buf_adjust(curwin);
- if (foldmethodIsDiff(curwin))
+ if (foldmethodIsDiff(curwin)) {
foldUpdateAll(curwin);
- }
-
-
- /* 'spell' */
- else if ((int *)varp == &curwin->w_p_spell) {
+ }
+ } else if ((int *)varp == &curwin->w_p_spell) { // 'spell'
if (curwin->w_p_spell) {
char_u *errmsg = did_set_spelllang(curwin);
- if (errmsg != NULL)
+ if (errmsg != NULL) {
EMSG(_(errmsg));
+ }
}
}
@@ -3953,13 +4036,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
* 'arabic' is set, handle various sub-settings.
*/
if (!p_tbidi) {
- /* set rightleft mode */
+ // set rightleft mode
if (!curwin->w_p_rl) {
- curwin->w_p_rl = TRUE;
+ curwin->w_p_rl = true;
changed_window_setting();
}
- /* Enable Arabic shaping (major part of what Arabic requires) */
+ // Enable Arabic shaping (major part of what Arabic requires)
if (!p_arshape) {
p_arshape = true;
redraw_all_later(NOT_VALID);
@@ -3977,8 +4060,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
}
- /* set 'delcombine' */
- p_deco = TRUE;
+ // set 'delcombine'
+ p_deco = true;
// Force-set the necessary keymap for arabic.
set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
@@ -3987,9 +4070,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
* 'arabic' is reset, handle various sub-settings.
*/
if (!p_tbidi) {
- /* reset rightleft mode */
+ // reset rightleft mode
if (curwin->w_p_rl) {
- curwin->w_p_rl = FALSE;
+ curwin->w_p_rl = false;
changed_window_setting();
}
@@ -4000,7 +4083,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
/* 'delcombine' isn't reset, it is a global option and another
* window may still want it "on". */
- /* Revert to the default keymap */
+ // Revert to the default keymap
curbuf->b_p_iminsert = B_IMODE_NONE;
curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
}
@@ -4040,10 +4123,11 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
BOOLEAN_OBJ(value));
}
- comp_col(); /* in case 'ruler' or 'showcmd' changed */
+ comp_col(); // in case 'ruler' or 'showcmd' changed
if (curwin->w_curswant != MAXCOL
- && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
+ curwin->w_set_curswant = true;
+ }
check_redraw(options[opt_idx].flags);
return NULL;
@@ -4064,11 +4148,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
{
char_u *errmsg = NULL;
long old_value = *(long *)varp;
- long old_Rows = Rows; /* remember old Rows */
- long old_Columns = Columns; /* remember old Columns */
+ long old_Rows = Rows; // remember old Rows
+ long old_Columns = Columns; // remember old Columns
long *pp = (long *)varp;
- /* Disallow changing some options from secure mode. */
+ // Disallow changing some options from secure mode.
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
return (char *)e_secure;
@@ -4374,21 +4458,21 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
&& curwin->w_height > 0))
&& full_screen) {
if (pp == &(curwin->w_p_scr)) {
- if (curwin->w_p_scr != 0)
+ if (curwin->w_p_scr != 0) {
errmsg = e_scroll;
+ }
win_comp_scroll(curwin);
- }
- /* If 'scroll' became invalid because of a side effect silently adjust
- * it. */
- else if (curwin->w_p_scr <= 0)
+ } else if (curwin->w_p_scr <= 0) {
+ // If 'scroll' became invalid because of a side effect silently adjust it.
curwin->w_p_scr = 1;
- else /* curwin->w_p_scr > curwin->w_height */
+ } else { // curwin->w_p_scr > curwin->w_height
curwin->w_p_scr = curwin->w_height;
+ }
}
if ((p_sj < -100 || p_sj >= Rows) && full_screen) {
- if (Rows != old_Rows) /* Rows changed, just adjust p_sj */
+ if (Rows != old_Rows) { // Rows changed, just adjust p_sj
p_sj = Rows / 2;
- else {
+ } else {
errmsg = e_scroll;
p_sj = 1;
}
@@ -4424,10 +4508,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
INTEGER_OBJ(value));
}
- comp_col(); /* in case 'columns' or 'ls' changed */
+ comp_col(); // in case 'columns' or 'ls' changed
if (curwin->w_curswant != MAXCOL
- && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
+ curwin->w_set_curswant = true;
+ }
check_redraw(options[opt_idx].flags);
return (char *)errmsg;
@@ -4458,7 +4543,7 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags,
*/
static void check_redraw(uint32_t flags)
{
- /* Careful: P_RCLR and P_RALL are a combination of other P_ flags */
+ // Careful: P_RCLR and P_RALL are a combination of other P_ flags
bool doclear = (flags & P_RCLR) == P_RCLR;
bool all = ((flags & P_RALL) == P_RALL || doclear);
@@ -4691,7 +4776,7 @@ int get_option_value(
// opt_type).
//
// Returned flags:
-// 0 hidden or unknown option, also option that does not have requested
+// 0 hidden or unknown option, also option that does not have requested
// type (see SREQ_* in option_defs.h)
// see SOPT_* in option_defs.h for other flags
//
@@ -4845,7 +4930,7 @@ char *set_option_value(const char *const name, const long number,
return set_string_option(opt_idx, s, opt_flags);
} else {
varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp != NULL) { /* hidden option is not changed */
+ if (varp != NULL) { // hidden option is not changed
if (number == 0 && string != NULL) {
int idx;
@@ -4904,10 +4989,10 @@ static int find_key_option(const char_u *arg)
* if 'all' == 0: show changed options
* if 'all' == 1: show all normal options
*/
-static void
-showoptions (
+static void
+showoptions(
int all,
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL and/or OPT_GLOBAL
)
{
vimoption_T *p;
@@ -4934,28 +5019,26 @@ showoptions (
MSG_PUTS_TITLE(_("\n--- Options ---"));
}
- /*
- * do the loop two times:
- * 1. display the short items
- * 2. display the long items (only strings and numbers)
- */
- for (run = 1; run <= 2 && !got_int; ++run) {
- /*
- * collect the items in items[]
- */
+ // Do the loop two times:
+ // 1. display the short items
+ // 2. display the long items (only strings and numbers)
+ for (run = 1; run <= 2 && !got_int; run++) {
+ // collect the items in items[]
item_count = 0;
for (p = &options[0]; p->fullname != NULL; p++) {
varp = NULL;
if (opt_flags != 0) {
- if (p->indir != PV_NONE)
+ if (p->indir != PV_NONE) {
varp = get_varp_scope(p, opt_flags);
- } else
+ }
+ } else {
varp = get_varp(p);
+ }
if (varp != NULL
&& (all == 1 || (all == 0 && !optval_default(p, varp)))) {
- if (p->flags & P_BOOL)
- len = 1; /* a toggle option fits always */
- else {
+ if (p->flags & P_BOOL) {
+ len = 1; // a toggle option fits always
+ } else {
option_value2string(p, opt_flags);
len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
}
@@ -4975,18 +5058,21 @@ showoptions (
&& (Columns + GAP - 3) / INC >= INT_MIN
&& (Columns + GAP - 3) / INC <= INT_MAX);
cols = (int)((Columns + GAP - 3) / INC);
- if (cols == 0)
+ if (cols == 0) {
cols = 1;
+ }
rows = (item_count + cols - 1) / cols;
- } else /* run == 2 */
+ } else { // run == 2
rows = item_count;
- for (row = 0; row < rows && !got_int; ++row) {
- msg_putchar('\n'); /* go to next line */
- if (got_int) /* 'q' typed in more */
+ }
+ for (row = 0; row < rows && !got_int; row++) {
+ msg_putchar('\n'); // go to next line
+ if (got_int) { // 'q' typed in more
break;
+ }
col = 0;
for (i = row; i < item_count; i += rows) {
- msg_col = col; /* make columns */
+ msg_col = col; // make columns
showoneopt(items[i], opt_flags);
col += INC;
}
@@ -4997,15 +5083,14 @@ showoptions (
xfree(items);
}
-/*
- * Return TRUE if option "p" has its default value.
- */
+/// Return true if option "p" has its default value.
static int optval_default(vimoption_T *p, char_u *varp)
{
int dvi;
- if (varp == NULL)
- return TRUE; /* hidden option is always at default */
+ if (varp == NULL) {
+ return true; // hidden option is always at default
+ }
dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT;
if (p->flags & P_NUM) {
return *(long *)varp == (long)(intptr_t)p->def_val[dvi];
@@ -5044,17 +5129,17 @@ void ui_refresh_options(void)
* showoneopt: show the value of one option
* must not be called with a hidden option!
*/
-static void
-showoneopt (
+static void
+showoneopt(
vimoption_T *p,
- int opt_flags /* OPT_LOCAL or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL or OPT_GLOBAL
)
{
char_u *varp;
int save_silent = silent_mode;
- silent_mode = FALSE;
- info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
+ silent_mode = false;
+ info_message = true; // use mch_msg(), not mch_errmsg()
varp = get_varp_scope(p, opt_flags);
@@ -5070,13 +5155,13 @@ showoneopt (
MSG_PUTS(p->fullname);
if (!(p->flags & P_BOOL)) {
msg_putchar('=');
- /* put value string in NameBuff */
+ // put value string in NameBuff
option_value2string(p, opt_flags);
msg_outtrans(NameBuff);
}
silent_mode = save_silent;
- info_message = FALSE;
+ info_message = false;
}
/*
@@ -5095,7 +5180,7 @@ showoneopt (
* may have set them when doing ":edit file" and the
* user has set them back at the default or fresh
* value.
- * When "local_only" is TRUE, don't write fresh
+ * When "local_only" is true, don't write fresh
* values, only local values (for ":mkview").
* (fresh value = value used for a new buffer or window for a local option).
*
@@ -5104,9 +5189,9 @@ showoneopt (
int makeset(FILE *fd, int opt_flags, int local_only)
{
vimoption_T *p;
- char_u *varp; /* currently used value */
- char_u *varp_fresh; /* local value */
- char_u *varp_local = NULL; /* fresh value */
+ char_u *varp; // currently used value
+ char_u *varp_fresh; // local value
+ char_u *varp_local = NULL; // fresh value
char *cmd;
int round;
int pri;
@@ -5120,35 +5205,40 @@ int makeset(FILE *fd, int opt_flags, int local_only)
* Do the loop over "options[]" twice: once for options with the
* P_PRI_MKRC flag and once without.
*/
- for (pri = 1; pri >= 0; --pri) {
- for (p = &options[0]; p->fullname; p++)
+ for (pri = 1; pri >= 0; pri--) {
+ for (p = &options[0]; p->fullname; p++) {
if (!(p->flags & P_NO_MKRC)
&& ((pri == 1) == ((p->flags & P_PRI_MKRC) != 0))) {
- /* skip global option when only doing locals */
- if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL))
+ // skip global option when only doing locals
+ if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) {
continue;
+ }
/* Do not store options like 'bufhidden' and 'syntax' in a vimrc
* file, they are always buffer-specific. */
- if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB))
+ if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB)) {
continue;
+ }
varp = get_varp_scope(p, opt_flags);
- /* Hidden options are never written. */
- if (!varp)
+ // Hidden options are never written.
+ if (!varp) {
continue;
- /* Global values are only written when not at the default value. */
- if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp))
+ }
+ // Global values are only written when not at the default value.
+ if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) {
continue;
+ }
round = 2;
if (p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
- /* skip window-local option when only doing globals */
- if (!(opt_flags & OPT_LOCAL))
+ // skip window-local option when only doing globals
+ if (!(opt_flags & OPT_LOCAL)) {
continue;
- /* When fresh value of window-local option is not at the
- * default, need to write it too. */
+ }
+ // When fresh value of window-local option is not at the
+ // default, need to write it too.
if (!(opt_flags & OPT_GLOBAL) && !local_only) {
varp_fresh = get_varp_scope(p, OPT_GLOBAL);
if (!optval_default(p, varp_fresh)) {
@@ -5162,20 +5252,23 @@ int makeset(FILE *fd, int opt_flags, int local_only)
/* Round 1: fresh value for window-local options.
* Round 2: other values */
- for (; round <= 2; varp = varp_local, ++round) {
- if (round == 1 || (opt_flags & OPT_GLOBAL))
+ for (; round <= 2; varp = varp_local, round++) {
+ if (round == 1 || (opt_flags & OPT_GLOBAL)) {
cmd = "set";
- else
+ } else {
cmd = "setlocal";
+ }
if (p->flags & P_BOOL) {
- if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL)
+ if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL) {
return FAIL;
+ }
} else if (p->flags & P_NUM) {
- if (put_setnum(fd, cmd, p->fullname, (long *)varp) == FAIL)
+ if (put_setnum(fd, cmd, p->fullname, (long *)varp) == FAIL) {
return FAIL;
- } else { /* P_STRING */
- int do_endif = FALSE;
+ }
+ } else { // P_STRING
+ int do_endif = false;
// Don't set 'syntax' and 'filetype' again if the value is
// already right, avoids reloading the syntax file.
@@ -5191,12 +5284,14 @@ int makeset(FILE *fd, int opt_flags, int local_only)
(p->flags & P_EXPAND) != 0) == FAIL)
return FAIL;
if (do_endif) {
- if (put_line(fd, "endif") == FAIL)
+ if (put_line(fd, "endif") == FAIL) {
return FAIL;
+ }
}
}
}
}
+ }
}
return OK;
}
@@ -5207,19 +5302,20 @@ int makeset(FILE *fd, int opt_flags, int local_only)
*/
int makefoldset(FILE *fd)
{
- if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, FALSE) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, FALSE)
+ if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, false) == FAIL
+ || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, false)
== FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, FALSE)
+ || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, false)
== FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, FALSE)
+ || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, false)
== FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
|| put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL
|| put_setbool(fd, "setlocal", "fen", curwin->w_p_fen) == FAIL
- )
+ ) {
return FAIL;
+ }
return OK;
}
@@ -5229,8 +5325,9 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
char_u *s;
char_u *buf;
- if (fprintf(fd, "%s %s=", cmd, name) < 0)
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
+ }
if (*valuep != NULL) {
/* Output 'pastetoggle' as key names. For other
* options some characters have to be escaped with
@@ -5246,17 +5343,19 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
}
} else if (expand) {
buf = xmalloc(MAXPATHL);
- home_replace(NULL, *valuep, buf, MAXPATHL, FALSE);
+ home_replace(NULL, *valuep, buf, MAXPATHL, false);
if (put_escstr(fd, buf, 2) == FAIL) {
xfree(buf);
return FAIL;
}
xfree(buf);
- } else if (put_escstr(fd, *valuep, 2) == FAIL)
+ } else if (put_escstr(fd, *valuep, 2) == FAIL) {
return FAIL;
+ }
}
- if (put_eol(fd) < 0)
+ if (put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
@@ -5264,26 +5363,32 @@ static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep)
{
long wc;
- if (fprintf(fd, "%s %s=", cmd, name) < 0)
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
+ }
if (wc_use_keyname((char_u *)valuep, &wc)) {
- /* print 'wildchar' and 'wildcharm' as a key name */
- if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0)
+ // print 'wildchar' and 'wildcharm' as a key name
+ if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0) {
return FAIL;
- } else if (fprintf(fd, "%" PRId64, (int64_t)*valuep) < 0)
+ }
+ } else if (fprintf(fd, "%" PRId64, (int64_t)(*valuep)) < 0) {
return FAIL;
- if (put_eol(fd) < 0)
+ }
+ if (put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
static int put_setbool(FILE *fd, char *cmd, char *name, int value)
{
- if (value < 0) /* global/local option using global value */
+ if (value < 0) { // global/local option using global value
return OK;
+ }
if (fprintf(fd, "%s %s%s", cmd, value ? "" : "no", name) < 0
- || put_eol(fd) < 0)
+ || put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
@@ -5294,7 +5399,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
* of 'ru_col'.
*/
-#define COL_RULER 17 /* columns needed by standard ruler */
+#define COL_RULER 17 // columns needed by standard ruler
void comp_col(void)
{
@@ -5304,14 +5409,16 @@ void comp_col(void)
ru_col = 0;
if (p_ru) {
ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
- /* no last status line, adjust sc_col */
- if (!last_has_status)
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
sc_col = ru_col;
+ }
}
if (p_sc) {
sc_col += SHOWCMD_COLS;
- if (!p_ru || last_has_status) /* no need for separating space */
- ++sc_col;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
+ }
}
assert(sc_col >= 0
&& INT_MIN + sc_col <= Columns
@@ -5321,10 +5428,12 @@ void comp_col(void)
&& INT_MIN + ru_col <= Columns
&& Columns - ru_col <= INT_MAX);
ru_col = (int)(Columns - ru_col);
- if (sc_col <= 0) /* screen too narrow, will become a mess */
+ if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
- if (ru_col <= 0)
+ }
+ if (ru_col <= 0) {
ru_col = 1;
+ }
}
// Unset local option value, similar to ":set opt<".
@@ -5411,8 +5520,9 @@ void unset_global_local_option(char *name, void *from)
static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
{
if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
- if (p->var == VAR_WIN)
+ if (p->var == VAR_WIN) {
return (char_u *)GLOBAL_WO(get_varp(p));
+ }
return p->var;
}
if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
@@ -5437,7 +5547,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
case PV_BKC: return (char_u *)&(curbuf->b_p_bkc);
case PV_MENC: return (char_u *)&(curbuf->b_p_menc);
}
- return NULL; /* "cannot happen" */
+ return NULL; // "cannot happen"
}
return get_varp(p);
}
@@ -5447,14 +5557,15 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
*/
static char_u *get_varp(vimoption_T *p)
{
- /* hidden option, always return NULL */
- if (p->var == NULL)
+ // hidden option, always return NULL
+ if (p->var == NULL) {
return NULL;
+ }
switch ((int)p->indir) {
case PV_NONE: return p->var;
- /* global option with local value: use local value if it's been set */
+ // global option with local value: use local value if it's been set
case PV_EP: return *curbuf->b_p_ep != NUL
? (char_u *)&curbuf->b_p_ep : p->var;
case PV_KP: return *curbuf->b_p_kp != NUL
@@ -5593,7 +5704,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
default: IEMSG(_("E356: get_varp ERROR"));
}
- /* always return a valid pointer to avoid a crash! */
+ // always return a valid pointer to avoid a crash!
return (char_u *)&(curbuf->b_p_wm);
}
@@ -5602,8 +5713,9 @@ static char_u *get_varp(vimoption_T *p)
*/
char_u *get_equalprg(void)
{
- if (*curbuf->b_p_ep == NUL)
+ if (*curbuf->b_p_ep == NUL) {
return p_ep;
+ }
return curbuf->b_p_ep;
}
@@ -5746,33 +5858,34 @@ void didset_window_options(win_T *wp)
*/
void buf_copy_options(buf_T *buf, int flags)
{
- int should_copy = TRUE;
- char_u *save_p_isk = NULL; /* init for GCC */
+ int should_copy = true;
+ char_u *save_p_isk = NULL; // init for GCC
int dont_do_help;
- int did_isk = FALSE;
+ int did_isk = false;
/*
* Skip this when the option defaults have not been set yet. Happens when
* main() allocates the first buffer.
*/
if (p_cpo != NULL) {
- /*
- * Always copy when entering and 'cpo' contains 'S'.
- * Don't copy when already initialized.
- * Don't copy when 'cpo' contains 's' and not entering.
- * 'S' BCO_ENTER initialized 's' should_copy
- * yes yes X X TRUE
- * yes no yes X FALSE
- * no X yes X FALSE
- * X no no yes FALSE
- * X no no no TRUE
- * no yes no X TRUE
- */
+ //
+ // Always copy when entering and 'cpo' contains 'S'.
+ // Don't copy when already initialized.
+ // Don't copy when 'cpo' contains 's' and not entering.
+ // 'S' BCO_ENTER initialized 's' should_copy
+ // yes yes X X true
+ // yes no yes X false
+ // no X yes X false
+ // X no no yes false
+ // X no no no true
+ // no yes no X true
+ ///
if ((vim_strchr(p_cpo, CPO_BUFOPTGLOB) == NULL || !(flags & BCO_ENTER))
&& (buf->b_p_initialized
|| (!(flags & BCO_ENTER)
- && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
- should_copy = FALSE;
+ && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) {
+ should_copy = false;
+ }
if (should_copy || (flags & BCO_ALWAYS)) {
/* Don't copy the options specific to a help buffer when
@@ -5780,7 +5893,7 @@ void buf_copy_options(buf_T *buf, int flags)
* (jumping back to a help file with CTRL-T or CTRL-O) */
dont_do_help = ((flags & BCO_NOHELP) && buf->b_help)
|| buf->b_p_initialized;
- if (dont_do_help) { /* don't free b_p_isk */
+ if (dont_do_help) { // don't free b_p_isk
save_p_isk = buf->b_p_isk;
buf->b_p_isk = NULL;
}
@@ -5789,8 +5902,8 @@ void buf_copy_options(buf_T *buf, int flags)
* If not already initialized, set 'readonly' and copy 'fileformat'.
*/
if (!buf->b_p_initialized) {
- free_buf_options(buf, TRUE);
- buf->b_p_ro = FALSE; /* don't copy readonly */
+ free_buf_options(buf, true);
+ buf->b_p_ro = false; // don't copy readonly
buf->b_p_fenc = vim_strsave(p_fenc);
switch (*p_ffs) {
case 'm': {
@@ -5853,12 +5966,12 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_cin = p_cin;
buf->b_p_cink = vim_strsave(p_cink);
buf->b_p_cino = vim_strsave(p_cino);
- /* Don't copy 'filetype', it must be detected */
+ // Don't copy 'filetype', it must be detected
buf->b_p_ft = empty_option;
buf->b_p_pi = p_pi;
buf->b_p_cinw = vim_strsave(p_cinw);
buf->b_p_lisp = p_lisp;
- /* Don't copy 'syntax', it must be set */
+ // Don't copy 'syntax', it must be set
buf->b_p_syn = empty_option;
buf->b_p_smc = p_smc;
buf->b_s.b_syn_isk = empty_option;
@@ -5909,15 +6022,16 @@ void buf_copy_options(buf_T *buf, int flags)
* Don't touch these at all when BCO_NOHELP is used and going from
* or to a help buffer.
*/
- if (dont_do_help)
+ if (dont_do_help) {
buf->b_p_isk = save_p_isk;
- else {
+ } else {
buf->b_p_isk = vim_strsave(p_isk);
did_isk = true;
buf->b_p_ts = p_ts;
buf->b_help = false;
- if (buf->b_p_bt[0] == 'h')
+ if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt);
+ }
buf->b_p_ma = p_ma;
}
}
@@ -5926,13 +6040,15 @@ void buf_copy_options(buf_T *buf, int flags)
* When the options should be copied (ignoring BCO_ALWAYS), set the
* flag that indicates that the options have been initialized.
*/
- if (should_copy)
+ if (should_copy) {
buf->b_p_initialized = true;
+ }
}
- check_buf_options(buf); /* make sure we don't have NULLs */
- if (did_isk)
- (void)buf_init_chartab(buf, FALSE);
+ check_buf_options(buf); // make sure we don't have NULLs
+ if (did_isk) {
+ (void)buf_init_chartab(buf, false);
+ }
}
/*
@@ -5970,19 +6086,19 @@ static int expand_option_idx = -1;
static char_u expand_option_name[5] = {'t', '_', NUL, NUL, NUL};
static int expand_option_flags = 0;
-void
-set_context_in_set_cmd (
+void
+set_context_in_set_cmd(
expand_T *xp,
char_u *arg,
- int opt_flags /* OPT_GLOBAL and/or OPT_LOCAL */
+ int opt_flags // OPT_GLOBAL and/or OPT_LOCAL
)
{
char_u nextchar;
- uint32_t flags = 0; /* init for GCC */
- int opt_idx = 0; /* init for GCC */
+ uint32_t flags = 0; // init for GCC
+ int opt_idx = 0; // init for GCC
char_u *p;
char_u *s;
- int is_term_option = FALSE;
+ int is_term_option = false;
int key;
expand_option_flags = opt_flags;
@@ -5999,17 +6115,18 @@ set_context_in_set_cmd (
}
while (p > arg) {
s = p;
- /* count number of backslashes before ' ' or ',' */
+ // count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
- while (s > arg && *(s - 1) == '\\')
- --s;
+ while (s > arg && *(s - 1) == '\\') {
+ s--;
+ }
}
- /* break at a space with an even number of backslashes */
+ // break at a space with an even number of backslashes
if (*p == ' ' && ((p - s) & 1) == 0) {
- ++p;
+ p++;
break;
}
- --p;
+ p--;
}
if (STRNCMP(p, "no", 2) == 0) {
xp->xp_context = EXPAND_BOOL_SETTINGS;
@@ -6021,27 +6138,31 @@ set_context_in_set_cmd (
}
xp->xp_pattern = arg = p;
if (*arg == '<') {
- while (*p != '>')
- if (*p++ == NUL) /* expand terminal option name */
+ while (*p != '>') {
+ if (*p++ == NUL) { // expand terminal option name
return;
+ }
+ }
key = get_special_key_code(arg + 1);
- if (key == 0) { /* unknown name */
+ if (key == 0) { // unknown name
xp->xp_context = EXPAND_NOTHING;
return;
}
nextchar = *++p;
- is_term_option = TRUE;
+ is_term_option = true;
expand_option_name[2] = (char_u)KEY2TERMCAP0(key);
expand_option_name[3] = KEY2TERMCAP1(key);
} else {
if (p[0] == 't' && p[1] == '_') {
p += 2;
- if (*p != NUL)
- ++p;
- if (*p == NUL)
- return; /* expand option name */
+ if (*p != NUL) {
+ p++;
+ }
+ if (*p == NUL) {
+ return; // expand option name
+ }
nextchar = *++p;
- is_term_option = TRUE;
+ is_term_option = true;
expand_option_name[2] = p[-2];
expand_option_name[3] = p[-1];
} else {
@@ -6065,9 +6186,9 @@ set_context_in_set_cmd (
}
}
}
- /* handle "-=" and "+=" */
+ // handle "-=" and "+="
if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') {
- ++p;
+ p++;
nextchar = '=';
}
if ((nextchar != '=' && nextchar != ':')
@@ -6077,16 +6198,18 @@ set_context_in_set_cmd (
}
if (p[1] == NUL) {
xp->xp_context = EXPAND_OLD_SETTING;
- if (is_term_option)
+ if (is_term_option) {
expand_option_idx = -1;
- else
+ } else {
expand_option_idx = opt_idx;
+ }
xp->xp_pattern = p + 1;
return;
}
xp->xp_context = EXPAND_NOTHING;
- if (is_term_option || (flags & P_NUM))
+ if (is_term_option || (flags & P_NUM)) {
return;
+ }
xp->xp_pattern = p + 1;
@@ -6109,22 +6232,24 @@ set_context_in_set_cmd (
xp->xp_backslash = XP_BS_ONE;
} else {
xp->xp_context = EXPAND_FILES;
- /* for 'tags' need three backslashes for a space */
- if (p == (char_u *)&p_tags)
+ // for 'tags' need three backslashes for a space
+ if (p == (char_u *)&p_tags) {
xp->xp_backslash = XP_BS_THREE;
- else
+ } else {
xp->xp_backslash = XP_BS_ONE;
+ }
}
}
/* For an option that is a list of file names, find the start of the
* last file name. */
- for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; --p) {
- /* count number of backslashes before ' ' or ',' */
+ for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; p--) {
+ // count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
s = p;
- while (s > xp->xp_pattern && *(s - 1) == '\\')
- --s;
+ while (s > xp->xp_pattern && *(s - 1) == '\\') {
+ s--;
+ }
if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
|| (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) {
xp->xp_pattern = p + 1;
@@ -6132,7 +6257,7 @@ set_context_in_set_cmd (
}
}
- /* for 'spellsuggest' start at "file:" */
+ // for 'spellsuggest' start at "file:"
if (options[opt_idx].var == (char_u *)&p_sps
&& STRNCMP(p, "file:", 5) == 0) {
xp->xp_pattern = p + 5;
@@ -6157,31 +6282,36 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
* loop == 0: count the number of matching options
* loop == 1: copy the matching options into allocated memory
*/
- for (loop = 0; loop <= 1; ++loop) {
+ for (loop = 0; loop <= 1; loop++) {
regmatch->rm_ic = ic;
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
for (match = 0; match < (int)ARRAY_SIZE(names);
- ++match)
+ match++) {
if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) {
- if (loop == 0)
+ if (loop == 0) {
num_normal++;
- else
+ } else {
(*file)[count++] = vim_strsave((char_u *)names[match]);
+ }
}
+ }
}
for (size_t opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
opt_idx++) {
- if (options[opt_idx].var == NULL)
+ if (options[opt_idx].var == NULL) {
continue;
+ }
if (xp->xp_context == EXPAND_BOOL_SETTINGS
- && !(options[opt_idx].flags & P_BOOL))
+ && !(options[opt_idx].flags & P_BOOL)) {
continue;
- match = FALSE;
+ }
+ match = false;
if (vim_regexec(regmatch, str, (colnr_T)0)
|| (options[opt_idx].shortname != NULL
&& vim_regexec(regmatch,
- (char_u *)options[opt_idx].shortname, (colnr_T)0))){
- match = TRUE;
+ (char_u *)options[opt_idx].shortname,
+ (colnr_T)0))) {
+ match = true;
}
if (match) {
@@ -6252,10 +6382,10 @@ void ExpandOldSetting(int *num_file, char_u ***file)
* Get the value for the numeric or string option *opp in a nice format into
* NameBuff[]. Must not be called with a hidden option!
*/
-static void
-option_value2string (
+static void
+option_value2string(
vimoption_T *opp,
- int opt_flags /* OPT_GLOBAL and/or OPT_LOCAL */
+ int opt_flags // OPT_GLOBAL and/or OPT_LOCAL
)
{
char_u *varp;
@@ -6290,19 +6420,18 @@ option_value2string (
}
}
-/*
- * Return TRUE if "varp" points to 'wildchar' or 'wildcharm' and it can be
- * printed as a keyname.
- * "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
- */
+/// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be
+/// printed as a keyname.
+/// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
static int wc_use_keyname(char_u *varp, long *wcp)
{
if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) {
*wcp = *(long *)varp;
- if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)*wcp) >= 0)
- return TRUE;
+ if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)(*wcp)) >= 0) {
+ return true;
+ }
}
- return FALSE;
+ return false;
}
/*
@@ -6314,7 +6443,7 @@ static int wc_use_keyname(char_u *varp, long *wcp)
*
* langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
* commands.
- * langmap_mapga.ga_data is a sorted table of langmap_entry_T.
+ * langmap_mapga.ga_data is a sorted table of langmap_entry_T.
* This does the same as langmap_mapchar[] for characters >= 256.
*/
/*
@@ -6338,7 +6467,7 @@ static void langmap_set_entry(int from, int to)
assert(langmap_mapga.ga_len >= 0);
unsigned int b = (unsigned int)langmap_mapga.ga_len;
- /* Do a binary search for an existing entry. */
+ // Do a binary search for an existing entry.
while (a != b) {
unsigned int i = (a + b) / 2;
int d = entries[i].from - from;
@@ -6347,19 +6476,20 @@ static void langmap_set_entry(int from, int to)
entries[i].to = to;
return;
}
- if (d < 0)
+ if (d < 0) {
a = i + 1;
- else
+ } else {
b = i;
+ }
}
ga_grow(&langmap_mapga, 1);
- /* insert new entry at position "a" */
+ // insert new entry at position "a"
entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
memmove(entries + 1, entries,
((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
- ++langmap_mapga.ga_len;
+ langmap_mapga.ga_len++;
entries[0].from = from;
entries[0].to = to;
}
@@ -6377,20 +6507,23 @@ int langmap_adjust_mb(int c)
int i = (a + b) / 2;
int d = entries[i].from - c;
- if (d == 0)
- return entries[i].to; /* found matching entry */
- if (d < 0)
+ if (d == 0) {
+ return entries[i].to; // found matching entry
+ }
+ if (d < 0) {
a = i + 1;
- else
+ } else {
b = i;
+ }
}
- return c; /* no entry found, return "c" unmodified */
+ return c; // no entry found, return "c" unmodified
}
static void langmap_init(void)
{
- for (int i = 0; i < 256; i++)
- langmap_mapchar[i] = (char_u)i; /* we init with a one-to-one map */
+ for (int i = 0; i < 256; i++) {
+ langmap_mapchar[i] = (char_u)i; // we init with a one-to-one map
+ }
ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
}
@@ -6404,8 +6537,8 @@ static void langmap_set(void)
char_u *p2;
int from, to;
- ga_clear(&langmap_mapga); /* clear the previous map first */
- langmap_init(); /* back to one-to-one map */
+ ga_clear(&langmap_mapga); // clear the previous map first
+ langmap_init(); // back to one-to-one map
for (p = p_langmap; p[0] != NUL; ) {
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
@@ -6414,13 +6547,14 @@ static void langmap_set(void)
p2++;
}
}
- if (p2[0] == ';')
- ++p2; /* abcd;ABCD form, p2 points to A */
- else
- p2 = NULL; /* aAbBcCdD form, p2 is NULL */
+ if (p2[0] == ';') {
+ p2++; // abcd;ABCD form, p2 points to A
+ } else {
+ p2 = NULL; // aAbBcCdD form, p2 is NULL
+ }
while (p[0]) {
if (p[0] == ',') {
- ++p;
+ p++;
break;
}
if (p[0] == '\\' && p[1] != NUL) {
@@ -6450,9 +6584,9 @@ static void langmap_set(void)
return;
}
- if (from >= 256)
+ if (from >= 256) {
langmap_set_entry(from, to);
- else {
+ } else {
assert(to <= UCHAR_MAX);
langmap_mapchar[from & 255] = (char_u)to;
}
@@ -6470,7 +6604,7 @@ static void langmap_set(void)
p);
return;
}
- ++p;
+ p++;
}
break;
}
@@ -6479,14 +6613,13 @@ static void langmap_set(void)
}
}
-/*
- * Return TRUE if format option 'x' is in effect.
- * Take care of no formatting when 'paste' is set.
- */
+/// Return true if format option 'x' is in effect.
+/// Take care of no formatting when 'paste' is set.
int has_format_option(int x)
{
- if (p_paste)
- return FALSE;
+ if (p_paste) {
+ return false;
+ }
return vim_strchr(curbuf->b_p_fo, x) != NULL;
}
@@ -6505,7 +6638,7 @@ bool shortmess(int x)
*/
static void paste_option_changed(void)
{
- static int old_p_paste = FALSE;
+ static int old_p_paste = false;
static int save_sm = 0;
static int save_sta = 0;
static int save_ru = 0;
@@ -6518,7 +6651,7 @@ static void paste_option_changed(void)
* Save the current values, so they can be restored later.
*/
if (!old_p_paste) {
- /* save options for each buffer */
+ // save options for each buffer
FOR_ALL_BUFFERS(buf) {
buf->b_p_tw_nopaste = buf->b_p_tw;
buf->b_p_wm_nopaste = buf->b_p_wm;
@@ -6566,12 +6699,10 @@ static void paste_option_changed(void)
p_wm = 0;
p_sts = 0;
p_ai = 0;
- }
- /*
- * Paste switched from on to off: Restore saved values.
- */
- else if (old_p_paste) {
- /* restore options for each buffer */
+ } else if (old_p_paste) {
+ // Paste switched from on to off: Restore saved values.
+
+ // restore options for each buffer
FOR_ALL_BUFFERS(buf) {
buf->b_p_tw = buf->b_p_tw_nopaste;
buf->b_p_wm = buf->b_p_wm_nopaste;
@@ -6580,7 +6711,7 @@ static void paste_option_changed(void)
buf->b_p_et = buf->b_p_et_nopaste;
}
- /* restore global options */
+ // restore global options
p_sm = save_sm;
p_sta = save_sta;
if (p_ru != save_ru) {
@@ -6659,12 +6790,15 @@ static void fill_breakat_flags(void)
char_u *p;
int i;
- for (i = 0; i < 256; i++)
- breakat_flags[i] = FALSE;
+ for (i = 0; i < 256; i++) {
+ breakat_flags[i] = false;
+ }
- if (p_breakat != NULL)
- for (p = p_breakat; *p; p++)
- breakat_flags[*p] = TRUE;
+ if (p_breakat != NULL) {
+ for (p = p_breakat; *p; p++) {
+ breakat_flags[*p] = true;
+ }
+ }
}
/*
@@ -6676,7 +6810,7 @@ static void fill_breakat_flags(void)
static int check_opt_strings(
char_u *val,
char **values,
- int list /* when TRUE: accept a list of values */
+ int list // when true: accept a list of values
)
{
return opt_strings_flags(val, values, NULL, list);
@@ -6690,18 +6824,19 @@ static int check_opt_strings(
* Empty is always OK.
*/
static int opt_strings_flags(
- char_u *val, /* new value */
- char **values, /* array of valid string values */
+ char_u *val, // new value
+ char **values, // array of valid string values
unsigned *flagp,
- bool list /* when TRUE: accept a list of values */
+ bool list // when true: accept a list of values
)
{
unsigned int new_flags = 0;
while (*val) {
- for (unsigned int i = 0;; ++i) {
- if (values[i] == NULL) /* val not found in values[] */
+ for (unsigned int i = 0;; i++) {
+ if (values[i] == NULL) { // val not found in values[]
return FAIL;
+ }
size_t len = STRLEN(values[i]);
if (STRNCMP(values[i], val, len) == 0
@@ -6709,12 +6844,13 @@ static int opt_strings_flags(
val += len + (val[len] == ',');
assert(i < sizeof(1U) * 8);
new_flags |= (1U << i);
- break; /* check next item in val list */
+ break; // check next item in val list
}
}
}
- if (flagp != NULL)
+ if (flagp != NULL) {
*flagp = new_flags;
+ }
return OK;
}
@@ -6729,55 +6865,60 @@ static int check_opt_wim(void)
int i;
int idx = 0;
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < 4; i++) {
new_wim_flags[i] = 0;
+ }
- for (p = p_wim; *p; ++p) {
- for (i = 0; ASCII_ISALPHA(p[i]); ++i)
- ;
- if (p[i] != NUL && p[i] != ',' && p[i] != ':')
+ for (p = p_wim; *p; p++) {
+ for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
+ if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
return FAIL;
- if (i == 7 && STRNCMP(p, "longest", 7) == 0)
+ }
+ if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
new_wim_flags[idx] |= WIM_LONGEST;
- else if (i == 4 && STRNCMP(p, "full", 4) == 0)
+ } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
new_wim_flags[idx] |= WIM_FULL;
- else if (i == 4 && STRNCMP(p, "list", 4) == 0)
+ } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
new_wim_flags[idx] |= WIM_LIST;
- else
+ } else {
return FAIL;
+ }
p += i;
- if (*p == NUL)
+ if (*p == NUL) {
break;
+ }
if (*p == ',') {
- if (idx == 3)
+ if (idx == 3) {
return FAIL;
- ++idx;
+ }
+ idx++;
}
}
- /* fill remaining entries with last flag */
+ // fill remaining entries with last flag
while (idx < 3) {
new_wim_flags[idx + 1] = new_wim_flags[idx];
- ++idx;
+ idx++;
}
- /* only when there are no errors, wim_flags[] is changed */
- for (i = 0; i < 4; ++i)
+ // only when there are no errors, wim_flags[] is changed
+ for (i = 0; i < 4; i++) {
wim_flags[i] = new_wim_flags[i];
+ }
return OK;
}
/*
* Check if backspacing over something is allowed.
- * The parameter what is one of the following: whatBS_INDENT, BS_EOL
+ * The parameter what is one of the following: whatBS_INDENT, BS_EOL
* or BS_START
*/
bool can_bs(int what)
{
switch (*p_bs) {
- case '2': return TRUE;
+ case '2': return true;
case '1': return what != BS_START;
- case '0': return FALSE;
+ case '0': return false;
}
return vim_strchr(p_bs, what) != NULL;
}
@@ -6792,7 +6933,7 @@ void save_file_ff(buf_T *buf)
buf->b_start_eol = buf->b_p_eol;
buf->b_start_bomb = buf->b_p_bomb;
- /* Only use free/alloc when necessary, they take time. */
+ // Only use free/alloc when necessary, they take time.
if (buf->b_start_fenc == NULL
|| STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
xfree(buf->b_start_fenc);
@@ -6800,33 +6941,37 @@ void save_file_ff(buf_T *buf)
}
}
-/*
- * Return TRUE if 'fileformat' and/or 'fileencoding' has a different value
- * from when editing started (save_file_ff() called).
- * Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
- * changed and 'binary' is not set.
- * Also when 'endofline' was changed and 'fixeol' is not set.
- * When "ignore_empty" is true don't consider a new, empty buffer to be
- * changed.
- */
+/// Return true if 'fileformat' and/or 'fileencoding' has a different value
+/// from when editing started (save_file_ff() called).
+/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
+/// changed and 'binary' is not set.
+/// Also when 'endofline' was changed and 'fixeol' is not set.
+/// When "ignore_empty" is true don't consider a new, empty buffer to be
+/// changed.
bool file_ff_differs(buf_T *buf, bool ignore_empty)
{
- /* In a buffer that was never loaded the options are not valid. */
- if (buf->b_flags & BF_NEVERLOADED)
- return FALSE;
+ // In a buffer that was never loaded the options are not valid.
+ if (buf->b_flags & BF_NEVERLOADED) {
+ return false;
+ }
if (ignore_empty
&& (buf->b_flags & BF_NEW)
&& buf->b_ml.ml_line_count == 1
- && *ml_get_buf(buf, (linenr_T)1, FALSE) == NUL)
- return FALSE;
- if (buf->b_start_ffc != *buf->b_p_ff)
+ && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
+ return false;
+ }
+ if (buf->b_start_ffc != *buf->b_p_ff) {
+ return true;
+ }
+ if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
return true;
- if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol)
+ }
+ if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
return true;
- if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb)
- return TRUE;
- if (buf->b_start_fenc == NULL)
+ }
+ if (buf->b_start_fenc == NULL) {
return *buf->b_p_fenc != NUL;
+ }
return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
}
@@ -6835,7 +6980,7 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
*/
int check_ff_value(char_u *p)
{
- return check_opt_strings(p, p_ff_values, FALSE);
+ return check_opt_strings(p, p_ff_values, false);
}
/*
@@ -6862,7 +7007,7 @@ int get_sts_value(void)
* Check matchpairs option for "*initc".
* If there is a match set "*initc" to the matching character and "*findc" to
* the opposite character. Set "*backwards" to the direction.
- * When "switchit" is TRUE swap the direction.
+ * When "switchit" is true swap the direction.
*/
void find_mps_values(int *initc, int *findc, int *backwards, int switchit)
{
@@ -6927,10 +7072,12 @@ static bool briopt_check(win_T *wp)
p += 3;
bri_sbr = true;
}
- if (*p != ',' && *p != NUL)
+ if (*p != ',' && *p != NUL) {
return false;
- if (*p == ',')
- ++p;
+ }
+ if (*p == ',') {
+ p++;
+ }
}
wp->w_p_brishift = bri_shift;
@@ -7086,22 +7233,40 @@ size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen,
return len;
}
-/// Return TRUE when 'shell' has "csh" in the tail.
+/// Return true when 'shell' has "csh" in the tail.
int csh_like_shell(void)
{
return strstr((char *)path_tail(p_sh), "csh") != NULL;
}
-/// Return true when window "wp" has a column to draw signs in.
-bool signcolumn_on(win_T *wp)
+/// Return the number of requested sign columns, based on current
+/// buffer signs and on user configuration.
+int win_signcol_count(win_T *wp)
{
- if (*wp->w_p_scl == 'n') {
- return false;
- }
- if (*wp->w_p_scl == 'y') {
- return true;
- }
- return wp->w_buffer->b_signlist != NULL;
+ int maximum = 1, needed_signcols;
+ const char *scl = (const char *)wp->w_p_scl;
+
+ if (*scl == 'n') {
+ return 0;
+ }
+ needed_signcols = buf_signcols(wp->w_buffer);
+
+ // yes or yes
+ if (!strncmp(scl, "yes:", 4)) {
+ // Fixed amount of columns
+ return scl[4] - '0';
+ }
+ if (*scl == 'y') {
+ return 1;
+ }
+
+ // auto or auto:<NUM>
+ if (!strncmp(scl, "auto:", 5)) {
+ // Variable depending on a configuration
+ maximum = scl[5] - '0';
+ }
+
+ return MIN(maximum, needed_signcols);
}
/// Get window or buffer local options
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 6dc5e418fc..3e116095fd 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -74,13 +74,14 @@
#define FO_MBYTE_JOIN 'M' /* no space before/after multi-byte char */
#define FO_MBYTE_JOIN2 'B' /* no space between multi-byte chars */
#define FO_ONE_LETTER '1'
-#define FO_WHITE_PAR 'w' /* trailing white space continues paragr. */
-#define FO_AUTO 'a' /* automatic formatting */
-#define FO_REMOVE_COMS 'j' /* remove comment leaders when joining lines */
+#define FO_WHITE_PAR 'w' // trailing white space continues paragr.
+#define FO_AUTO 'a' // automatic formatting
+#define FO_REMOVE_COMS 'j' // remove comment leaders when joining lines
+#define FO_PERIOD_ABBR 'p' // don't break a single space after a period
#define DFLT_FO_VI "vt"
#define DFLT_FO_VIM "tcqj"
-#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */
+#define FO_ALL "tcroq2vlb1mMBn,awjp" // for do_set()
// characters for the p_cpo option:
#define CPO_ALTREAD 'a' // ":read" sets alternate file name
@@ -178,14 +179,6 @@ enum {
SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
0, \
})
-/// All possible flags for 'shm'.
-#define SHM_ALL ((char_u[]) { \
- SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
- SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \
- SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \
- SHM_RECORDING, SHM_FILEINFO, \
- 0, \
-})
/* characters for p_go: */
#define GO_ASEL 'a' /* autoselect */
@@ -637,6 +630,7 @@ EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
EXTERN char_u *p_shada; ///< 'shada'
+EXTERN char *p_shadafile; ///< 'shadafile'
EXTERN char_u *p_vdir; ///< 'viewdir'
EXTERN char_u *p_vop; ///< 'viewoptions'
EXTERN unsigned vop_flags; ///< uses SSOP_ flags
@@ -658,6 +652,12 @@ extern char_u *p_vfile; /* 'verbosefile' */
#endif
EXTERN int p_warn; // 'warn'
EXTERN char_u *p_wop; // 'wildoptions'
+EXTERN unsigned wop_flags;
+# ifdef IN_OPTION_C
+static char *(p_wop_values[]) = { "tagfile", "pum", NULL };
+#endif
+#define WOP_TAGFILE 0x01
+#define WOP_PUM 0x02
EXTERN long p_window; // 'window'
EXTERN char_u *p_wak; // 'winaltkeys'
EXTERN char_u *p_wig; // 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 2398f9d61c..affddd8084 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -44,6 +44,8 @@ local N_=function(s)
return 'N_(' .. cstr(s) .. ')'
end
end
+-- used for 'cinkeys' and 'indentkeys'
+local indentkeys_default = '0{,0},0),0],:,0#,!^F,o,O,e';
return {
cstr=cstr,
options={
@@ -179,6 +181,7 @@ return {
{
full_name='backupskip', abbreviation='bsk',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_bsk',
defaults={if_true={vi=""}}
@@ -187,6 +190,7 @@ return {
full_name='belloff', abbreviation='bo',
deny_duplicates=true,
type='string', list='comma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_bo',
defaults={if_true={vi="all"}}
@@ -319,7 +323,7 @@ return {
vi_def=true,
alloced=true,
varname='p_cink',
- defaults={if_true={vi="0{,0},0),:,0#,!^F,o,O,e"}}
+ defaults={if_true={vi=indentkeys_default}}
},
{
full_name='cinoptions', abbreviation='cino',
@@ -754,6 +758,7 @@ return {
{
full_name='fileencodings', abbreviation='fencs',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_fencs',
defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}}
@@ -1014,6 +1019,7 @@ return {
{
full_name='guifontset', abbreviation='gfs',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_guifontset',
redraw={'ui_option'},
@@ -1068,6 +1074,7 @@ return {
{
full_name='helplang', abbreviation='hlg',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_hlg',
defaults={if_true={vi=""}}
@@ -1218,7 +1225,7 @@ return {
vi_def=true,
alloced=true,
varname='p_indk',
- defaults={if_true={vi="0{,0},:,0#,!^F,o,O,e"}}
+ defaults={if_true={vi=indentkeys_default}}
},
{
full_name='infercase', abbreviation='inf',
@@ -2008,6 +2015,15 @@ return {
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{
+ full_name='shadafile', abbreviation='sdf',
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
+ vi_def=true,
+ secure=true,
+ varname='p_shadafile',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='shell', abbreviation='sh',
type='string', scope={'global'},
secure=true,
@@ -2238,6 +2254,7 @@ return {
{
full_name='spellfile', abbreviation='spf',
type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
secure=true,
vi_def=true,
alloced=true,
@@ -2248,6 +2265,7 @@ return {
{
full_name='spelllang', abbreviation='spl',
type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
vi_def=true,
alloced=true,
expand=true,
@@ -2258,6 +2276,7 @@ return {
{
full_name='spellsuggest', abbreviation='sps',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
secure=true,
vi_def=true,
expand=true,
@@ -2623,6 +2642,15 @@ return {
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{
+ full_name='viminfofile', abbreviation='vif',
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
+ vi_def=true,
+ secure=true,
+ varname='p_shadafile',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='virtualedit', abbreviation='ve',
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
@@ -2699,7 +2727,8 @@ return {
},
{
full_name='wildoptions', abbreviation='wop',
- type='string', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_wop',
defaults={if_true={vi=""}}
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index e7bfbc8240..7d1021962c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -151,25 +151,36 @@ int os_unsetenv(const char *name)
char *os_getenvname_at_index(size_t index)
{
#ifdef _WIN32
- // Check if index is inside the environ array and is not the last element.
- for (size_t i = 0; i <= index; i++) {
- if (_wenviron[i] == NULL) {
- return NULL;
- }
- }
- wchar_t *utf16_str = _wenviron[index];
- char *utf8_str;
- int conversion_result = utf16_to_utf8(utf16_str, &utf8_str);
- if (conversion_result != 0) {
- EMSG2("utf16_to_utf8 failed: %d", conversion_result);
+ wchar_t *env = GetEnvironmentStringsW();
+ if (!env) {
return NULL;
}
- size_t namesize = 0;
- while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) {
- namesize++;
+ char *name = NULL;
+ size_t current_index = 0;
+ // GetEnvironmentStringsW() result has this format:
+ // var1=value1\0var2=value2\0...varN=valueN\0\0
+ for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) {
+ if (index == current_index) {
+ char *utf8_str;
+ int conversion_result = utf16_to_utf8(it, &utf8_str);
+ if (conversion_result != 0) {
+ EMSG2("utf16_to_utf8 failed: %d", conversion_result);
+ break;
+ }
+ size_t namesize = 0;
+ while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) {
+ namesize++;
+ }
+ name = (char *)vim_strnsave((char_u *)utf8_str, namesize);
+ xfree(utf8_str);
+ break;
+ }
+ if (*it == L'\0') {
+ current_index++;
+ }
}
- char *name = (char *)vim_strnsave((char_u *)utf8_str, namesize);
- xfree(utf8_str);
+
+ FreeEnvironmentStringsW(env);
return name;
#else
# if defined(HAVE__NSGETENVIRON)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 9b9b4581f4..8c7dfcdee7 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -226,13 +226,13 @@ int os_exepath(char *buffer, size_t *size)
return uv_exepath(buffer, size);
}
-/// Checks if the given path represents an executable file.
+/// Checks if the file `name` is executable.
///
-/// @param[in] name Name of the executable.
-/// @param[out] abspath Path of the executable, if found and not `NULL`.
-/// @param[in] use_path If 'false', only check if "name" is executable
+/// @param[in] name Filename to check.
+/// @param[out] abspath Returns resolved executable path, if not NULL.
+/// @param[in] use_path Also search $PATH.
///
-/// @return `true` if `name` is executable and
+/// @return true if `name` is executable and
/// - can be found in $PATH,
/// - is relative to current dir or
/// - is absolute.
@@ -242,40 +242,36 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
bool no_path = !use_path || path_is_absolute(name);
-#ifndef WIN32
// If the filename is "qualified" (relative or absolute) do not check $PATH.
+#ifdef WIN32
+ no_path |= (name[0] == '.'
+ && ((name[1] == '/' || name[1] == '\\')
+ || (name[1] == '.' && (name[2] == '/' || name[2] == '\\'))));
+#else
no_path |= (name[0] == '.'
&& (name[1] == '/' || (name[1] == '.' && name[2] == '/')));
#endif
if (no_path) {
#ifdef WIN32
- const char *pathext = os_getenv("PATHEXT");
- if (!pathext) {
- pathext = ".com;.exe;.bat;.cmd";
- }
- bool ok = is_executable((char *)name) || is_executable_ext((char *)name,
- pathext);
+ if (is_executable_ext((char *)name, abspath)) {
#else
// Must have path separator, cannot execute files in the current directory.
- const bool ok = ((const char_u *)gettail_dir((const char *)name) != name
- && is_executable((char *)name));
+ if ((const char_u *)gettail_dir((const char *)name) != name
+ && is_executable((char *)name, abspath)) {
#endif
- if (ok) {
- if (abspath != NULL) {
- *abspath = save_abs_path(name);
- }
return true;
+ } else {
+ return false;
}
- return false;
}
return is_executable_in_path(name, abspath);
}
/// Returns true if `name` is an executable file.
-static bool is_executable(const char *name)
- FUNC_ATTR_NONNULL_ALL
+static bool is_executable(const char *name, char_u **abspath)
+ FUNC_ATTR_NONNULL_ARG(1)
{
int32_t mode = os_getperm((const char *)name);
@@ -286,40 +282,58 @@ static bool is_executable(const char *name)
#ifdef WIN32
// Windows does not have exec bit; just check if the file exists and is not
// a directory.
- return (S_ISREG(mode));
+ const bool ok = S_ISREG(mode);
#else
int r = -1;
if (S_ISREG(mode)) {
RUN_UV_FS_FUNC(r, uv_fs_access, name, X_OK, NULL);
}
- return (r == 0);
+ const bool ok = (r == 0);
#endif
+ if (ok && abspath != NULL) {
+ *abspath = save_abs_path((char_u *)name);
+ }
+ return ok;
}
#ifdef WIN32
-/// Appends file extensions from `pathext` to `name` and returns true if any
-/// such combination is executable.
-static bool is_executable_ext(char *name, const char *pathext)
- FUNC_ATTR_NONNULL_ALL
+/// Checks if file `name` is executable under any of these conditions:
+/// - extension is in $PATHEXT and `name` is executable
+/// - result of any $PATHEXT extension appended to `name` is executable
+static bool is_executable_ext(char *name, char_u **abspath)
+ FUNC_ATTR_NONNULL_ARG(1)
{
+ const bool is_unix_shell = strstr((char *)path_tail(p_sh), "sh") != NULL;
+ char *nameext = strrchr(name, '.');
+ size_t nameext_len = nameext ? strlen(nameext) : 0;
xstrlcpy(os_buf, name, sizeof(os_buf));
char *buf_end = xstrchrnul(os_buf, '\0');
+ const char *pathext = os_getenv("PATHEXT");
+ if (!pathext) {
+ pathext = ".com;.exe;.bat;.cmd";
+ }
for (const char *ext = pathext; *ext; ext++) {
- // Skip the extension if there is no suffix after a '.'.
+ // If $PATHEXT itself contains dot:
if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) {
+ if (is_executable(name, abspath)) {
+ return true;
+ }
+ // Skip it.
ext++;
continue;
}
const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR);
- STRLCPY(buf_end, ext, ext_end - ext + 1);
-
- if (is_executable(os_buf)) {
- return true;
- }
-
- if (*ext_end != ENV_SEPCHAR) {
- break;
+ size_t ext_len = (size_t)(ext_end - ext);
+ if (ext_len != 0) {
+ STRLCPY(buf_end, ext, ext_len + 1);
+ bool in_pathext = nameext_len == ext_len
+ && 0 == mb_strnicmp((char_u *)nameext, (char_u *)ext, ext_len);
+
+ if (((in_pathext || is_unix_shell) && is_executable(name, abspath))
+ || is_executable(os_buf, abspath)) {
+ return true;
+ }
}
ext = ext_end;
}
@@ -327,10 +341,10 @@ static bool is_executable_ext(char *name, const char *pathext)
}
#endif
-/// Checks if a file is inside the `$PATH` and is executable.
+/// Checks if a file is in `$PATH` and is executable.
///
-/// @param[in] name The name of the executable.
-/// @param[out] abspath Path of the executable, if found and not `NULL`.
+/// @param[in] name Filename to check.
+/// @param[out] abspath Returns resolved executable path, if not NULL.
///
/// @return `true` if `name` is an executable inside `$PATH`.
static bool is_executable_in_path(const char_u *name, char_u **abspath)
@@ -351,15 +365,6 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
#endif
size_t buf_len = STRLEN(name) + strlen(path) + 2;
-
-#ifdef WIN32
- const char *pathext = os_getenv("PATHEXT");
- if (!pathext) {
- pathext = ".com;.exe;.bat;.cmd";
- }
- buf_len += strlen(pathext);
-#endif
-
char *buf = xmalloc(buf_len);
// Walk through all entries in $PATH to check if "name" exists there and
@@ -374,15 +379,10 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
append_path(buf, (char *)name, buf_len);
#ifdef WIN32
- bool ok = is_executable(buf) || is_executable_ext(buf, pathext);
+ if (is_executable_ext(buf, abspath)) {
#else
- bool ok = is_executable(buf);
+ if (is_executable(buf, abspath)) {
#endif
- if (ok) {
- if (abspath != NULL) { // Caller asked for a copy of the path.
- *abspath = save_abs_path((char_u *)buf);
- }
-
rv = true;
goto end;
}
@@ -643,8 +643,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size,
return (ptrdiff_t)written_bytes;
}
-/// Copies a file from path to new_path. Currently this passes
-/// the arguments through to uv_fs_copyfile.
+/// Copies a file from `path` to `new_path`.
+///
+/// @see http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_copyfile
///
/// @param path Path of file to be copied
/// @param path_new Path of new file
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index a67e7487eb..a1020be215 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -265,3 +265,9 @@ Dictionary os_proc_info(int pid)
return pinfo;
}
#endif
+
+/// Return true if process `pid` is running.
+bool os_proc_running(int pid)
+{
+ return uv_kill(pid, 0) == 0;
+}
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index f6503dfdb0..91ee45d3a9 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -80,14 +80,14 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
return ret;
}
-/// Return nvim-specific XDG directory subpath
+/// Return Nvim-specific XDG directory subpath.
///
-/// @param[in] idx XDG directory to use.
+/// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing
+/// configuration and data files in the same path. #4403
///
-/// @return [allocated] `{xdg_directory}/nvim`
+/// @param[in] idx XDG directory to use.
///
-/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to
-/// avoid storing configuration and data files in the same path.
+/// @return [allocated] "{xdg_directory}/nvim"
char *get_xdg_home(const XDGVarType idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
diff --git a/src/nvim/os/stdpaths_defs.h b/src/nvim/os/stdpaths_defs.h
index 1433e0bc62..44c30df373 100644
--- a/src/nvim/os/stdpaths_defs.h
+++ b/src/nvim/os/stdpaths_defs.h
@@ -3,6 +3,7 @@
/// List of possible XDG variables
typedef enum {
+ kXDGNone = -1,
kXDGConfigHome, ///< XDG_CONFIG_HOME
kXDGDataHome, ///< XDG_DATA_HOME
kXDGCacheHome, ///< XDG_CACHE_HOME
diff --git a/src/nvim/path.c b/src/nvim/path.c
index a706e32773..67b88a861a 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1346,6 +1346,15 @@ void slash_adjust(char_u *p)
if (path_with_url((const char *)p)) {
return;
}
+
+ if (*p == '`') {
+ // don't replace backslash in backtick quoted strings
+ const size_t len = STRLEN(p);
+ if (len > 2 && *(p + len - 1) == '`') {
+ return;
+ }
+ }
+
while (*p) {
if (*p == (char_u)psepcN) {
*p = (char_u)psepc;
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 499ee11cad..58a0008e04 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -12,6 +12,7 @@
#include "nvim/vim.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/eval/typval.h"
#include "nvim/popupmnu.h"
#include "nvim/charset.h"
#include "nvim/ex_cmds.h"
@@ -65,7 +66,9 @@ static bool pum_invalid = false; // the screen was just cleared
/// @param array_changed if true, array contains different items since last call
/// if false, a new item is selected, but the array
/// is the same
-void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
+/// @param cmd_startcol only for cmdline mode: column of completed match
+void pum_display(pumitem_T *array, int size, int selected, bool array_changed,
+ int cmd_startcol)
{
int w;
int def_width;
@@ -83,7 +86,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
if (!pum_is_visible) {
// To keep the code simple, we only allow changing the
// draw mode when the popup menu is not being displayed
- pum_external = ui_has(kUIPopupmenu);
+ pum_external = ui_has(kUIPopupmenu)
+ || (State == CMDLINE && ui_has(kUIWildmenu));
}
do {
@@ -95,19 +99,26 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
above_row = 0;
below_row = cmdline_row;
- // anchor position: the start of the completed word
- row = curwin->w_wrow;
- if (curwin->w_p_rl) {
- col = curwin->w_width - curwin->w_wcol - 1;
+ // wildoptions=pum
+ if (State == CMDLINE) {
+ row = ui_has(kUICmdline) ? 0 : cmdline_row;
+ col = cmd_startcol;
+ pum_anchor_grid = ui_has(kUICmdline) ? -1 : DEFAULT_GRID_HANDLE;
} else {
- col = curwin->w_wcol;
- }
+ // anchor position: the start of the completed word
+ row = curwin->w_wrow;
+ if (curwin->w_p_rl) {
+ col = curwin->w_width - curwin->w_wcol - 1;
+ } else {
+ col = curwin->w_wcol;
+ }
- pum_anchor_grid = (int)curwin->w_grid.handle;
- if (!ui_has(kUIMultigrid)) {
- pum_anchor_grid = (int)default_grid.handle;
- row += curwin->w_winrow;
- col += curwin->w_wincol;
+ pum_anchor_grid = (int)curwin->w_grid.handle;
+ if (!ui_has(kUIMultigrid)) {
+ pum_anchor_grid = (int)default_grid.handle;
+ row += curwin->w_winrow;
+ col += curwin->w_wincol;
+ }
}
if (pum_external) {
@@ -661,7 +672,7 @@ static int pum_set_selected(int n, int repeat)
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
while (!BUFEMPTY()) {
- ml_delete((linenr_T)1, FALSE);
+ ml_delete((linenr_T)1, false);
}
} else {
// Don't want to sync undo in the current buffer.
@@ -686,11 +697,11 @@ static int pum_set_selected(int n, int repeat)
for (p = pum_array[pum_selected].pum_info; *p != NUL;) {
e = vim_strchr(p, '\n');
if (e == NULL) {
- ml_append(lnum++, p, 0, FALSE);
+ ml_append(lnum++, p, 0, false);
break;
} else {
*e = NUL;
- ml_append(lnum++, p, (int)(e - p + 1), FALSE);
+ ml_append(lnum++, p, (int)(e - p + 1), false);
*e = '\n';
p = e + 1;
}
@@ -841,3 +852,17 @@ int pum_get_height(void)
{
return pum_height;
}
+
+void pum_set_boundings(dict_T *dict)
+{
+ if (!pum_visible()) {
+ return;
+ }
+ tv_dict_add_nr(dict, S_LEN("height"), pum_height);
+ tv_dict_add_nr(dict, S_LEN("width"), pum_width);
+ tv_dict_add_nr(dict, S_LEN("row"), pum_row);
+ tv_dict_add_nr(dict, S_LEN("col"), pum_col);
+ tv_dict_add_nr(dict, S_LEN("size"), pum_size);
+ tv_dict_add_special(dict, S_LEN("scrollbar"),
+ pum_scrollbar ? kSpecialVarTrue : kSpecialVarFalse);
+}
diff --git a/src/nvim/popupmnu.h b/src/nvim/popupmnu.h
index 42e6ef5653..7956e50a93 100644
--- a/src/nvim/popupmnu.h
+++ b/src/nvim/popupmnu.h
@@ -1,6 +1,7 @@
#ifndef NVIM_POPUPMNU_H
#define NVIM_POPUPMNU_H
+#include "nvim/vim.h"
#include "nvim/macros.h"
#include "nvim/grid_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index ee1cf2182f..29b3d19f52 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -57,19 +57,20 @@ struct dir_stack_T {
*/
typedef struct qfline_S qfline_T;
struct qfline_S {
- qfline_T *qf_next; /* pointer to next error in the list */
- qfline_T *qf_prev; /* pointer to previous error in the list */
- linenr_T qf_lnum; /* line number where the error occurred */
- int qf_fnum; /* file number for the line */
- int qf_col; /* column where the error occurred */
- int qf_nr; /* error number */
- char_u *qf_pattern; /* search pattern for the error */
- char_u *qf_text; /* description of the error */
- char_u qf_viscol; /* set to TRUE if qf_col is screen column */
- char_u qf_cleared; /* set to TRUE if line has been deleted */
- char_u qf_type; /* type of the error (mostly 'E'); 1 for
- :helpgrep */
- char_u qf_valid; /* valid error message detected */
+ qfline_T *qf_next; ///< pointer to next error in the list
+ qfline_T *qf_prev; ///< pointer to previous error in the list
+ linenr_T qf_lnum; ///< line number where the error occurred
+ int qf_fnum; ///< file number for the line
+ int qf_col; ///< column where the error occurred
+ int qf_nr; ///< error number
+ char_u *qf_module; ///< module name for this error
+ char_u *qf_pattern; ///< search pattern for the error
+ char_u *qf_text; ///< description of the error
+ char_u qf_viscol; ///< set to TRUE if qf_col is screen column
+ char_u qf_cleared; ///< set to TRUE if line has been deleted
+ char_u qf_type; ///< type of the error (mostly 'E'); 1 for
+ // :helpgrep
+ char_u qf_valid; ///< valid error message detected
};
/*
@@ -101,6 +102,7 @@ typedef struct qf_list_S {
bool qf_multiline;
bool qf_multiignore;
bool qf_multiscan;
+ long qf_changedtick;
} qf_list_T;
/// Quickfix/Location list stack definition
@@ -121,7 +123,7 @@ struct qf_info_S {
static qf_info_T ql_info; // global quickfix list
static unsigned last_qf_id = 0; // Last Used quickfix list id
-#define FMT_PATTERNS 10 /* maximum number of % recognized */
+#define FMT_PATTERNS 11 // maximum number of % recognized
/*
* Structure used to hold the info of one part of 'errorformat'
@@ -155,7 +157,8 @@ enum {
QF_OK = 1,
QF_END_OF_INPUT = 2,
QF_NOMEM = 3,
- QF_IGNORE_LINE = 4
+ QF_IGNORE_LINE = 4,
+ QF_MULTISCAN = 5,
};
typedef struct {
@@ -176,6 +179,7 @@ typedef struct {
typedef struct {
char_u *namebuf;
+ char_u *module;
char_u *errmsg;
size_t errmsglen;
long lnum;
@@ -205,6 +209,8 @@ typedef struct {
static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = { NULL, 0, 0 };
+static char *e_loc_list_changed = N_("E926: Current location list was changed");
+
/// Read the errorfile "efile" into memory, line by line, building the error
/// list. Set the error list's title to qf_title.
///
@@ -247,7 +253,8 @@ static struct fmtpattern
{ 'r', ".*" },
{ 'p', "[- .]*" }, // NOLINT(whitespace/tab)
{ 'v', "\\d\\+" },
- { 's', ".\\+" }
+ { 's', ".\\+" },
+ { 'o', ".\\+" }
};
// Converts a 'errorformat' string to regular expression pattern
@@ -423,7 +430,11 @@ static efm_T * parse_efm_option(char_u *efm)
for (int round = FMT_PATTERNS - 1; round >= 0; ) {
i += STRLEN(fmt_pat[round--].pattern);
}
+#ifdef BACKSLASH_IN_FILENAME
+ i += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
+#else
i += 2; // "%f" can become two chars longer
+#endif
char_u *fmtstr = xmalloc(i);
while (efm[0] != NUL) {
@@ -717,20 +728,17 @@ static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf,
size_t linelen, efm_T *fmt_first, qffields_T *fields)
{
efm_T *fmt_ptr;
- size_t len;
- int i;
int idx = 0;
char_u *tail = NULL;
- regmatch_T regmatch;
qf_list_T *qfl = &qi->qf_lists[qf_idx];
+ int status;
- // Always ignore case when looking for a matching error.
- regmatch.rm_ic = true;
-
+restofline:
// If there was no %> item start at the first pattern
if (fmt_start == NULL) {
fmt_ptr = fmt_first;
} else {
+ // Otherwise start from the last used pattern.
fmt_ptr = fmt_start;
fmt_start = NULL;
}
@@ -738,143 +746,15 @@ static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf,
// Try to match each part of 'errorformat' until we find a complete
// match or no match.
fields->valid = true;
-restofline:
for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) {
idx = fmt_ptr->prefix;
- if (qfl->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) {
- continue;
- }
- fields->namebuf[0] = NUL;
- fields->pattern[0] = NUL;
- if (!qfl->qf_multiscan) {
- fields->errmsg[0] = NUL;
+ status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields,
+ qfl->qf_multiline, qfl->qf_multiscan,
+ &tail);
+ if (status == QF_NOMEM) {
+ return status;
}
- fields->lnum = 0;
- fields->col = 0;
- fields->use_viscol = false;
- fields->enr = -1;
- fields->type = 0;
- tail = NULL;
-
- regmatch.regprog = fmt_ptr->prog;
- int r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
- fmt_ptr->prog = regmatch.regprog;
- if (r) {
- if ((idx == 'C' || idx == 'Z') && !qfl->qf_multiline) {
- continue;
- }
- if (vim_strchr((char_u *)"EWI", idx) != NULL) {
- fields->type = (char_u)idx;
- } else {
- fields->type = 0;
- }
- // Extract error message data from matched line.
- // We check for an actual submatch, because "\[" and "\]" in
- // the 'errorformat' may cause the wrong submatch to be used.
- if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- // Expand ~/file and $HOME/file to full path.
- char_u c = *regmatch.endp[i];
- *regmatch.endp[i] = NUL;
- expand_env(regmatch.startp[i], fields->namebuf, CMDBUFFSIZE);
- *regmatch.endp[i] = c;
-
- if (vim_strchr((char_u *)"OPQ", idx) != NULL
- && !os_path_exists(fields->namebuf)) {
- continue;
- }
- }
- if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->enr = (int)atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->lnum = atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->col = (int)atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->type = *regmatch.startp[i];
- }
- if (fmt_ptr->flags == '+' && !qfl->qf_multiscan) { // %+
- if (linelen >= fields->errmsglen) {
- // linelen + null terminator
- fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
- fields->errmsglen = linelen + 1;
- }
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
- } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
- if (len >= fields->errmsglen) {
- // len + null terminator
- fields->errmsg = xrealloc(fields->errmsg, len + 1);
- fields->errmsglen = len + 1;
- }
- STRLCPY(fields->errmsg, regmatch.startp[i], len + 1);
- }
- if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- tail = regmatch.startp[i];
- }
- if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p
- char_u *match_ptr;
-
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- fields->col = 0;
- for (match_ptr = regmatch.startp[i];
- match_ptr != regmatch.endp[i]; match_ptr++) {
- fields->col++;
- if (*match_ptr == TAB) {
- fields->col += 7;
- fields->col -= fields->col % 8;
- }
- }
- fields->col++;
- fields->use_viscol = true;
- }
- if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->col = (int)atol((char *)regmatch.startp[i]);
- fields->use_viscol = true;
- }
- if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
- if (len > CMDBUFFSIZE - 5) {
- len = CMDBUFFSIZE - 5;
- }
- STRCPY(fields->pattern, "^\\V");
- xstrlcat((char *)fields->pattern, (char *)regmatch.startp[i],
- CMDBUFFSIZE+1);
- fields->pattern[len + 3] = '\\';
- fields->pattern[len + 4] = '$';
- fields->pattern[len + 5] = NUL;
- }
+ if (status == QF_OK) {
break;
}
}
@@ -882,30 +762,16 @@ restofline:
if (fmt_ptr == NULL || idx == 'D' || idx == 'X') {
if (fmt_ptr != NULL) {
- if (idx == 'D') { // enter directory
- if (*fields->namebuf == NUL) {
- EMSG(_("E379: Missing or empty directory name"));
- return QF_FAIL;
- }
- qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack,
- false);
- if (qfl->qf_directory == NULL) {
- return QF_FAIL;
- }
- } else if (idx == 'X') { // leave directory
- qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack);
+ // 'D' and 'X' directory specifiers.
+ status = qf_parse_dir_pfx(idx, fields, qfl);
+ if (status != QF_OK) {
+ return status;
}
}
- fields->namebuf[0] = NUL; // no match found, remove file name
- fields->lnum = 0; // don't jump to this line
- fields->valid = false;
- if (linelen >= fields->errmsglen) {
- // linelen + null terminator
- fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
- fields->errmsglen = linelen + 1;
+ status = qf_parse_line_nomatch(linebuf, linelen, fields);
+ if (status != QF_OK) {
+ return status;
}
- // copy whole line to error message
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
if (fmt_ptr == NULL) {
qfl->qf_multiline = qfl->qf_multiignore = false;
}
@@ -918,63 +784,17 @@ restofline:
if (vim_strchr((char_u *)"AEWI", 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) { // continuation of multi-line msg
- if (!qfl->qf_multiignore) {
- qfline_T *qfprev = qfl->qf_last;
- if (qfprev == NULL) {
- return QF_FAIL;
- }
- if (*fields->errmsg) {
- size_t textlen = STRLEN(qfprev->qf_text);
- qfprev->qf_text = xrealloc(qfprev->qf_text,
- textlen + STRLEN(fields->errmsg) + 2);
- qfprev->qf_text[textlen] = '\n';
- STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg);
- }
- if (qfprev->qf_nr == -1) {
- qfprev->qf_nr = fields->enr;
- }
- if (vim_isprintc(fields->type) && !qfprev->qf_type) {
- qfprev->qf_type = fields->type; // only printable chars allowed
- }
- if (!qfprev->qf_lnum) {
- qfprev->qf_lnum = fields->lnum;
- }
- if (!qfprev->qf_col) {
- qfprev->qf_col = fields->col;
- }
- qfprev->qf_viscol = fields->use_viscol;
- if (!qfprev->qf_fnum) {
- qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory,
- *fields->namebuf || qfl->qf_directory
- ? fields->namebuf
- : qfl->qf_currfile && fields->valid
- ? qfl->qf_currfile : 0);
- }
- }
- if (idx == 'Z') {
- qfl->qf_multiline = qfl->qf_multiignore = false;
+ } else if (vim_strchr((char_u *)"CZ", idx) != NULL) {
+ // continuation of multi-line msg
+ status = qf_parse_multiline_pfx(qi, qf_idx, idx, qfl, fields);
+ if (status != QF_OK) {
+ return status;
}
-
- line_breakcheck();
- return QF_IGNORE_LINE;
} else if (vim_strchr((char_u *)"OPQ", idx) != NULL) {
// global file names
- fields->valid = false;
- if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
- if (*fields->namebuf && idx == 'P') {
- qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack,
- true);
- } else if (idx == 'Q') {
- qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
- }
- *fields->namebuf = NUL;
- if (tail && *tail) {
- STRMOVE(IObuff, skipwhite(tail));
- qfl->qf_multiscan = true;
- goto restofline;
- }
+ status = qf_parse_file_pfx(idx, fields, qfl, tail);
+ if (status == QF_MULTISCAN) {
+ goto restofline;
}
}
if (fmt_ptr->flags == '-') { // generally exclude this line
@@ -1034,6 +854,7 @@ qf_init_ext(
}
fields.namebuf = xmalloc(CMDBUFFSIZE + 1);
+ fields.module = xmalloc(CMDBUFFSIZE + 1);
fields.errmsglen = CMDBUFFSIZE + 1;
fields.errmsg = xmalloc(fields.errmsglen);
fields.pattern = xmalloc(CMDBUFFSIZE + 1);
@@ -1128,6 +949,7 @@ qf_init_ext(
(*fields.namebuf || qfl->qf_directory)
? fields.namebuf : ((qfl->qf_currfile && fields.valid)
? qfl->qf_currfile : (char_u *)NULL),
+ fields.module,
0,
fields.errmsg,
fields.lnum,
@@ -1172,6 +994,7 @@ qf_init_end:
fclose(state.fd);
}
xfree(fields.namebuf);
+ xfree(fields.module);
xfree(fields.errmsg);
xfree(fields.pattern);
xfree(state.growbuf);
@@ -1187,6 +1010,8 @@ qf_init_end:
return retval;
}
+/// Set the title of the specified quickfix list. Frees the previous title.
+/// Prepends ':' to the title.
static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title)
{
xfree(qi->qf_lists[qf_idx].qf_title);
@@ -1197,10 +1022,23 @@ static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title)
char_u *p = xmallocz(len);
qi->qf_lists[qf_idx].qf_title = p;
- snprintf((char *)p, len + 1, ":%s", (char *)title);
+ xstrlcpy((char *)p, (char *)title, len + 1);
}
}
+/// The title of a quickfix/location list is set, by default, to the command
+/// that created the quickfix list with the ":" prefix.
+/// Create a quickfix list title string by prepending ":" to a user command.
+/// Returns a pointer to a static buffer with the title.
+static char_u * qf_cmdtitle(char_u *cmd)
+{
+ static char_u qftitle_str[IOSIZE];
+
+ snprintf((char *)qftitle_str, IOSIZE, ":%s", (char *)cmd);
+
+ return qftitle_str;
+}
+
// Prepare for adding a new quickfix list. If the current list is in the
// middle of the stack, then all the following lists are freed and then
// the new list is added.
@@ -1230,9 +1068,301 @@ static void qf_new_list(qf_info_T *qi, char_u *qf_title)
qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
}
-/*
- * Free a location list
- */
+/// Parse the error format matches in 'regmatch' and set the values in 'fields'.
+/// fmt_ptr contains the 'efm' format specifiers/prefixes that have a match.
+/// Returns QF_OK if all the matches are successfully parsed. On failure,
+/// returns QF_FAIL or QF_NOMEM.
+static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
+ regmatch_T *regmatch, qffields_T *fields,
+ int qf_multiline, int qf_multiscan, char_u **tail)
+{
+ char_u idx = fmt_ptr->prefix;
+ int i;
+ size_t len;
+
+ if ((idx == 'C' || idx == 'Z') && !qf_multiline) {
+ return QF_FAIL;
+ }
+ if (vim_strchr((char_u *)"EWI", idx) != NULL) {
+ fields->type = idx;
+ } else {
+ fields->type = 0;
+ }
+
+ // Extract error message data from matched line.
+ // We check for an actual submatch, because "\[" and "\]" in
+ // the 'errorformat' may cause the wrong submatch to be used.
+ if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+
+ // Expand ~/file and $HOME/file to full path.
+ char_u c = *regmatch->endp[i];
+ *regmatch->endp[i] = NUL;
+ expand_env(regmatch->startp[i], fields->namebuf, CMDBUFFSIZE);
+ *regmatch->endp[i] = c;
+
+ if (vim_strchr((char_u *)"OPQ", idx) != NULL
+ && !os_path_exists(fields->namebuf)) {
+ return QF_FAIL;
+ }
+ }
+ if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->enr = (int)atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->lnum = atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = (int)atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->type = *regmatch->startp[i];
+ }
+ if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
+ if (linelen >= fields->errmsglen) {
+ // linelen + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
+ fields->errmsglen = linelen + 1;
+ }
+ STRLCPY(fields->errmsg, linebuf, linelen + 1);
+ } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len >= fields->errmsglen) {
+ // len + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, len + 1);
+ fields->errmsglen = len + 1;
+ }
+ STRLCPY(fields->errmsg, regmatch->startp[i], len + 1);
+ }
+ if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ *tail = regmatch->startp[i];
+ }
+ if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = 0;
+ char_u *match_ptr;
+ for (match_ptr = regmatch->startp[i]; match_ptr != regmatch->endp[i];
+ match_ptr++) {
+ fields->col++;
+ if (*match_ptr == TAB) {
+ fields->col += 7;
+ fields->col -= fields->col % 8;
+ }
+ }
+ fields->col++;
+ fields->use_viscol = true;
+ }
+ if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = (int)atol((char *)regmatch->startp[i]);
+ fields->use_viscol = true;
+ }
+ if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len > CMDBUFFSIZE - 5) {
+ len = CMDBUFFSIZE - 5;
+ }
+ STRCPY(fields->pattern, "^\\V");
+ STRNCAT(fields->pattern, regmatch->startp[i], len);
+ fields->pattern[len + 3] = '\\';
+ fields->pattern[len + 4] = '$';
+ fields->pattern[len + 5] = NUL;
+ }
+ if ((i = (int)fmt_ptr->addr[10]) > 0) { // %o
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len > CMDBUFFSIZE) {
+ len = CMDBUFFSIZE;
+ }
+ STRNCAT(fields->module, regmatch->startp[i], len);
+ }
+
+ return QF_OK;
+}
+
+/// Parse an error line in 'linebuf' using a single error format string in
+/// 'fmt_ptr->prog' and return the matching values in 'fields'.
+/// Returns QF_OK if the efm format matches completely and the fields are
+/// successfully copied. Otherwise returns QF_FAIL or QF_NOMEM.
+static int qf_parse_get_fields(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
+ qffields_T *fields, int qf_multiline,
+ int qf_multiscan, char_u **tail)
+{
+ regmatch_T regmatch;
+ int status = QF_FAIL;
+ int r;
+
+ if (qf_multiscan && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL) {
+ return QF_FAIL;
+ }
+
+ fields->namebuf[0] = NUL;
+ fields->module[0] = NUL;
+ fields->pattern[0] = NUL;
+ if (!qf_multiscan) {
+ fields->errmsg[0] = NUL;
+ }
+ fields->lnum = 0;
+ fields->col = 0;
+ fields->use_viscol = false;
+ fields->enr = -1;
+ fields->type = 0;
+ *tail = NULL;
+
+ // Always ignore case when looking for a matching error.
+ regmatch.rm_ic = true;
+ regmatch.regprog = fmt_ptr->prog;
+ r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
+ fmt_ptr->prog = regmatch.regprog;
+ if (r) {
+ status = qf_parse_match(linebuf, linelen, fmt_ptr, &regmatch, fields,
+ qf_multiline, qf_multiscan, tail);
+ }
+
+ return status;
+}
+
+/// Parse directory error format prefixes (%D and %X).
+/// Push and pop directories from the directory stack when scanning directory
+/// names.
+static int qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl)
+{
+ if (idx == 'D') { // enter directory
+ if (*fields->namebuf == NUL) {
+ EMSG(_("E379: Missing or empty directory name"));
+ return QF_FAIL;
+ }
+ qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack, false);
+ if (qfl->qf_directory == NULL) {
+ return QF_FAIL;
+ }
+ } else if (idx == 'X') { // leave directory
+ qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack);
+ }
+
+ return QF_OK;
+}
+
+/// Parse global file name error format prefixes (%O, %P and %Q).
+static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl,
+ char_u *tail)
+{
+ fields->valid = false;
+ if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
+ if (*fields->namebuf && idx == 'P') {
+ qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack,
+ true);
+ } else if (idx == 'Q') {
+ qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
+ }
+ *fields->namebuf = NUL;
+ if (tail && *tail) {
+ STRMOVE(IObuff, skipwhite(tail));
+ qfl->qf_multiscan = true;
+ return QF_MULTISCAN;
+ }
+ }
+
+ return QF_OK;
+}
+
+/// Parse a non-error line (a line which doesn't match any of the error
+/// format in 'efm').
+static int qf_parse_line_nomatch(char_u *linebuf, size_t linelen,
+ qffields_T *fields)
+{
+ fields->namebuf[0] = NUL; // no match found, remove file name
+ fields->lnum = 0; // don't jump to this line
+ fields->valid = false;
+ if (linelen >= fields->errmsglen) {
+ // linelen + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
+ fields->errmsglen = linelen + 1;
+ }
+ // copy whole line to error message
+ STRLCPY(fields->errmsg, linebuf, linelen + 1);
+
+ return QF_OK;
+}
+
+/// Parse multi-line error format prefixes (%C and %Z)
+static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx,
+ qf_list_T *qfl, qffields_T *fields)
+{
+ if (!qfl->qf_multiignore) {
+ qfline_T *qfprev = qfl->qf_last;
+
+ if (qfprev == NULL) {
+ return QF_FAIL;
+ }
+ if (*fields->errmsg && !qfl->qf_multiignore) {
+ size_t textlen = strlen((char *)qfprev->qf_text);
+ size_t errlen = strlen((char *)fields->errmsg);
+ qfprev->qf_text = xrealloc(qfprev->qf_text, textlen + errlen + 2);
+ qfprev->qf_text[textlen] = '\n';
+ STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg);
+ }
+ if (qfprev->qf_nr == -1) {
+ qfprev->qf_nr = fields->enr;
+ }
+ if (vim_isprintc(fields->type) && !qfprev->qf_type) {
+ // only printable chars allowed
+ qfprev->qf_type = fields->type;
+ }
+
+ if (!qfprev->qf_lnum) {
+ qfprev->qf_lnum = fields->lnum;
+ }
+ if (!qfprev->qf_col) {
+ qfprev->qf_col = fields->col;
+ }
+ qfprev->qf_viscol = fields->use_viscol;
+ if (!qfprev->qf_fnum) {
+ qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory,
+ *fields->namebuf || qfl->qf_directory
+ ? fields->namebuf
+ : qfl->qf_currfile && fields->valid
+ ? qfl->qf_currfile : 0);
+ }
+ }
+ if (idx == 'Z') {
+ qfl->qf_multiline = qfl->qf_multiignore = false;
+ }
+ line_breakcheck();
+
+ return QF_IGNORE_LINE;
+}
+
+/// Free a location list.
static void ll_free_all(qf_info_T **pqi)
{
int i;
@@ -1252,6 +1382,7 @@ static void ll_free_all(qf_info_T **pqi)
}
}
+/// Free all the quickfix/location lists in the stack.
void qf_free_all(win_T *wp)
{
int i;
@@ -1273,6 +1404,7 @@ void qf_free_all(win_T *wp)
/// @param qf_idx list index
/// @param dir optional directory name
/// @param fname file name or NULL
+/// @param module module name or NULL
/// @param bufnum buffer number or zero
/// @param mesg message
/// @param lnum line number
@@ -1285,9 +1417,9 @@ void qf_free_all(win_T *wp)
///
/// @returns OK or FAIL.
static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
- int bufnum, char_u *mesg, long lnum, int col,
- char_u vis_col, char_u *pattern, int nr, char_u type,
- char_u valid)
+ char_u *module, int bufnum, char_u *mesg, long lnum,
+ int col, char_u vis_col, char_u *pattern, int nr,
+ char_u type, char_u valid)
{
qfline_T *qfp = xmalloc(sizeof(qfline_T));
qfline_T **lastp; // pointer to qf_last or NULL
@@ -1312,9 +1444,15 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
} else {
qfp->qf_pattern = vim_strsave(pattern);
}
+ if (module == NULL || *module == NUL) {
+ qfp->qf_module = NULL;
+ } else {
+ qfp->qf_module = vim_strsave(module);
+ }
qfp->qf_nr = nr;
- if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */
+ if (type != 1 && !vim_isprintc(type)) { // only printable chars allowed
type = 0;
+ }
qfp->qf_type = (char_u)type;
qfp->qf_valid = valid;
@@ -1443,6 +1581,7 @@ void copy_loclist(win_T *from, win_T *to)
to->w_llist->qf_curlist,
NULL,
NULL,
+ from_qfp->qf_module,
0,
from_qfp->qf_text,
from_qfp->qf_lnum,
@@ -1473,6 +1612,7 @@ void copy_loclist(win_T *from, win_T *to)
// Assign a new ID for the location list
to_qfl->qf_id = ++last_qf_id;
+ to_qfl->qf_changedtick = 0L;
/* When no valid entries are present in the list, qf_ptr points to
* the first item in the list */
@@ -1699,6 +1839,27 @@ static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename)
return ds_ptr == NULL ? NULL : ds_ptr->dirname;
}
+/// Returns true, if a quickfix/location list with the given identifier exists.
+static bool qflist_valid(win_T *wp, unsigned int qf_id)
+{
+ qf_info_T *qi = &ql_info;
+
+ if (wp) {
+ qi = GET_LOC_LIST(wp);
+ if (!qi) {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < qi->qf_listcount; i++) {
+ if (qi->qf_lists[i].qf_id == qf_id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/// When loading a file from the quickfix, the auto commands may modify it.
/// This may invalidate the current quickfix entry. This function checks
/// whether a entry is still present in the quickfix.
@@ -1725,314 +1886,520 @@ static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr)
return true;
}
-/*
- * jump to a quickfix line
- * if dir == FORWARD go "errornr" valid entries forward
- * if dir == BACKWARD go "errornr" valid entries backward
- * if dir == FORWARD_FILE go "errornr" valid entries files backward
- * if dir == BACKWARD_FILE go "errornr" valid entries files backward
- * else if "errornr" is zero, redisplay the same line
- * else go to entry "errornr"
- */
-void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
+/// Get the next valid entry in the current quickfix/location list. The search
+/// starts from the current entry. Returns NULL on failure.
+static qfline_T *get_next_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+ int *qf_index, int dir)
{
- qf_info_T *ll_ref;
- qfline_T *qf_ptr;
- qfline_T *old_qf_ptr;
- int qf_index;
- int old_qf_fnum;
- int old_qf_index;
- int prev_index;
- static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
- char_u *err = e_no_more_items;
- linenr_T i;
- buf_T *old_curbuf;
- linenr_T old_lnum;
- colnr_T screen_col;
- colnr_T char_col;
- char_u *line;
- char_u *old_swb = p_swb;
- unsigned old_swb_flags = swb_flags;
- int opened_window = FALSE;
- win_T *win;
- win_T *altwin;
- int flags;
- win_T *oldwin = curwin;
- int print_message = TRUE;
- int len;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
- int ok = OK;
- bool usable_win;
+ int idx = *qf_index;
+ int old_qf_fnum = qf_ptr->qf_fnum;
- if (qi == NULL)
- qi = &ql_info;
+ do {
+ if (idx == qi->qf_lists[qi->qf_curlist].qf_count
+ || qf_ptr->qf_next == NULL) {
+ return NULL;
+ }
+ idx++;
+ qf_ptr = qf_ptr->qf_next;
+ } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- if (qi->qf_curlist >= qi->qf_listcount
- || qi->qf_lists[qi->qf_curlist].qf_count == 0) {
- EMSG(_(e_quickfix));
- return;
- }
+ *qf_index = idx;
+ return qf_ptr;
+}
- qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
- old_qf_ptr = qf_ptr;
- qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
- old_qf_index = qf_index;
- if (dir == FORWARD || dir == FORWARD_FILE) { /* next valid entry */
- while (errornr--) {
- old_qf_ptr = qf_ptr;
- prev_index = qf_index;
- old_qf_fnum = qf_ptr->qf_fnum;
- do {
- if (qf_index == qi->qf_lists[qi->qf_curlist].qf_count
- || qf_ptr->qf_next == NULL) {
- qf_ptr = old_qf_ptr;
- qf_index = prev_index;
- if (err != NULL) {
- EMSG(_(err));
- goto theend;
- }
- errornr = 0;
- break;
- }
- ++qf_index;
- qf_ptr = qf_ptr->qf_next;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
- && !qf_ptr->qf_valid)
- || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- err = NULL;
- }
- } else if (dir == BACKWARD || dir == BACKWARD_FILE) { /* prev. valid entry */
- while (errornr--) {
- old_qf_ptr = qf_ptr;
- prev_index = qf_index;
- old_qf_fnum = qf_ptr->qf_fnum;
- do {
- if (qf_index == 1 || qf_ptr->qf_prev == NULL) {
- qf_ptr = old_qf_ptr;
- qf_index = prev_index;
- if (err != NULL) {
- EMSG(_(err));
- goto theend;
- }
- errornr = 0;
- break;
- }
- --qf_index;
- qf_ptr = qf_ptr->qf_prev;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
- && !qf_ptr->qf_valid)
- || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- err = NULL;
+/// Get the previous valid entry in the current quickfix/location list. The
+/// search starts from the current entry. Returns NULL on failure.
+static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+ int *qf_index, int dir)
+{
+ int idx = *qf_index;
+ int old_qf_fnum = qf_ptr->qf_fnum;
+
+ do {
+ if (idx == 1 || qf_ptr->qf_prev == NULL) {
+ return NULL;
}
- } else if (errornr != 0) { /* go to specified number */
- while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL) {
- --qf_index;
- qf_ptr = qf_ptr->qf_prev;
+ idx--;
+ qf_ptr = qf_ptr->qf_prev;
+ } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
+
+ *qf_index = idx;
+ return qf_ptr;
+}
+
+/// Get the n'th (errornr) previous/next valid entry from the current entry in
+/// the quickfix list.
+/// dir == FORWARD or FORWARD_FILE: next valid entry
+/// dir == BACKWARD or BACKWARD_FILE: previous valid entry
+static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr,
+ qfline_T *qf_ptr, int *qf_index, int dir)
+{
+ qfline_T *prev_qf_ptr;
+ int prev_index;
+ static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
+ char_u *err = e_no_more_items;
+
+ while (errornr--) {
+ prev_qf_ptr = qf_ptr;
+ prev_index = *qf_index;
+
+ if (dir == FORWARD || dir == FORWARD_FILE) {
+ qf_ptr = get_next_valid_entry(qi, qf_ptr, qf_index, dir);
+ } else {
+ qf_ptr = get_prev_valid_entry(qi, qf_ptr, qf_index, dir);
}
- while (errornr > qf_index && qf_index <
- qi->qf_lists[qi->qf_curlist].qf_count
- && qf_ptr->qf_next != NULL) {
- ++qf_index;
- qf_ptr = qf_ptr->qf_next;
+
+ if (qf_ptr == NULL) {
+ qf_ptr = prev_qf_ptr;
+ *qf_index = prev_index;
+ if (err != NULL) {
+ EMSG(_(err));
+ return NULL;
+ }
+ break;
}
+
+ err = NULL;
}
- qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
- if (qf_win_pos_update(qi, old_qf_index))
- /* No need to print the error message if it's visible in the error
- * window */
- print_message = FALSE;
+ return qf_ptr;
+}
- /*
- * For ":helpgrep" find a help window or open one.
- */
- if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
- win_T *wp = NULL;
+/// Get n'th (errornr) quickfix entry
+static qfline_T *get_nth_entry(qf_info_T *qi, int errornr, qfline_T *qf_ptr,
+ int *cur_qfidx)
+{
+ int qf_idx = *cur_qfidx;
- if (cmdmod.tab == 0) {
- FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
- if (bt_help(wp2->w_buffer)) {
- wp = wp2;
- break;
- }
+ // New error number is less than the current error number
+ while (errornr < qf_idx && qf_idx > 1 && qf_ptr->qf_prev != NULL) {
+ qf_idx--;
+ qf_ptr = qf_ptr->qf_prev;
+ }
+
+ // New error number is greater than the current error number
+ while (errornr > qf_idx
+ && qf_idx < qi->qf_lists[qi->qf_curlist].qf_count
+ && qf_ptr->qf_next != NULL) {
+ qf_idx++;
+ qf_ptr = qf_ptr->qf_next;
+ }
+
+ *cur_qfidx = qf_idx;
+ return qf_ptr;
+}
+
+/// Find a help window or open one.
+static int jump_to_help_window(qf_info_T *qi, int *opened_window)
+{
+ win_T *wp = NULL;
+
+ if (cmdmod.tab != 0) {
+ wp = NULL;
+ } else {
+ FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
+ if (bt_help(wp2->w_buffer)) {
+ wp = wp2;
+ break;
}
}
- if (wp != NULL && wp->w_buffer->b_nwindows > 0)
- win_enter(wp, true);
- else {
- /*
- * Split off help window; put it at far top if no position
- * specified, the current window is vertically split and narrow.
- */
- flags = WSP_HELP;
- if (cmdmod.split == 0 && curwin->w_width != Columns
- && curwin->w_width < 80)
- flags |= WSP_TOP;
- if (qi != &ql_info)
- flags |= WSP_NEWLOC; /* don't copy the location list */
-
- if (win_split(0, flags) == FAIL)
- goto theend;
- opened_window = TRUE; /* close it when fail */
-
- if (curwin->w_height < p_hh)
- win_setheight((int)p_hh);
-
- if (qi != &ql_info) { /* not a quickfix list */
- /* The new window should use the supplied location list */
- curwin->w_llist = qi;
- qi->qf_refcount++;
- }
+ }
+
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
+ win_enter(wp, true);
+ } else {
+ // Split off help window; put it at far top if no position
+ // specified, the current window is vertically split and narrow.
+ int flags = WSP_HELP;
+ if (cmdmod.split == 0
+ && curwin->w_width != Columns
+ && curwin->w_width < 80) {
+ flags |= WSP_TOP;
}
- if (!p_im)
- restart_edit = 0; /* don't want insert mode in help file */
- }
+ if (qi != &ql_info) {
+ flags |= WSP_NEWLOC; // don't copy the location list
+ }
- /*
- * If currently in the quickfix window, find another window to show the
- * file in.
- */
- if (bt_quickfix(curbuf) && !opened_window) {
- win_T *usable_win_ptr = NULL;
+ if (win_split(0, flags) == FAIL) {
+ return FAIL;
+ }
- /*
- * If there is no file specified, we don't know where to go.
- * But do advance, otherwise ":cn" gets stuck.
- */
- if (qf_ptr->qf_fnum == 0)
- goto theend;
+ *opened_window = true;
- usable_win = false;
+ if (curwin->w_height < p_hh) {
+ win_setheight((int)p_hh);
+ }
- ll_ref = curwin->w_llist_ref;
- if (ll_ref != NULL) {
- /* Find a window using the same location list that is not a
- * quickfix window. */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_llist == ll_ref
- && wp->w_buffer->b_p_bt[0] != 'q') {
- usable_win = true;
- usable_win_ptr = wp;
- break;
- }
+ if (qi != &ql_info) { // not a quickfix list
+ // The new window should use the supplied location list
+ curwin->w_llist = qi;
+ qi->qf_refcount++;
+ }
+ }
+
+ if (!p_im) {
+ restart_edit = 0; // don't want insert mode in help file
+ }
+
+ return OK;
+}
+
+/// Find a suitable window for opening a file (qf_fnum) and jump to it.
+/// If the file is already opened in a window, jump to it.
+static int qf_jump_to_usable_window(int qf_fnum, int *opened_window)
+{
+ win_T *usable_win_ptr = NULL;
+ int usable_win;
+ int flags;
+ win_T *win = NULL;
+ win_T *altwin;
+
+ usable_win = 0;
+
+ qf_info_T *ll_ref = curwin->w_llist_ref;
+ if (ll_ref != NULL) {
+ // Find a window using the same location list that is not a
+ // quickfix window.
+ FOR_ALL_WINDOWS_IN_TAB(usable_win_ptr2, curtab) {
+ if (usable_win_ptr2->w_llist == ll_ref
+ && !bt_quickfix(usable_win_ptr2->w_buffer)) {
+ usable_win_ptr = usable_win_ptr2;
+ usable_win = 1;
+ break;
}
}
+ }
- if (!usable_win) {
- /* Locate a window showing a normal buffer */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->b_p_bt[0] == NUL) {
- usable_win = true;
- break;
- }
+ if (!usable_win) {
+ // Locate a window showing a normal buffer
+ FOR_ALL_WINDOWS_IN_TAB(win2, curtab) {
+ if (win2->w_buffer->b_p_bt[0] == NUL) {
+ win = win2;
+ usable_win = 1;
+ break;
}
}
+ }
- /*
- * If no usable window is found and 'switchbuf' contains "usetab"
- * then search in other tabs.
- */
- if (!usable_win && (swb_flags & SWB_USETAB)) {
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) {
- goto_tabpage_win(tp, wp);
- usable_win = true;
- goto win_found;
- }
+ // If no usable window is found and 'switchbuf' contains "usetab"
+ // then search in other tabs.
+ if (!usable_win && (swb_flags & SWB_USETAB)) {
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer->b_fnum == qf_fnum) {
+ goto_tabpage_win(tp, wp);
+ usable_win = 1;
+ goto win_found;
}
}
+ }
win_found:
- /*
- * If there is only one window and it is the quickfix window, create a
- * new one above the quickfix window.
- */
- if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
- flags = WSP_ABOVE;
- if (ll_ref != NULL)
- flags |= WSP_NEWLOC;
- if (win_split(0, flags) == FAIL)
- goto failed; /* not enough room for window */
- opened_window = TRUE; /* close it when fail */
- p_swb = empty_option; /* don't split again */
- swb_flags = 0;
- RESET_BINDING(curwin);
- if (ll_ref != NULL) {
- /* The new window should use the location list from the
- * location list window */
- curwin->w_llist = ll_ref;
- ll_ref->qf_refcount++;
- }
- } else {
- if (curwin->w_llist_ref != NULL) {
- /* In a location window */
- win = usable_win_ptr;
+ // If there is only one window and it is the quickfix window, create a
+ // new one above the quickfix window.
+ if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
+ flags = WSP_ABOVE;
+ if (ll_ref != NULL) {
+ flags |= WSP_NEWLOC;
+ }
+ if (win_split(0, flags) == FAIL) {
+ return FAIL; // not enough room for window
+ }
+ *opened_window = true; // close it when fail
+ p_swb = empty_option; // don't split again
+ swb_flags = 0;
+ RESET_BINDING(curwin);
+ if (ll_ref != NULL) {
+ // The new window should use the location list from the
+ // location list window
+ curwin->w_llist = ll_ref;
+ ll_ref->qf_refcount++;
+ }
+ } else {
+ if (curwin->w_llist_ref != NULL) {
+ // In a location window
+ win = usable_win_ptr;
+ if (win == NULL) {
+ // Find the window showing the selected file
+ FOR_ALL_WINDOWS_IN_TAB(win2, curtab) {
+ if (win2->w_buffer->b_fnum == qf_fnum) {
+ win = win2;
+ break;
+ }
+ }
if (win == NULL) {
- /* Find the window showing the selected file */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) {
- win = wp;
+ // Find a previous usable window
+ win = curwin;
+ do {
+ if (win->w_buffer->b_p_bt[0] == NUL) {
break;
}
+ if (win->w_prev == NULL) {
+ win = lastwin; // wrap around the top
+ } else {
+ win = win->w_prev; // go to previous window
+ }
+ } while (win != curwin);
+ }
+ }
+ win_goto(win);
+
+ // If the location list for the window is not set, then set it
+ // to the location list from the location window
+ if (win->w_llist == NULL) {
+ win->w_llist = ll_ref;
+ ll_ref->qf_refcount++;
+ }
+ } else {
+ // Try to find a window that shows the right buffer.
+ // Default to the window just above the quickfix buffer.
+ win = curwin;
+ altwin = NULL;
+ for (;;) {
+ if (win->w_buffer->b_fnum == qf_fnum) {
+ break;
+ }
+ if (win->w_prev == NULL) {
+ win = lastwin; // wrap around the top
+ } else {
+ win = win->w_prev; // go to previous window
+ }
+ if (IS_QF_WINDOW(win)) {
+ // Didn't find it, go to the window before the quickfix window.
+ if (altwin != NULL) {
+ win = altwin;
+ } else if (curwin->w_prev != NULL) {
+ win = curwin->w_prev;
+ } else {
+ win = curwin->w_next;
}
- if (win == NULL) {
- /* Find a previous usable window */
- win = curwin;
- do {
- if (win->w_buffer->b_p_bt[0] == NUL)
- break;
- if (win->w_prev == NULL)
- win = lastwin; /* wrap around the top */
- else
- win = win->w_prev; /* go to previous window */
- } while (win != curwin);
- }
+ break;
}
- win_goto(win);
- /* If the location list for the window is not set, then set it
- * to the location list from the location window */
- if (win->w_llist == NULL) {
- win->w_llist = ll_ref;
- ll_ref->qf_refcount++;
+ // Remember a usable window.
+ if (altwin == NULL && !win->w_p_pvw
+ && win->w_buffer->b_p_bt[0] == NUL) {
+ altwin = win;
}
+ }
+
+ win_goto(win);
+ }
+ }
+
+ return OK;
+}
+
+/// Edit the selected file or help file.
+static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit,
+ win_T *oldwin, int *opened_window, int *abort)
+{
+ int retval = OK;
+
+ if (qf_ptr->qf_type == 1) {
+ // Open help file (do_ecmd() will set b_help flag, readfile() will
+ // set b_p_ro flag).
+ if (!can_abandon(curbuf, forceit)) {
+ EMSG(_(e_nowrtmsg));
+ retval = false;
+ } else {
+ retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
+ ECMD_HIDE + ECMD_SET_HELP,
+ oldwin == curwin ? curwin : NULL);
+ }
+ } else {
+ int old_qf_curlist = qi->qf_curlist;
+ unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+
+ retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
+ GETF_SETMARK | GETF_SWITCH, forceit);
+
+ if (qi != &ql_info) {
+ // Location list. Check whether the associated window is still
+ // present and the list is still valid.
+ if (!win_valid_any_tab(oldwin)) {
+ EMSG(_("E924: Current window was closed"));
+ *abort = true;
+ *opened_window = false;
+ } else if (!qflist_valid(oldwin, save_qfid)) {
+ EMSG(_(e_loc_list_changed));
+ *abort = true;
+ }
+ } else if (old_qf_curlist != qi->qf_curlist
+ || !is_qf_entry_present(qi, qf_ptr)) {
+ if (qi == &ql_info) {
+ EMSG(_("E925: Current quickfix was changed"));
} else {
+ EMSG(_(e_loc_list_changed));
+ }
+ *abort = true;
+ }
- /*
- * Try to find a window that shows the right buffer.
- * Default to the window just above the quickfix buffer.
- */
- win = curwin;
- altwin = NULL;
- for (;; ) {
- if (win->w_buffer->b_fnum == qf_ptr->qf_fnum)
- break;
- if (win->w_prev == NULL)
- win = lastwin; /* wrap around the top */
- else
- win = win->w_prev; /* go to previous window */
-
- if (IS_QF_WINDOW(win)) {
- /* Didn't find it, go to the window before the quickfix
- * window. */
- if (altwin != NULL)
- win = altwin;
- else if (curwin->w_prev != NULL)
- win = curwin->w_prev;
- else
- win = curwin->w_next;
+ if (*abort) {
+ retval = false;
+ }
+ }
+
+ return retval;
+}
+
+/// Goto the error line in the current file using either line/column number or a
+/// search pattern.
+static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char_u qf_viscol,
+ char_u *qf_pattern)
+{
+ linenr_T i;
+ char_u *line;
+ colnr_T screen_col;
+ colnr_T char_col;
+
+ if (qf_pattern == NULL) {
+ // Go to line with error, unless qf_lnum is 0.
+ i = qf_lnum;
+ if (i > 0) {
+ if (i > curbuf->b_ml.ml_line_count) {
+ i = curbuf->b_ml.ml_line_count;
+ }
+ curwin->w_cursor.lnum = i;
+ }
+ if (qf_col > 0) {
+ curwin->w_cursor.col = qf_col - 1;
+ curwin->w_cursor.coladd = 0;
+ if (qf_viscol == true) {
+ // Check each character from the beginning of the error
+ // line up to the error column. For each tab character
+ // found, reduce the error column value by the length of
+ // a tab character.
+ line = get_cursor_line_ptr();
+ screen_col = 0;
+ for (char_col = 0; char_col < curwin->w_cursor.col; char_col++) {
+ if (*line == NUL) {
break;
}
-
- /* Remember a usable window. */
- if (altwin == NULL && !win->w_p_pvw
- && win->w_buffer->b_p_bt[0] == NUL)
- altwin = win;
+ if (*line++ == '\t') {
+ curwin->w_cursor.col -= 7 - (screen_col % 8);
+ screen_col += 8 - (screen_col % 8);
+ } else {
+ screen_col++;
+ }
}
-
- win_goto(win);
}
+ check_cursor();
+ } else {
+ beginline(BL_WHITE | BL_FIX);
+ }
+ } else {
+ // Move the cursor to the first line in the buffer
+ pos_T save_cursor = curwin->w_cursor;
+ curwin->w_cursor.lnum = 0;
+ if (!do_search(NULL, '/', qf_pattern, (long)1, SEARCH_KEEP, NULL, NULL)) {
+ curwin->w_cursor = save_cursor;
+ }
+ }
+}
+
+/// Display quickfix list index and size message
+static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr,
+ buf_T *old_curbuf, linenr_T old_lnum)
+{
+ // Update the screen before showing the message, unless the screen
+ // scrolled up.
+ if (!msg_scrolled) {
+ update_topline_redraw();
+ }
+ snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
+ qi->qf_lists[qi->qf_curlist].qf_count,
+ qf_ptr->qf_cleared ? _(" (line deleted)") : "",
+ (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
+ // Add the message, skipping leading whitespace and newlines.
+ int len = (int)STRLEN(IObuff);
+ qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);
+
+ // Output the message. Overwrite to avoid scrolling when the 'O'
+ // flag is present in 'shortmess'; But when not jumping, print the
+ // whole message.
+ linenr_T i = msg_scroll;
+ if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) {
+ msg_scroll = true;
+ } else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
+ msg_scroll = false;
+ }
+ msg_attr_keep(IObuff, 0, true, false);
+ msg_scroll = (int)i;
+}
+
+/// jump to a quickfix line
+/// if dir == FORWARD go "errornr" valid entries forward
+/// if dir == BACKWARD go "errornr" valid entries backward
+/// if dir == FORWARD_FILE go "errornr" valid entries files backward
+/// if dir == BACKWARD_FILE go "errornr" valid entries files backward
+/// else if "errornr" is zero, redisplay the same line
+/// else go to entry "errornr"
+void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
+{
+ qfline_T *qf_ptr;
+ qfline_T *old_qf_ptr;
+ int qf_index;
+ int old_qf_index;
+ buf_T *old_curbuf;
+ linenr_T old_lnum;
+ char_u *old_swb = p_swb;
+ unsigned old_swb_flags = swb_flags;
+ int opened_window = false;
+ win_T *oldwin = curwin;
+ int print_message = true;
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ int retval = OK;
+
+ if (qi == NULL)
+ qi = &ql_info;
+
+ if (qi->qf_curlist >= qi->qf_listcount
+ || qi->qf_lists[qi->qf_curlist].qf_count == 0) {
+ EMSG(_(e_quickfix));
+ return;
+ }
+
+ qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
+ old_qf_ptr = qf_ptr;
+ qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
+ old_qf_index = qf_index;
+ if (dir != 0) { // next/prev valid entry
+ qf_ptr = get_nth_valid_entry(qi, errornr, qf_ptr, &qf_index, dir);
+ if (qf_ptr == NULL) {
+ qf_ptr = old_qf_ptr;
+ qf_index = old_qf_index;
+ goto theend; // The horror... the horror...
+ }
+ } else if (errornr != 0) { // go to specified number
+ qf_ptr = get_nth_entry(qi, errornr, qf_ptr, &qf_index);
+ }
+
+ qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
+ if (qf_win_pos_update(qi, old_qf_index))
+ /* No need to print the error message if it's visible in the error
+ * window */
+ print_message = FALSE;
+
+ // For ":helpgrep" find a help window or open one.
+ if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
+ if (jump_to_help_window(qi, &opened_window) == FAIL) {
+ goto theend;
+ }
+ }
+
+ // If currently in the quickfix window, find another window to show the
+ // file in.
+ if (bt_quickfix(curbuf) && !opened_window) {
+ // If there is no file specified, we don't know where to go.
+ // But do advance, otherwise ":cn" gets stuck.
+ if (qf_ptr->qf_fnum == 0) {
+ goto theend;
+ }
+ if (qf_jump_to_usable_window(qf_ptr->qf_fnum, &opened_window) == FAIL) {
+ goto failed;
}
}
@@ -2044,122 +2411,28 @@ win_found:
old_lnum = curwin->w_cursor.lnum;
if (qf_ptr->qf_fnum != 0) {
- if (qf_ptr->qf_type == 1) {
- /* Open help file (do_ecmd() will set b_help flag, readfile() will
- * set b_p_ro flag). */
- if (!can_abandon(curbuf, forceit)) {
- EMSG(_(e_nowrtmsg));
- ok = false;
- } else {
- ok = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
- ECMD_HIDE + ECMD_SET_HELP,
- oldwin == curwin ? curwin : NULL);
- }
- } else {
- int old_qf_curlist = qi->qf_curlist;
- bool is_abort = false;
-
- ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
- GETF_SETMARK | GETF_SWITCH, forceit);
- if (qi != &ql_info && !win_valid_any_tab(oldwin)) {
- EMSG(_("E924: Current window was closed"));
- is_abort = true;
- opened_window = false;
- } else if (old_qf_curlist != qi->qf_curlist // -V560
- || !is_qf_entry_present(qi, qf_ptr)) {
- if (qi == &ql_info) {
- EMSG(_("E925: Current quickfix was changed"));
- } else {
- EMSG(_("E926: Current location list was changed"));
- }
- is_abort = true;
- }
-
- if (is_abort) {
- ok = false;
- qi = NULL;
- qf_ptr = NULL;
- }
+ int abort = false;
+ retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, oldwin, &opened_window,
+ &abort);
+ if (abort) {
+ qi = NULL;
+ qf_ptr = NULL;
}
}
- if (ok == OK) {
- /* When not switched to another buffer, still need to set pc mark */
- if (curbuf == old_curbuf)
+ if (retval == OK) {
+ // When not switched to another buffer, still need to set pc mark
+ if (curbuf == old_curbuf) {
setpcmark();
-
- if (qf_ptr->qf_pattern == NULL) {
- /*
- * Go to line with error, unless qf_lnum is 0.
- */
- i = qf_ptr->qf_lnum;
- if (i > 0) {
- if (i > curbuf->b_ml.ml_line_count)
- i = curbuf->b_ml.ml_line_count;
- curwin->w_cursor.lnum = i;
- }
- if (qf_ptr->qf_col > 0) {
- curwin->w_cursor.col = qf_ptr->qf_col - 1;
- curwin->w_cursor.coladd = 0;
- if (qf_ptr->qf_viscol == true) {
- // Check each character from the beginning of the error
- // line up to the error column. For each tab character
- // found, reduce the error column value by the length of
- // a tab character.
- line = get_cursor_line_ptr();
- screen_col = 0;
- for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col) {
- if (*line == NUL)
- break;
- if (*line++ == '\t') {
- curwin->w_cursor.col -= 7 - (screen_col % 8);
- screen_col += 8 - (screen_col % 8);
- } else
- ++screen_col;
- }
- }
- check_cursor();
- } else
- beginline(BL_WHITE | BL_FIX);
- } else {
- pos_T save_cursor;
-
- /* Move the cursor to the first line in the buffer */
- save_cursor = curwin->w_cursor;
- curwin->w_cursor.lnum = 0;
- if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1,
- SEARCH_KEEP, NULL, NULL)) {
- curwin->w_cursor = save_cursor;
- }
}
+ qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol,
+ qf_ptr->qf_pattern);
+
if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped)
foldOpenCursor();
if (print_message) {
- /* Update the screen before showing the message, unless the screen
- * scrolled up. */
- if (!msg_scrolled)
- update_topline_redraw();
- sprintf((char *)IObuff, _("(%d of %d)%s%s: "), qf_index,
- qi->qf_lists[qi->qf_curlist].qf_count,
- qf_ptr->qf_cleared ? _(" (line deleted)") : "",
- (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
- /* Add the message, skipping leading whitespace and newlines. */
- len = (int)STRLEN(IObuff);
- qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);
-
- /* Output the message. Overwrite to avoid scrolling when the 'O'
- * flag is present in 'shortmess'; But when not jumping, print the
- * whole message. */
- i = msg_scroll;
- if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) {
- msg_scroll = true;
- } else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
- msg_scroll = false;
- }
- msg_ext_set_kind("quickfix");
- msg_attr_keep(IObuff, 0, true, false);
- msg_scroll = (int)i;
+ qf_jump_print_msg(qi, qf_index, qf_ptr, old_curbuf, old_lnum);
}
} else {
if (opened_window) {
@@ -2257,17 +2530,22 @@ void qf_list(exarg_T *eap)
break;
fname = NULL;
- if (qfp->qf_fnum != 0
- && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
- fname = buf->b_fname;
- if (qfp->qf_type == 1) /* :helpgrep */
- fname = path_tail(fname);
+ if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i,
+ (char *)qfp->qf_module);
+ } else {
+ if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
+ fname = buf->b_fname;
+ if (qfp->qf_type == 1) { // :helpgrep
+ fname = path_tail(fname);
+ }
+ }
+ if (fname == NULL) {
+ snprintf((char *)IObuff, IOSIZE, "%2d", i);
+ } else {
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)fname);
+ }
}
- if (fname == NULL)
- sprintf((char *)IObuff, "%2d", i);
- else
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
- i, (char *)fname);
msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index
? HL_ATTR(HLF_QFL) : HL_ATTR(HLF_D));
if (qfp->qf_lnum == 0) {
@@ -2328,6 +2606,8 @@ static void qf_fmt_text(char_u *text, char_u *buf, int bufsize)
buf[i] = NUL;
}
+/// Display information (list number, list size and the title) about a
+/// quickfix/location list.
static void qf_msg(qf_info_T *qi, int which, char *lead)
{
char *title = (char *)qi->qf_lists[which].qf_title;
@@ -2397,6 +2677,7 @@ void qf_age(exarg_T *eap)
qf_update_buffer(qi, NULL);
}
+/// Display the information about all the quickfix/location lists in the stack.
void qf_history(exarg_T *eap)
{
qf_info_T *qi = &ql_info;
@@ -2428,9 +2709,10 @@ static void qf_free_items(qf_info_T *qi, int idx)
qfp = qfl->qf_start;
qfpnext = qfp->qf_next;
if (!stop) {
+ xfree(qfp->qf_module);
xfree(qfp->qf_text);
- stop = (qfp == qfpnext);
xfree(qfp->qf_pattern);
+ stop = (qfp == qfpnext);
xfree(qfp);
if (stop) {
// Somehow qf_count may have an incorrect value, set it to 1
@@ -2472,6 +2754,7 @@ static void qf_free(qf_info_T *qi, int idx)
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
qfl->qf_id = 0;
+ qfl->qf_changedtick = 0L;
}
/*
@@ -2841,10 +3124,8 @@ static int is_qf_win(win_T *win, qf_info_T *qi)
return FALSE;
}
-/*
- * Find a window displaying the quickfix/location list 'qi'
- * Searches in only the windows opened in the current tab.
- */
+/// Find a window displaying the quickfix/location list 'qi'
+/// Only searches in the current tabpage.
static win_T *qf_find_win(qf_info_T *qi)
{
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
@@ -2972,9 +3253,12 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
lnum = buf->b_ml.ml_line_count;
}
while (lnum < qi->qf_lists[qi->qf_curlist].qf_count) {
- if (qfp->qf_fnum != 0
- && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
- && errbuf->b_fname != NULL) {
+ if (qfp->qf_module != NULL) {
+ STRCPY(IObuff, qfp->qf_module);
+ len = (int)STRLEN(IObuff);
+ } else if (qfp->qf_fnum != 0
+ && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
+ && errbuf->b_fname != NULL) {
if (qfp->qf_type == 1) { // :helpgrep
STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff));
} else {
@@ -3062,51 +3346,9 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
KeyTyped = old_KeyTyped;
}
-/*
- * Return TRUE if "buf" is the quickfix buffer.
- */
-int bt_quickfix(const buf_T *const buf)
-{
- return buf != NULL && buf->b_p_bt[0] == 'q';
-}
-
-// Return TRUE if "buf" is a "nofile", "acwrite" or "terminal" buffer.
-// This means the buffer name is not a file name.
-int bt_nofile(buf_T *buf)
-{
- return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
- || buf->b_p_bt[0] == 'a' || buf->terminal);
-}
-
-// Return TRUE if "buf" is a "nowrite", "nofile" or "terminal" buffer.
-int bt_dontwrite(buf_T *buf)
-{
- return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal);
-}
-
-int bt_dontwrite_msg(buf_T *buf)
-{
- if (bt_dontwrite(buf)) {
- EMSG(_("E382: Cannot write, 'buftype' option is set"));
- return TRUE;
- }
- return FALSE;
-}
-
-/*
- * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
- * and 'bufhidden'.
- */
-int buf_hide(buf_T *buf)
+static void qf_list_changed(qf_info_T *qi, int qf_idx)
{
- /* 'bufhidden' overrules 'hidden' and ":hide", check it first */
- switch (buf->b_p_bh[0]) {
- case 'u': /* "unload" */
- case 'w': /* "wipe" */
- case 'd': return FALSE; /* "delete" */
- case 'h': return TRUE; /* "hide" */
- }
- return p_hid || cmdmod.hide;
+ qi->qf_lists[qf_idx].qf_changedtick++;
}
/*
@@ -3192,17 +3434,21 @@ void ex_make(exarg_T *eap)
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
&& eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
(eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd),
- *eap->cmdlinep, enc);
+ qf_cmdtitle(*eap->cmdlinep), enc);
if (wp != NULL) {
qi = GET_LOC_LIST(wp);
}
+ if (res >= 0 && qi != NULL) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, TRUE, curbuf);
- if (qi->qf_curlist < qi->qf_listcount)
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true,
+ curbuf);
+ if (qi != NULL && qi->qf_curlist < qi->qf_listcount) {
res = qi->qf_lists[qi->qf_curlist].qf_count;
- else
+ } else {
res = 0;
+ }
}
if (res > 0 && !eap->forceit)
qf_jump(qi, 0, 0, FALSE); /* display first error */
@@ -3546,19 +3792,218 @@ void ex_cfile(exarg_T *eap)
// first error.
// :caddfile adds to an existing quickfix list. If there is no
// quickfix list then a new list is created.
- if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
- && eap->cmdidx != CMD_laddfile),
- *eap->cmdlinep, enc) > 0
- && (eap->cmdidx == CMD_cfile
- || eap->cmdidx == CMD_lfile)) {
- if (au_name != NULL)
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
- if (wp != NULL)
- qi = GET_LOC_LIST(wp);
- qf_jump(qi, 0, 0, eap->forceit); /* display first error */
+ int res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
+ && eap->cmdidx != CMD_laddfile),
+ qf_cmdtitle(*eap->cmdlinep), enc);
+ if (wp != NULL) {
+ qi = GET_LOC_LIST(wp);
+ }
+ if (res >= 0 && qi != NULL) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ unsigned save_qfid = 0;
+ if (qi != NULL) {
+ save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, false, curbuf);
+ }
+ // Autocmd might have freed the quickfix/location list. Check whether it is
+ // still valid
+ if (qi != NULL && !qflist_valid(wp, save_qfid)) {
+ return;
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
+ }
+}
+
+/// Return the vimgrep autocmd name.
+static char_u *vgr_get_auname(cmdidx_T cmdidx)
+{
+ switch (cmdidx) {
+ case CMD_vimgrep: return (char_u *)"vimgrep";
+ case CMD_lvimgrep: return (char_u *)"lvimgrep";
+ case CMD_vimgrepadd: return (char_u *)"vimgrepadd";
+ case CMD_lvimgrepadd: return (char_u *)"lvimgrepadd";
+ case CMD_grep: return (char_u *)"grep";
+ case CMD_lgrep: return (char_u *)"lgrep";
+ case CMD_grepadd: return (char_u *)"grepadd";
+ case CMD_lgrepadd: return (char_u *)"lgrepadd";
+ default: return NULL;
+ }
+}
+
+/// Initialize the regmatch used by vimgrep for pattern "s".
+static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
+{
+ // Get the search pattern: either white-separated or enclosed in //.
+ regmatch->regprog = NULL;
+
+ if (s == NULL || *s == NUL) {
+ // Pattern is empty, use last search pattern.
+ if (last_search_pat() == NULL) {
+ EMSG(_(e_noprevre));
+ return;
+ }
+ regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
} else {
- if (au_name != NULL)
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
+ regmatch->regprog = vim_regcomp(s, RE_MAGIC);
+ }
+
+ regmatch->rmm_ic = p_ic;
+ regmatch->rmm_maxcol = 0;
+}
+
+
+/// Display a file name when vimgrep is running.
+static void vgr_display_fname(char_u *fname)
+{
+ msg_start();
+ char_u *p = msg_strtrunc(fname, true);
+ if (p == NULL) {
+ msg_outtrans(fname);
+ } else {
+ msg_outtrans(p);
+ xfree(p);
+ }
+ msg_clr_eos();
+ msg_didout = false; // overwrite this message
+ msg_nowait = true; // don't wait for this message
+ msg_col = 0;
+ ui_flush();
+}
+
+/// Load a dummy buffer to search for a pattern using vimgrep.
+static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start,
+ char_u *dirname_now)
+{
+ char_u *save_ei = NULL;
+
+ // Don't do Filetype autocommands to avoid loading syntax and
+ // indent scripts, a great speed improvement.
+ long save_mls = p_mls;
+ p_mls = 0;
+
+ // Load file into a buffer, so that 'fileencoding' is detected,
+ // autocommands applied, etc.
+ buf_T *buf = load_dummy_buffer(fname, dirname_start, dirname_now);
+
+ p_mls = save_mls;
+ au_event_restore(save_ei);
+
+ return buf;
+}
+
+/// Check whether a quickfix/location list is valid. Autocmds may remove or
+/// change a quickfix list when vimgrep is running. If the list is not found,
+/// create a new list.
+static bool vgr_qflist_valid(qf_info_T *qi, unsigned save_qfid,
+ qfline_T *cur_qf_start, int loclist_cmd,
+ char_u *title)
+{
+ if (loclist_cmd) {
+ // Verify that the location list is still valid. An autocmd might have
+ // freed the location list.
+ if (!qflist_valid(curwin, save_qfid)) {
+ EMSG(_(e_loc_list_changed));
+ return false;
+ }
+ }
+ if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start) {
+ int idx;
+ // Autocommands changed the quickfix list. Find the one we were using
+ // and restore it.
+ for (idx = 0; idx < LISTCOUNT; idx++) {
+ if (cur_qf_start == qi->qf_lists[idx].qf_start) {
+ qi->qf_curlist = idx;
+ break;
+ }
+ }
+ if (idx == LISTCOUNT) {
+ // List cannot be found, create a new one.
+ qf_new_list(qi, title);
+ }
+ }
+
+ return true;
+}
+
+
+/// Search for a pattern in all the lines in a buffer and add the matching lines
+/// to a quickfix list.
+static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
+ regmmatch_T *regmatch, long tomatch,
+ int duplicate_name, int flags)
+{
+ bool found_match = false;
+
+ for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0; lnum++) {
+ colnr_T col = 0;
+ while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL,
+ NULL) > 0) {
+ // Pass the buffer number so that it gets used even for a
+ // dummy buffer, unless duplicate_name is set, then the
+ // buffer will be wiped out below.
+ if (qf_add_entry(qi,
+ qi->qf_curlist,
+ NULL, // dir
+ fname,
+ NULL,
+ duplicate_name ? 0 : buf->b_fnum,
+ ml_get_buf(buf, regmatch->startpos[0].lnum + lnum,
+ false),
+ regmatch->startpos[0].lnum + lnum,
+ regmatch->startpos[0].col + 1,
+ false, // vis_col
+ NULL, // search pattern
+ 0, // nr
+ 0, // type
+ true // valid
+ ) == FAIL) {
+ got_int = true;
+ break;
+ }
+ found_match = true;
+ if (--tomatch == 0) {
+ break;
+ }
+ if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) {
+ break;
+ }
+ col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col);
+ if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
+ break;
+ }
+ }
+ line_breakcheck();
+ if (got_int) {
+ break;
+ }
+ }
+
+ return found_match;
+}
+
+/// Jump to the first match and update the directory.
+static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy,
+ buf_T *first_match_buf, char_u *target_dir)
+{
+ buf_T *buf = curbuf;
+ qf_jump(qi, 0, 0, forceit);
+ if (buf != curbuf) {
+ // If we jumped to another buffer redrawing will already be
+ // taken care of.
+ *redraw_for_dummy = false;
+ }
+
+ // Jump to the directory used after loading the buffer.
+ if (curbuf == first_match_buf && target_dir != NULL) {
+ exarg_T ea = {
+ .arg = target_dir,
+ .cmdidx = CMD_lcd,
+ };
+ ex_cd(&ea);
}
}
@@ -3578,8 +4023,9 @@ void ex_vimgrep(exarg_T *eap)
char_u *p;
int fi;
qf_info_T *qi = &ql_info;
+ int loclist_cmd = false;
qfline_T *cur_qf_start;
- long lnum;
+ win_T *wp;
buf_T *buf;
int duplicate_name = FALSE;
int using_dummy;
@@ -3587,28 +4033,15 @@ void ex_vimgrep(exarg_T *eap)
int found_match;
buf_T *first_match_buf = NULL;
time_t seconds = 0;
- long save_mls;
- char_u *save_ei = NULL;
aco_save_T aco;
int flags = 0;
- colnr_T col;
long tomatch;
char_u *dirname_start = NULL;
char_u *dirname_now = NULL;
char_u *target_dir = NULL;
char_u *au_name = NULL;
- switch (eap->cmdidx) {
- case CMD_vimgrep: au_name = (char_u *)"vimgrep"; break;
- case CMD_lvimgrep: au_name = (char_u *)"lvimgrep"; break;
- case CMD_vimgrepadd: au_name = (char_u *)"vimgrepadd"; break;
- case CMD_lvimgrepadd: au_name = (char_u *)"lvimgrepadd"; break;
- case CMD_grep: au_name = (char_u *)"grep"; break;
- case CMD_lgrep: au_name = (char_u *)"lgrep"; break;
- case CMD_grepadd: au_name = (char_u *)"grepadd"; break;
- case CMD_lgrepadd: au_name = (char_u *)"lgrepadd"; break;
- default: break;
- }
+ au_name = vgr_get_auname(eap->cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
@@ -3621,6 +4054,7 @@ void ex_vimgrep(exarg_T *eap)
|| eap->cmdidx == CMD_lgrepadd
|| eap->cmdidx == CMD_lvimgrepadd) {
qi = ll_get_or_alloc_list(curwin);
+ loclist_cmd = true;
}
if (eap->addr_count > 0)
@@ -3630,28 +4064,17 @@ void ex_vimgrep(exarg_T *eap)
/* Get the search pattern: either white-separated or enclosed in // */
regmatch.regprog = NULL;
- char_u *title = vim_strsave(*eap->cmdlinep);
+ char_u *title = vim_strsave(qf_cmdtitle(*eap->cmdlinep));
p = skip_vimgrep_pat(eap->arg, &s, &flags);
if (p == NULL) {
EMSG(_(e_invalpat));
goto theend;
}
- if (s == NULL || *s == NUL) {
- // Pattern is empty, use last search pattern.
- if (last_search_pat() == NULL) {
- EMSG(_(e_noprevre));
- goto theend;
- }
- regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
- } else {
- regmatch.regprog = vim_regcomp(s, RE_MAGIC);
- }
-
- if (regmatch.regprog == NULL)
+ vgr_init_regmatch(&regmatch, s);
+ if (regmatch.regprog == NULL) {
goto theend;
- regmatch.rmm_ic = p_ic;
- regmatch.rmm_maxcol = 0;
+ }
p = skipwhite(p);
if (*p == NUL) {
@@ -3681,8 +4104,9 @@ void ex_vimgrep(exarg_T *eap)
* ":lcd %:p:h" changes the meaning of short path names. */
os_dirname(dirname_start, MAXPATHL);
- /* Remember the value of qf_start, so that we can check for autocommands
- * changing the current quickfix list. */
+ // Remember the current values of the quickfix list and qf_start, so that
+ // we can check for autocommands changing the current quickfix list.
+ unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
seconds = (time_t)0;
@@ -3692,19 +4116,7 @@ void ex_vimgrep(exarg_T *eap)
/* Display the file name every second or so, show the user we are
* working on it. */
seconds = time(NULL);
- msg_start();
- p = msg_strtrunc(fname, TRUE);
- if (p == NULL)
- msg_outtrans(fname);
- else {
- msg_outtrans(p);
- xfree(p);
- }
- msg_clr_eos();
- msg_didout = FALSE; /* overwrite this message */
- msg_nowait = TRUE; /* don't wait for this message */
- msg_col = 0;
- ui_flush();
+ vgr_display_fname(fname);
}
buf = buflist_findname_exp(fnames[fi]);
@@ -3714,88 +4126,28 @@ void ex_vimgrep(exarg_T *eap)
using_dummy = TRUE;
redraw_for_dummy = TRUE;
- /* Don't do Filetype autocommands to avoid loading syntax and
- * indent scripts, a great speed improvement. */
- save_ei = au_event_disable(",Filetype");
- /* Don't use modelines here, it's useless. */
- save_mls = p_mls;
- p_mls = 0;
-
- /* Load file into a buffer, so that 'fileencoding' is detected,
- * autocommands applied, etc. */
- buf = load_dummy_buffer(fname, dirname_start, dirname_now);
-
- p_mls = save_mls;
- au_event_restore(save_ei);
- } else
- /* Use existing, loaded buffer. */
- using_dummy = FALSE;
-
- if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start) {
- int idx;
+ buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now);
+ } else {
+ // Use existing, loaded buffer.
+ using_dummy = false;
+ }
- /* Autocommands changed the quickfix list. Find the one we were
- * using and restore it. */
- for (idx = 0; idx < LISTCOUNT; ++idx)
- if (cur_qf_start == qi->qf_lists[idx].qf_start) {
- qi->qf_curlist = idx;
- break;
- }
- if (idx == LISTCOUNT) {
- /* List cannot be found, create a new one. */
- qf_new_list(qi, *eap->cmdlinep);
- cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
- }
+ // Check whether the quickfix list is still valid
+ if (!vgr_qflist_valid(qi, save_qfid, cur_qf_start, loclist_cmd,
+ *eap->cmdlinep)) {
+ goto theend;
}
+ cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
if (buf == NULL) {
if (!got_int)
smsg(_("Cannot open file \"%s\""), fname);
} else {
- /* Try for a match in all lines of the buffer.
- * For ":1vimgrep" look for first match only. */
- found_match = FALSE;
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0;
- ++lnum) {
- col = 0;
- while (vim_regexec_multi(&regmatch, curwin, buf, lnum,
- col, NULL, NULL) > 0) {
- // Pass the buffer number so that it gets used even for a
- // dummy buffer, unless duplicate_name is set, then the
- // buffer will be wiped out below.
- if (qf_add_entry(qi,
- qi->qf_curlist,
- NULL, // dir
- fname,
- duplicate_name ? 0 : buf->b_fnum,
- ml_get_buf(buf,
- regmatch.startpos[0].lnum + lnum, false),
- regmatch.startpos[0].lnum + lnum,
- regmatch.startpos[0].col + 1,
- false, // vis_col
- NULL, // search pattern
- 0, // nr
- 0, // type
- true) // valid
- == FAIL) {
- got_int = true;
- break;
- }
- found_match = TRUE;
- if (--tomatch == 0)
- break;
- if ((flags & VGR_GLOBAL) == 0
- || regmatch.endpos[0].lnum > 0)
- break;
- col = regmatch.endpos[0].col
- + (col == regmatch.endpos[0].col);
- if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, FALSE)))
- break;
- }
- line_breakcheck();
- if (got_int)
- break;
- }
+ // Try for a match in all lines of the buffer.
+ // For ":1vimgrep" look for first match only.
+ found_match = vgr_match_buflines(qi, fname, buf, &regmatch, tomatch,
+ duplicate_name, flags);
+
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
if (using_dummy) {
@@ -3858,6 +4210,7 @@ void ex_vimgrep(exarg_T *eap)
qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
qi->qf_lists[qi->qf_curlist].qf_index = 1;
+ qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
@@ -3865,24 +4218,18 @@ void ex_vimgrep(exarg_T *eap)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, TRUE, curbuf);
+ // The QuickFixCmdPost autocmd may free the quickfix list. Check the list
+ // is still valid.
+ wp = loclist_cmd ? curwin : NULL;
+ if (!qflist_valid(wp, save_qfid)) {
+ goto theend;
+ }
+
/* Jump to first match. */
if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
if ((flags & VGR_NOJUMP) == 0) {
- buf = curbuf;
- qf_jump(qi, 0, 0, eap->forceit);
- if (buf != curbuf)
- /* If we jumped to another buffer redrawing will already be
- * taken care of. */
- redraw_for_dummy = FALSE;
-
- /* Jump to the directory used after loading the buffer. */
- if (curbuf == first_match_buf && target_dir != NULL) {
- exarg_T ea;
-
- ea.arg = target_dir;
- ea.cmdidx = CMD_lcd;
- ex_cd(&ea);
- }
+ vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf,
+ target_dir);
}
} else
EMSG2(_(e_nomatch2), s);
@@ -3913,10 +4260,10 @@ static void restore_start_dir(char_u *dirname_start)
if (STRCMP(dirname_start, dirname_now) != 0) {
/* If the directory has changed, change it back by building up an
* appropriate ex command and executing it. */
- exarg_T ea;
-
- ea.arg = dirname_start;
- ea.cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd;
+ exarg_T ea = {
+ .arg = dirname_start,
+ .cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd,
+ };
ex_cd(&ea);
}
xfree(dirname_now);
@@ -4112,6 +4459,10 @@ int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
|| (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol)
== FAIL)
|| (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL)
+ || tv_dict_add_str(dict, S_LEN("module"),
+ (qfp->qf_module == NULL
+ ? ""
+ : (const char *)qfp->qf_module)) == FAIL
|| tv_dict_add_str(dict, S_LEN("pattern"),
(qfp->qf_pattern == NULL
? ""
@@ -4145,10 +4496,14 @@ enum {
QF_GETLIST_WINID = 0x8,
QF_GETLIST_CONTEXT = 0x10,
QF_GETLIST_ID = 0x20,
- QF_GETLIST_ALL = 0xFF
+ QF_GETLIST_IDX = 0x40,
+ QF_GETLIST_SIZE = 0x80,
+ QF_GETLIST_TICK = 0x100,
+ QF_GETLIST_ALL = 0x1FF
};
-// Parse text from 'di' and return the quickfix list items
+/// Parse text from 'di' and return the quickfix list items.
+/// Existing quickfix lists are not modified.
static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
{
int status = FAIL;
@@ -4186,35 +4541,85 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
return status;
}
-/// Return quickfix/location list details (title) as a
-/// dictionary. 'what' contains the details to return. If 'list_idx' is -1,
-/// then current list is used. Otherwise the specified list is used.
-int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
+// Return the quickfix/location list number with the given identifier.
+// Returns -1 if list is not found.
+static int qf_id2nr(const qf_info_T *const qi, const unsigned qfid)
{
- qf_info_T *qi = &ql_info;
- dictitem_T *di;
-
- if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
- return qf_get_list_from_lines(what, di, retdict);
+ for (int qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
+ if (qi->qf_lists[qf_idx].qf_id == qfid) {
+ return qf_idx;
+ }
}
+ return -1;
+}
- if (wp != NULL) {
- qi = GET_LOC_LIST(wp);
+/// Return the quickfix/location list window identifier in the current tabpage.
+static int qf_winid(qf_info_T *qi)
+{
+ // The quickfix window can be opened even if the quickfix list is not set
+ // using ":copen". This is not true for location lists.
+ if (qi == NULL) {
+ return 0;
}
- // List is not present or is empty
- if (qi == NULL || qi->qf_listcount == 0) {
- // If querying for the size of the list, return 0
- if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL)
- && (di->di_tv.v_type == VAR_STRING)
- && (STRCMP(di->di_tv.vval.v_string, "$") == 0)) {
- return tv_dict_add_nr(retdict, S_LEN("nr"), 0);
- }
- return FAIL;
+ win_T *win = qf_find_win(qi);
+ if (win != NULL) {
+ return win->handle;
}
+ return 0;
+}
- int status = OK;
+/// Convert the keys in 'what' to quickfix list property flags.
+static int qf_getprop_keys2flags(dict_T *what)
+{
int flags = QF_GETLIST_NONE;
+ if (tv_dict_find(what, S_LEN("all")) != NULL) {
+ flags |= QF_GETLIST_ALL;
+ }
+ if (tv_dict_find(what, S_LEN("title")) != NULL) {
+ flags |= QF_GETLIST_TITLE;
+ }
+ if (tv_dict_find(what, S_LEN("nr")) != NULL) {
+ flags |= QF_GETLIST_NR;
+ }
+ if (tv_dict_find(what, S_LEN("winid")) != NULL) {
+ flags |= QF_GETLIST_WINID;
+ }
+ if (tv_dict_find(what, S_LEN("context")) != NULL) {
+ flags |= QF_GETLIST_CONTEXT;
+ }
+ if (tv_dict_find(what, S_LEN("id")) != NULL) {
+ flags |= QF_GETLIST_ID;
+ }
+ if (tv_dict_find(what, S_LEN("items")) != NULL) {
+ flags |= QF_GETLIST_ITEMS;
+ }
+ if (tv_dict_find(what, S_LEN("idx")) != NULL) {
+ flags |= QF_GETLIST_IDX;
+ }
+ if (tv_dict_find(what, S_LEN("size")) != NULL) {
+ flags |= QF_GETLIST_SIZE;
+ }
+ if (tv_dict_find(what, S_LEN("changedtick")) != NULL) {
+ flags |= QF_GETLIST_TICK;
+ }
+
+ return flags;
+}
+
+/// Return the quickfix list index based on 'nr' or 'id' in 'what'.
+///
+/// If 'nr' and 'id' are not present in 'what' then return the current
+/// quickfix list index.
+/// If 'nr' is zero then return the current quickfix list index.
+/// If 'nr' is '$' then return the last quickfix list index.
+/// If 'id' is present then return the index of the quickfix list with that id.
+/// If 'id' is zero then return the quickfix list index specified by 'nr'.
+/// Return -1, if quickfix list is not present or if the stack is empty.
+static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what)
+{
+ dictitem_T *di = NULL;
+
int qf_idx = qi->qf_curlist; // default is the current list
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
// Use the specified quickfix/location list
@@ -4223,7 +4628,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (di->di_tv.vval.v_number != 0) {
qf_idx = (int)di->di_tv.vval.v_number - 1;
if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
- return FAIL;
+ qf_idx = -1;
}
}
} else if (di->di_tv.v_type == VAR_STRING
@@ -4231,9 +4636,8 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
// Get the last quickfix list number
qf_idx = qi->qf_listcount - 1;
} else {
- return FAIL;
+ qf_idx = -1;
}
- flags |= QF_GETLIST_NR;
}
if ((di = tv_dict_find(what, S_LEN("id"))) != NULL) {
@@ -4241,75 +4645,161 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (di->di_tv.v_type == VAR_NUMBER) {
// For zero, use the current list or the list specifed by 'nr'
if (di->di_tv.vval.v_number != 0) {
- for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
- if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) {
- break;
- }
- }
- if (qf_idx == qi->qf_listcount) {
- return FAIL; // List not found
- }
+ qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number);
}
- flags |= QF_GETLIST_ID;
} else {
- return FAIL;
+ qf_idx = -1;
}
}
- if (tv_dict_find(what, S_LEN("all")) != NULL) {
- flags |= QF_GETLIST_ALL;
+ return qf_idx;
+}
+
+/// Return default values for quickfix list properties in retdict.
+static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict)
+{
+ int status = OK;
+
+ if (flags & QF_GETLIST_TITLE) {
+ status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)"");
}
- if (tv_dict_find(what, S_LEN("title")) != NULL) {
- flags |= QF_GETLIST_TITLE;
+ if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ status = tv_dict_add_list(retdict, S_LEN("items"), l);
}
- if (tv_dict_find(what, S_LEN("winid")) != NULL) {
- flags |= QF_GETLIST_WINID;
+ if ((status == OK) && (flags & QF_GETLIST_NR)) {
+ status = tv_dict_add_nr(retdict, S_LEN("nr"), 0);
}
- if (tv_dict_find(what, S_LEN("context")) != NULL) {
- flags |= QF_GETLIST_CONTEXT;
+ if ((status == OK) && (flags & QF_GETLIST_WINID)) {
+ status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi));
}
- if (tv_dict_find(what, S_LEN("items")) != NULL) {
- flags |= QF_GETLIST_ITEMS;
+ if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
+ status = tv_dict_add_str(retdict, S_LEN("context"), (const char *)"");
+ }
+ if ((status == OK) && (flags & QF_GETLIST_ID)) {
+ status = tv_dict_add_nr(retdict, S_LEN("id"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_IDX)) {
+ status = tv_dict_add_nr(retdict, S_LEN("idx"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
+ status = tv_dict_add_nr(retdict, S_LEN("size"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_TICK)) {
+ status = tv_dict_add_nr(retdict, S_LEN("changedtick"), 0);
}
- if (flags & QF_GETLIST_TITLE) {
+ return status;
+}
+
+/// Return the quickfix list title as 'title' in retdict
+static int qf_getprop_title(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
char_u *t = qi->qf_lists[qf_idx].qf_title;
if (t == NULL) {
t = (char_u *)"";
}
- status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t);
+ return tv_dict_add_str(retdict, S_LEN("title"), (const char *)t);
+}
+
+/// Return the quickfix list items/entries as 'items' in retdict
+static int qf_getprop_items(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ get_errorlist(qi, NULL, qf_idx, l);
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+
+ return OK;
+}
+
+/// Return the quickfix list context (if any) as 'context' in retdict.
+static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ int status;
+
+ if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
+ dictitem_T *di = tv_dict_item_alloc_len(S_LEN("context"));
+ tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
+ status = tv_dict_add(retdict, di);
+ if (status == FAIL) {
+ tv_dict_item_free(di);
+ }
+ } else {
+ status = tv_dict_add_str(retdict, S_LEN("context"), "");
+ }
+
+ return status;
+}
+
+/// Return the quickfix list index as 'idx' in retdict
+static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ int idx = qi->qf_lists[qf_idx].qf_index;
+ if (qi->qf_lists[qf_idx].qf_count == 0) {
+ // For empty lists, qf_index is set to 1
+ idx = 0;
+ }
+ return tv_dict_add_nr(retdict, S_LEN("idx"), idx);
+}
+
+/// Return quickfix/location list details (title) as a dictionary.
+/// 'what' contains the details to return. If 'list_idx' is -1,
+/// then current list is used. Otherwise the specified list is used.
+int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
+{
+ qf_info_T *qi = &ql_info;
+ dictitem_T *di = NULL;
+ int status = OK;
+ int qf_idx;
+
+ if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
+ return qf_get_list_from_lines(what, di, retdict);
+ }
+
+ if (wp != NULL) {
+ qi = GET_LOC_LIST(wp);
+ }
+
+ int flags = qf_getprop_keys2flags(what);
+
+ if (qi != NULL && qi->qf_listcount != 0) {
+ qf_idx = qf_getprop_qfidx(qi, what);
+ }
+
+ // List is not present or is empty
+ if (qi == NULL || qi->qf_listcount == 0 || qf_idx == -1) {
+ return qf_getprop_defaults(qi, flags, retdict);
+ }
+
+ if (flags & QF_GETLIST_TITLE) {
+ status = qf_getprop_title(qi, qf_idx, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_NR)) {
status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1);
}
if ((status == OK) && (flags & QF_GETLIST_WINID)) {
- win_T *win = qf_find_win(qi);
- if (win != NULL) {
- status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle);
- }
+ status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi));
}
if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
- list_T *l = tv_list_alloc(kListLenMayKnow);
- (void)get_errorlist(qi, NULL, qf_idx, l);
- tv_dict_add_list(retdict, S_LEN("items"), l);
+ status = qf_getprop_items(qi, qf_idx, retdict);
}
-
if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
- if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
- di = tv_dict_item_alloc_len(S_LEN("context"));
- tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
- status = tv_dict_add(retdict, di);
- if (status == FAIL) {
- tv_dict_item_free(di);
- }
- } else {
- status = tv_dict_add_str(retdict, S_LEN("context"), "");
- }
+ status = qf_getprop_ctx(qi, qf_idx, retdict);
}
-
if ((status == OK) && (flags & QF_GETLIST_ID)) {
status = tv_dict_add_nr(retdict, S_LEN("id"), qi->qf_lists[qf_idx].qf_id);
}
+ if ((status == OK) && (flags & QF_GETLIST_IDX)) {
+ status = qf_getprop_idx(qi, qf_idx, retdict);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
+ status = tv_dict_add_nr(retdict, S_LEN("size"),
+ qi->qf_lists[qf_idx].qf_count);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_TICK)) {
+ status = tv_dict_add_nr(retdict, S_LEN("changedtick"),
+ qi->qf_lists[qf_idx].qf_changedtick);
+ }
return status;
}
@@ -4347,6 +4837,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
}
char *const filename = tv_dict_get_string(d, "filename", true);
+ char *const module = tv_dict_get_string(d, "module", true);
int bufnum = (int)tv_dict_get_number(d, "bufnr");
long lnum = (long)tv_dict_get_number(d, "lnum");
int col = (int)tv_dict_get_number(d, "col");
@@ -4384,6 +4875,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
qf_idx,
NULL, // dir
(char_u *)filename,
+ (char_u *)module,
bufnum,
(char_u *)text,
lnum,
@@ -4395,6 +4887,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
valid);
xfree(filename);
+ xfree(module);
xfree(pattern);
xfree(text);
@@ -4471,12 +4964,8 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
if (!newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) {
// Use the quickfix/location list with the specified id
if (di->di_tv.v_type == VAR_NUMBER) {
- for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
- if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) {
- break;
- }
- }
- if (qf_idx == qi->qf_listcount) {
+ qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number);
+ if (qf_idx == -1) {
return FAIL; // List not found
}
} else {
@@ -4508,6 +4997,13 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list,
title_save, action == ' ' ? 'a' : action);
+ if (action == 'r') {
+ // When replacing the quickfix list entries using
+ // qf_add_entries(), the title is set with a ':' prefix.
+ // Restore the title with the saved title.
+ xfree(qi->qf_lists[qf_idx].qf_title);
+ qi->qf_lists[qf_idx].qf_title = vim_strsave(title_save);
+ }
xfree(title_save);
}
}
@@ -4543,6 +5039,10 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
retval = OK;
}
+ if (retval == OK) {
+ qf_list_changed(qi, qf_idx);
+ }
+
return retval;
}
@@ -4624,11 +5124,15 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
retval = qf_set_properties(qi, what, action, title);
} else {
retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
+ if (retval == OK) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
}
return retval;
}
+/// Mark the context as in use for all the lists in a quickfix stack.
static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
{
bool abort = false;
@@ -4645,7 +5149,7 @@ static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
}
/// Mark the context of the quickfix list and the location lists (if present) as
-/// "in use". So that garabage collection doesn't free the context.
+/// "in use". So that garbage collection doesn't free the context.
bool set_ref_in_quickfix(int copyID)
{
bool abort = mark_quickfix_ctx(&ql_info, copyID);
@@ -4730,10 +5234,10 @@ void ex_cbuffer(exarg_T *eap)
eap->line2 = buf->b_ml.ml_line_count;
}
if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
- || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count)
+ || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count) {
EMSG(_(e_invrange));
- else {
- char_u *qf_title = *eap->cmdlinep;
+ } else {
+ char_u *qf_title = qf_cmdtitle(*eap->cmdlinep);
if (buf->b_sfname) {
vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
@@ -4741,17 +5245,20 @@ void ex_cbuffer(exarg_T *eap)
qf_title = IObuff;
}
- if (qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
- (eap->cmdidx != CMD_caddbuffer
- && eap->cmdidx != CMD_laddbuffer),
- eap->line1, eap->line2, qf_title, NULL) > 0) {
- if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
- curbuf->b_fname, true, curbuf);
- }
- if (eap->cmdidx == CMD_cbuffer || eap->cmdidx == CMD_lbuffer) {
- qf_jump(qi, 0, 0, eap->forceit); // display first error
- }
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
+ (eap->cmdidx != CMD_caddbuffer
+ && eap->cmdidx != CMD_laddbuffer),
+ eap->line1, eap->line2, qf_title, NULL);
+ if (res >= 0) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
+ curbuf->b_fname, true, curbuf);
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cbuffer
+ || eap->cmdidx == CMD_lbuffer)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
}
}
}
@@ -4766,11 +5273,6 @@ void ex_cexpr(exarg_T *eap)
qf_info_T *qi = &ql_info;
const char *au_name = NULL;
- if (eap->cmdidx == CMD_lexpr || eap->cmdidx == CMD_lgetexpr
- || eap->cmdidx == CMD_laddexpr) {
- qi = ll_get_or_alloc_list(curwin);
- }
-
switch (eap->cmdidx) {
case CMD_cexpr:
au_name = "cexpr";
@@ -4800,23 +5302,32 @@ void ex_cexpr(exarg_T *eap)
}
}
+ if (eap->cmdidx == CMD_lexpr
+ || eap->cmdidx == CMD_lgetexpr
+ || eap->cmdidx == CMD_laddexpr) {
+ qi = ll_get_or_alloc_list(curwin);
+ }
+
/* Evaluate the expression. When the result is a string or a list we can
* use it to fill the errorlist. */
typval_T tv;
if (eval0(eap->arg, &tv, NULL, true) != FAIL) {
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
|| tv.v_type == VAR_LIST) {
- if (qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
- (eap->cmdidx != CMD_caddexpr
- && eap->cmdidx != CMD_laddexpr),
- (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL) > 0) {
- if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
- curbuf->b_fname, true, curbuf);
- }
- if (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr) {
- qf_jump(qi, 0, 0, eap->forceit); // display first error
- }
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
+ (eap->cmdidx != CMD_caddexpr
+ && eap->cmdidx != CMD_laddexpr),
+ (linenr_T)0, (linenr_T)0,
+ qf_cmdtitle(*eap->cmdlinep), NULL);
+ if (res >= 0) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
+ curbuf->b_fname, true, curbuf);
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
}
} else {
EMSG(_("E777: String or List expected"));
@@ -4890,16 +5401,13 @@ void ex_helpgrep(exarg_T *eap)
}
}
- // Autocommands may change the list. Save it for later comparison
- qf_info_T *save_qi = qi;
-
regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING);
regmatch.rm_ic = FALSE;
if (regmatch.regprog != NULL) {
// Create a new quickfix list.
- qf_new_list(qi, *eap->cmdlinep);
+ qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep));
- /* Go through all directories in 'runtimepath' */
+ // Go through all the directories in 'runtimepath'
p = p_rtp;
while (*p != NUL && !got_int) {
copy_option_part(&p, NameBuff, MAXPATHL, ",");
@@ -4914,15 +5422,15 @@ void ex_helpgrep(exarg_T *eap)
if (gen_expand_wildcards(1, buff_list, &fcount,
&fnames, EW_FILE|EW_SILENT) == OK
&& fcount > 0) {
- for (fi = 0; fi < fcount && !got_int; ++fi) {
- /* Skip files for a different language. */
+ for (fi = 0; fi < fcount && !got_int; fi++) {
+ // Skip files for a different language.
if (lang != NULL
- && STRNICMP(lang, fnames[fi]
- + STRLEN(fnames[fi]) - 3, 2) != 0
+ && STRNICMP(lang, fnames[fi] + STRLEN(fnames[fi]) - 3, 2) != 0
&& !(STRNICMP(lang, "en", 2) == 0
&& STRNICMP("txt", fnames[fi]
- + STRLEN(fnames[fi]) - 3, 3) == 0))
+ + STRLEN(fnames[fi]) - 3, 3) == 0)) {
continue;
+ }
fd = mch_fopen((char *)fnames[fi], "r");
if (fd != NULL) {
lnum = 1;
@@ -4939,6 +5447,7 @@ void ex_helpgrep(exarg_T *eap)
qi->qf_curlist,
NULL, // dir
fnames[fi],
+ NULL,
0,
line,
lnum,
@@ -4983,12 +5492,13 @@ void ex_helpgrep(exarg_T *eap)
/* Darn, some plugin changed the value. */
free_string_option(save_cpo);
+ qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, true, curbuf);
- if (!new_qi && qi != save_qi && qf_find_buf(qi) == NULL) {
+ if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL) {
// autocommands made "qi" invalid
return;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index ab1a7c7b3e..39ce7ff844 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -5098,8 +5098,6 @@ static int regmatch(
printf("Premature EOL\n");
#endif
}
- if (status == RA_FAIL)
- got_int = TRUE;
return status == RA_MATCH;
}
@@ -6963,10 +6961,11 @@ char_u *reg_submatch(int no)
return NULL;
}
- s = reg_getline_submatch(lnum) + rsm.sm_mmatch->startpos[no].col;
+ s = reg_getline_submatch(lnum);
if (s == NULL) { // anti-crash check, cannot happen?
break;
}
+ s += rsm.sm_mmatch->startpos[no].col;
if (rsm.sm_mmatch->endpos[no].lnum == lnum) {
// Within one line: take form start to end col.
len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col;
@@ -7225,7 +7224,8 @@ static void report_re_switch(char_u *pat)
/// @param nl
///
/// @return TRUE if there is a match, FALSE if not.
-static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
+static int vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col,
+ bool nl)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -7274,8 +7274,8 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
colnr_T col)
{
- regmatch_T regmatch = {.regprog = *prog, .rm_ic = ignore_case};
- int r = vim_regexec_both(&regmatch, line, col, false);
+ regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case };
+ int r = vim_regexec_string(&regmatch, line, col, false);
*prog = regmatch.regprog;
return r;
}
@@ -7284,7 +7284,7 @@ int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
// Return TRUE if there is a match, FALSE if not.
int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
{
- return vim_regexec_both(rmp, line, col, false);
+ return vim_regexec_string(rmp, line, col, false);
}
// Like vim_regexec(), but consider a "\n" in "line" to be a line break.
@@ -7292,7 +7292,7 @@ int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
// Return TRUE if there is a match, FALSE if not.
int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
{
- return vim_regexec_both(rmp, line, col, true);
+ return vim_regexec_string(rmp, line, col, true);
}
/// Match a regexp against multiple lines.
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 298d82fc6b..5e5b19b63f 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -94,10 +94,8 @@ typedef struct {
char_u program[1]; /* actually longer.. */
} bt_regprog_T;
-/*
- * Structure representing a NFA state.
- * A NFA state may have no outgoing edge, when it is a NFA_MATCH state.
- */
+// Structure representing a NFA state.
+// An NFA state may have no outgoing edge, when it is a NFA_MATCH state.
typedef struct nfa_state nfa_state_T;
struct nfa_state {
int c;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index e0e8820b87..ce7270ae65 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -4582,7 +4582,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
* "pim" is NULL or contains info about a Postponed Invisible Match (start
* position).
*/
-static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog, regsubs_T *submatch, regsubs_T *m, int **listids)
+static int recursive_regmatch(
+ nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
+ regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
{
int save_reginput_col = (int)(reginput - regline);
int save_reglnum = reglnum;
@@ -4665,8 +4667,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
if (nfa_ll_index == 1) {
/* Already calling nfa_regmatch() recursively. Save the lastlist[1]
* values and clear them. */
- if (*listids == NULL) {
+ if (*listids == NULL || *listids_len < nstate) {
+ xfree(*listids);
*listids = xmalloc(sizeof(**listids) * nstate);
+ *listids_len = nstate;
}
nfa_save_listids(prog, *listids);
need_restore = TRUE;
@@ -4964,13 +4968,15 @@ static int nfa_did_time_out(void)
///
/// When "nfa_endp" is not NULL it is a required end-of-match position.
///
-/// Return TRUE if there is a match, FALSE otherwise.
+/// Return TRUE if there is a match, FALSE if there is no match,
+/// NFA_TOO_EXPENSIVE if we end up with too many states.
/// When there is a match "submatch" contains the positions.
+///
/// Note: Caller must ensure that: start != NULL.
static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
regsubs_T *submatch, regsubs_T *m)
{
- int result;
+ int result = false;
int flag = 0;
bool go_to_nextline = false;
nfa_thread_T *t;
@@ -4979,6 +4985,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
nfa_list_T *thislist;
nfa_list_T *nextlist;
int *listids = NULL;
+ int listids_len = 0;
nfa_state_T *add_state;
bool add_here;
int add_count;
@@ -5271,7 +5278,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the invisible match, then what
// follows.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -5372,7 +5379,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the pattern.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -6079,8 +6086,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
fprintf(log_fd, "Postponed recursive nfa_regmatch()\n");
fprintf(log_fd, "\n");
#endif
- result = recursive_regmatch(pim->state, pim,
- prog, submatch, m, &listids);
+ result = recursive_regmatch(pim->state, pim, prog, submatch, m,
+ &listids, &listids_len);
pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH;
// for \@! and \@<! it is a match when the result is
// FALSE
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5255fd2a51..4c830bb256 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -363,7 +363,7 @@ void update_screen(int type)
need_wait_return = FALSE;
}
- if (type >= NOT_VALID) {
+ if (type >= CLEAR || !default_grid.valid) {
ui_comp_set_screen_valid(false);
}
win_ui_flush_positions();
@@ -621,6 +621,11 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+ // If we can compute a change in the automatic sizing of the sign column
+ // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
+ // figuring it out here so we can redraw the entire screen for it.
+ buf_signcols(buf);
+
type = wp->w_redr_type;
win_grid_alloc(wp);
@@ -673,8 +678,6 @@ static void win_update(win_T *wp)
mod_bot = wp->w_redraw_bot + 1;
else
mod_bot = 0;
- wp->w_redraw_top = 0; /* reset for next time */
- wp->w_redraw_bot = 0;
if (buf->b_mod_set) {
if (mod_top == 0 || mod_top > buf->b_mod_top) {
mod_top = buf->b_mod_top;
@@ -771,6 +774,8 @@ static void win_update(win_T *wp)
if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu)
mod_bot = MAXLNUM;
}
+ wp->w_redraw_top = 0; // reset for next time
+ wp->w_redraw_bot = 0;
/*
* When only displaying the lines at the top, set top_end. Used when
@@ -1130,6 +1135,9 @@ static void win_update(win_T *wp)
/* reset got_int, otherwise regexp won't work */
save_got_int = got_int;
got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
win_foldinfo.fi_level = 0;
/*
@@ -1456,7 +1464,7 @@ static void win_update(win_T *wp)
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
- win_draw_end(wp, '@', ' ', srow, wp->w_grid.Rows, HLF_AT);
+ win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, at_attr);
wp->w_botline = lnum;
}
} else {
@@ -1473,7 +1481,7 @@ static void win_update(win_T *wp)
if (row + j > wp->w_grid.Rows) {
j = wp->w_grid.Rows - row;
}
- win_draw_end(wp, i, i, row, row + (int)j, HLF_DED);
+ win_draw_end(wp, i, i, true, row, row + (int)j, HLF_DED);
row += j;
}
} else if (dollar_vcol == -1)
@@ -1481,12 +1489,14 @@ static void win_update(win_T *wp)
// make sure the rest of the screen is blank
// write the 'eob' character to rows that aren't part of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.Rows,
+ HLF_EOB);
}
if (wp->w_redr_type >= REDRAW_TOP) {
draw_vsep_win(wp, 0);
}
+ syn_set_timeout(NULL);
/* Reset the type of redrawing required, the window has been updated. */
wp->w_redr_type = 0;
@@ -1543,85 +1553,66 @@ int win_signcol_width(win_T *wp)
return 2;
}
-/*
- * Clear the rest of the window and mark the unused lines with "c1". use "c2"
- * as the filler character.
- */
-static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T hl)
+/// Call grid_fill() with columns adjusted for 'rightleft' if needed.
+/// Return the new offset.
+static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
+ int endrow, int attr)
{
- int n = 0;
-# define FDC_OFF n
- int fdc = compute_foldcolumn(wp, 0);
+ int nn = off + width;
- int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
+ if (nn > wp->w_grid.Columns) {
+ nn = wp->w_grid.Columns;
+ }
if (wp->w_p_rl) {
- // No check for cmdline window: should never be right-left.
- n = fdc;
+ grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - nn, W_ENDCOL(wp) - off,
+ c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr);
+ }
- if (n > 0) {
- // draw the fold column at the right
- if (n > wp->w_grid.Columns) {
- n = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - n,
- wp->w_grid.Columns, ' ', ' ', win_hl_attr(wp, HLF_FC));
- }
+ return nn;
+}
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
+/// Clear lines near the end of the window and mark the unused lines with "c1".
+/// Use "c2" as filler character.
+/// When "draw_margin" is true, then draw the sign/fold/number columns.
+static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row,
+ int endrow, hlf_T hl)
+{
+ int n = 0;
- // draw the sign column left of the fold column
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - nn,
- wp->w_grid.Columns - n, ' ', ' ', win_hl_attr(wp, HLF_SC));
- n = nn;
+ if (draw_margin) {
+ // draw the fold column
+ int fdc = compute_foldcolumn(wp, 0);
+ if (fdc > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow,
+ win_hl_attr(wp, HLF_FC));
}
-
- grid_fill(&wp->w_grid, row, endrow, 0, wp->w_grid.Columns - 1 - FDC_OFF,
- c2, c2, attr);
- grid_fill(&wp->w_grid, row, endrow,
- wp->w_grid.Columns - 1 - FDC_OFF, wp->w_grid.Columns - FDC_OFF,
- c1, c2, attr);
- } else {
- if (cmdwin_type != 0 && wp == curwin) {
- /* draw the cmdline character in the leftmost column */
- n = 1;
- if (n > wp->w_grid.Columns) {
- n = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, 0, n, cmdwin_type, ' ',
- win_hl_attr(wp, HLF_AT));
+ // draw the sign column
+ int count = win_signcol_count(wp);
+ if (count > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row,
+ endrow, win_hl_attr(wp, HLF_SC));
}
- if (fdc > 0) {
- int nn = n + fdc;
-
- // draw the fold column at the left
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ',
- win_hl_attr(wp, HLF_FC));
- n = nn;
+ // draw the number column
+ if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) {
+ n = win_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, row, endrow,
+ win_hl_attr(wp, HLF_N));
}
+ }
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
-
- // draw the sign column after the fold column
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ',
- win_hl_attr(wp, HLF_SC));
- n = nn;
- }
+ int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
- grid_fill(&wp->w_grid, row, endrow, FDC_OFF, wp->w_grid.Columns, c1, c2,
- attr);
+ if (wp->w_p_rl) {
+ grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n,
+ c2, c2, attr);
+ grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n,
+ c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.Columns, c1, c2, attr);
}
+
set_empty_rows(wp, row);
}
@@ -1773,10 +1764,10 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
// If signs are being displayed, add spaces.
- if (signcolumn_on(wp)) {
+ if (win_signcol_count(wp) > 0) {
len = wp->w_grid.Columns - col;
if (len > 0) {
- int len_max = win_signcol_width(wp);
+ int len_max = win_signcol_width(wp) * win_signcol_count(wp);
if (len > len_max) {
len = len_max;
}
@@ -2180,7 +2171,6 @@ win_line (
int vcol_off = 0; ///< offset for concealed characters
int did_wcol = false;
int match_conc = 0; ///< cchar for match functions
- int has_match_conc = 0; ///< match wants to conceal
int old_boguscols = 0;
# define VCOL_HLC (vcol - vcol_off)
# define FIX_FOR_BOGUSCOLS \
@@ -2202,7 +2192,7 @@ win_line (
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error) {
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) {
// Prepare for syntax highlighting in this line. When there is an
// error, stop syntax highlighting.
save_did_emsg = did_emsg;
@@ -2212,8 +2202,10 @@ win_line (
wp->w_s->b_syn_error = true;
} else {
did_emsg = save_did_emsg;
- has_syntax = true;
- extra_check = true;
+ if (!wp->w_s->b_syn_slow) {
+ has_syntax = true;
+ extra_check = true;
+ }
}
}
@@ -2377,31 +2369,34 @@ win_line (
filler_lines = wp->w_topfill;
filler_todo = filler_lines;
- // Cursor line highlighting for 'cursorline' in the current window. Not
- // when Visual mode is active, because it's not clear what is selected
- // then.
- if (wp->w_p_cul && lnum == wp->w_cursor.lnum
- && !(wp == curwin && VIsual_active)) {
- int cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
-
- // We make a compromise here (#7383):
- // * low-priority CursorLine if fg is not set
- // * high-priority ("same as Vim" priority) CursorLine if fg is set
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
+ // Cursor line highlighting for 'cursorline' in the current window.
+ if (wp->w_p_cul && lnum == wp->w_cursor.lnum) {
+ // Do not show the cursor line when Visual mode is active, because it's
+ // not clear what is selected then.
+ if (!(wp == curwin && VIsual_active)) {
+ int cul_attr = win_hl_attr(wp, HLF_CUL);
+ HlAttrs ae = syn_attr2entry(cul_attr);
+
+ // We make a compromise here (#7383):
+ // * low-priority CursorLine if fg is not set
+ // * high-priority ("same as Vim" priority) CursorLine if fg is set
+ if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
+ line_attr_lowprio = cul_attr;
} else {
- line_attr = cul_attr;
+ if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
+ && qf_current_entry(wp) == lnum) {
+ line_attr = hl_combine_attr(cul_attr, line_attr);
+ } else {
+ line_attr = cul_attr;
+ }
}
}
+ // Update w_last_cursorline even if Visual mode is active.
+ wp->w_last_cursorline = wp->w_cursor.lnum;
}
// If this line has a sign with line highlighting set line_attr.
- v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
+ v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1);
if (v != 0) {
line_attr = sign_get_attr((int)v, SIGN_LINEHL);
}
@@ -2450,7 +2445,9 @@ win_line (
}
if (wp->w_p_list) {
- if (curwin->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail) {
+ if (curwin->w_p_lcs_chars.space
+ || wp->w_p_lcs_chars.trail
+ || wp->w_p_lcs_chars.nbsp) {
extra_check = true;
}
// find start of trailing whitespace
@@ -2549,9 +2546,10 @@ win_line (
}
wp->w_cursor = pos;
- /* Need to restart syntax highlighting for this line. */
- if (has_syntax)
+ // Need to restart syntax highlighting for this line.
+ if (has_syntax) {
syntax_start(wp, lnum);
+ }
}
}
@@ -2597,6 +2595,9 @@ win_line (
}
next_search_hl(wp, shl, lnum, (colnr_T)v,
shl == &search_hl ? NULL : cur);
+ if (wp->w_s->b_syn_slow) {
+ has_syntax = false;
+ }
// Need to get the line again, a multi-line regexp may have made it
// invalid.
@@ -2651,9 +2652,11 @@ win_line (
extra_check = true;
}
+ int sign_idx = 0;
// Repeat for the whole displayed line.
for (;; ) {
- has_match_conc = 0;
+ int has_match_conc = 0; ///< match wants to conceal
+ bool did_decrement_ptr = false;
// Skip this quickly when working on the text.
if (draw_state != WL_LINE) {
if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
@@ -2691,7 +2694,8 @@ win_line (
draw_state = WL_SIGN;
/* Show the sign column when there are any signs in this
* buffer or when using Netbeans. */
- if (signcolumn_on(wp)) {
+ int count = win_signcol_count(wp);
+ if (count > 0) {
int text_sign;
// Draw cells with the sign value or blank.
c_extra = ' ';
@@ -2700,7 +2704,8 @@ win_line (
n_extra = win_signcol_width(wp);
if (row == startrow + filler_lines && filler_todo <= 0) {
- text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT);
+ text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT,
+ sign_idx, count);
if (text_sign != 0) {
p_extra = sign_get_text(text_sign);
int symbol_blen = (int)STRLEN(p_extra);
@@ -2718,6 +2723,11 @@ win_line (
char_attr = sign_get_attr(text_sign, SIGN_TEXT);
}
}
+
+ sign_idx++;
+ if (sign_idx < count) {
+ draw_state = WL_SIGN - 1;
+ }
}
}
@@ -2754,8 +2764,15 @@ win_line (
if (wp->w_skipcol > 0)
for (p_extra = extra; *p_extra == ' '; ++p_extra)
*p_extra = '-';
- if (wp->w_p_rl) /* reverse line numbers */
- rl_mirror(extra);
+ if (wp->w_p_rl) { // reverse line numbers
+ // like rl_mirror(), but keep the space at the end
+ char_u *p2 = skiptowhite(extra) - 1;
+ for (char_u *p1 = extra; p1 < p2; p1++, p2--) {
+ const int t = *p1;
+ *p1 = *p2;
+ *p2 = t;
+ }
+ }
p_extra = extra;
c_extra = NUL;
c_final = NUL;
@@ -2766,7 +2783,8 @@ win_line (
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
- int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL);
+ int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL,
+ 0, 1);
if (num_sign != 0) {
// :sign defined with "numhl" highlight.
char_attr = sign_get_attr(num_sign, SIGN_NUMHL);
@@ -2853,6 +2871,7 @@ win_line (
}
if (draw_state == WL_LINE - 1 && n_extra == 0) {
+ sign_idx = 0;
draw_state = WL_LINE;
if (saved_n_extra) {
/* Continue item from end of wrapped line. */
@@ -2936,8 +2955,11 @@ win_line (
shl->endcol = tmp_col;
}
shl->attr_cur = shl->attr;
- if (cur != NULL && syn_name2id((char_u *)"Conceal")
- == cur->hlg_id) {
+ // Match with the "Conceal" group results in hiding
+ // the match.
+ if (cur != NULL
+ && shl != &search_hl
+ && syn_name2id((char_u *)"Conceal") == cur->hlg_id) {
has_match_conc = v == (long)shl->startcol ? 2 : 1;
match_conc = cur->conceal_char;
} else {
@@ -3207,6 +3229,7 @@ win_line (
// Put pointer back so that the character will be
// displayed at the start of the next line.
ptr--;
+ did_decrement_ptr = true;
} else if (*ptr != NUL) {
ptr += mb_l - 1;
}
@@ -3255,16 +3278,18 @@ win_line (
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + v;
- if (!attr_pri)
+ if (!attr_pri) {
char_attr = syntax_attr;
- else
+ } else {
char_attr = hl_combine_attr(syntax_attr, char_attr);
- /* no concealing past the end of the line, it interferes
- * with line highlighting */
- if (c == NUL)
+ }
+ // no concealing past the end of the line, it interferes
+ // with line highlighting.
+ if (c == NUL) {
syntax_flags = 0;
- else
+ } else {
syntax_flags = get_syntax_info(&syntax_seqnr);
+ }
} else if (!attr_pri) {
char_attr = 0;
}
@@ -3356,7 +3381,7 @@ win_line (
}
if (wp->w_buffer->terminal) {
- char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
+ char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
}
// Found last space before word: check for line break.
@@ -3663,6 +3688,11 @@ win_line (
prev_syntax_id = 0;
is_concealing = FALSE;
}
+
+ if (n_skip > 0 && did_decrement_ptr) {
+ // not showing the '>', put pointer back to avoid getting stuck
+ ptr++;
+ }
}
/* In the cursor line and we may be concealing characters: correct
@@ -3971,8 +4001,10 @@ win_line (
break;
}
- // line continues beyond line end
- if (wp->w_p_lcs_chars.ext
+ // Show "extends" character from 'listchars' if beyond the line end and
+ // 'list' is set.
+ if (wp->w_p_lcs_chars.ext != NUL
+ && wp->w_p_list
&& !wp->w_p_wrap
&& filler_todo <= 0
&& (wp->w_p_rl ? col == 0 : col == grid->Columns - 1)
@@ -4192,11 +4224,9 @@ win_line (
) || lcs_eol_one == -1)
break;
- /* When the window is too narrow draw all "@" lines. */
- if (draw_state != WL_LINE
- && filler_todo <= 0
- ) {
- win_draw_end(wp, '@', ' ', row, wp->w_grid.Rows, HLF_AT);
+ // When the window is too narrow draw all "@" lines.
+ if (draw_state != WL_LINE && filler_todo <= 0) {
+ win_draw_end(wp, '@', ' ', true, row, wp->w_grid.Rows, HLF_AT);
row = endrow;
}
@@ -4813,13 +4843,13 @@ static void win_redr_status(win_T *wp)
p = NameBuff;
len = (int)STRLEN(p);
- if (wp->w_buffer->b_help
+ if (bt_help(wp->w_buffer)
|| wp->w_p_pvw
|| bufIsChanged(wp->w_buffer)
|| wp->w_buffer->b_p_ro) {
*(p + len++) = ' ';
}
- if (wp->w_buffer->b_help) {
+ if (bt_help(wp->w_buffer)) {
STRCPY(p + len, _("[Help]"));
len += (int)STRLEN(p + len);
}
@@ -6351,14 +6381,12 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
-/*
- * show the current mode and ruler
- *
- * If clear_cmdline is TRUE, clear the rest of the cmdline.
- * If clear_cmdline is FALSE there may be a message there that needs to be
- * cleared only if a mode is shown.
- * Return the length of the message (0 if no message).
- */
+// Show the current mode and ruler.
+//
+// If clear_cmdline is TRUE, clear the rest of the cmdline.
+// If clear_cmdline is FALSE there may be a message there that needs to be
+// cleared only if a mode is shown.
+// Return the length of the message (0 if no message).
int showmode(void)
{
int need_clear;
@@ -7151,8 +7179,12 @@ void screen_resize(int width, int height)
if (curwin->w_p_scb)
do_check_scrollbind(TRUE);
if (State & CMDLINE) {
+ redraw_popupmenu = false;
update_screen(NOT_VALID);
redrawcmdline();
+ if (pum_drawn()) {
+ cmdline_pum_display(false);
+ }
} else {
update_topline();
if (pum_drawn()) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 777ea07a21..6e00602e66 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -310,7 +310,7 @@ void free_search_patterns(void)
/// Save and restore the search pattern for incremental highlight search
/// feature.
///
-/// It's similar but different from save_search_patterns() and
+/// It's similar to but different from save_search_patterns() and
/// restore_search_patterns(), because the search pattern must be restored when
/// cancelling incremental searching even if it's called inside user functions.
void save_last_search_pattern(void)
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 8864301e4c..4440d3905f 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -856,13 +856,13 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len)
return 0;
}
-/// Check whether writing to shada file was disabled with -i NONE
+/// Check whether writing to shada file was disabled ("-i NONE" or "--clean").
///
/// @return true if it was disabled, false otherwise.
static bool shada_disabled(void)
FUNC_ATTR_PURE
{
- return used_shada_file != NULL && STRCMP(used_shada_file, "NONE") == 0;
+ return strequal(p_shadafile, "NONE");
}
/// Read ShaDa file
@@ -1542,14 +1542,14 @@ static char *shada_filename(const char *file)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
{
if (file == NULL || *file == NUL) {
- if (used_shada_file != NULL) {
- file = used_shada_file;
+ if (p_shadafile != NULL && *p_shadafile != NUL) {
+ file = p_shadafile;
} else {
if ((file = find_shada_parameter('n')) == NULL || *file == NUL) {
file = shada_get_default_file();
}
// XXX It used to be one level lower, so that whatever is in
- // `used_shada_file` was expanded. I intentionally moved it here
+ // `p_shadafile` was expanded. I intentionally moved it here
// because various expansions must have already be done by the shell.
// If shell is not performing them then they should be done in main.c
// where arguments are parsed, *not here*.
@@ -2739,8 +2739,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
// Initialize jump list
const void *jump_iter = NULL;
+ cleanup_jumplist(curwin, false);
setpcmark();
- cleanup_jumplist();
do {
xfmark_T fm;
jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 4443fd8a2e..b4f2709d45 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -13,6 +13,7 @@ struct signlist
linenr_T lnum; // line number which has this sign
int typenr; // typenr of sign
signlist_T *next; // next signlist entry
+ signlist_T *prev; // previous entry -- for easy reordering
};
// type argument for buf_getsigntype() and sign_get_attr()
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index ec4da88ea1..0fc33bec81 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -2294,7 +2294,7 @@ static void use_midword(slang_T *lp, win_T *wp)
}
// Find the region "region[2]" in "rp" (points to "sl_regions").
-// Each region is simply stored as the two characters of it's name.
+// Each region is simply stored as the two characters of its name.
// Returns the index if found (first is 0), REGION_ALL if not found.
static int find_region(char_u *rp, char_u *region)
{
@@ -7100,9 +7100,9 @@ void ex_spelldump(exarg_T *eap)
spell_dump_compl(NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0);
// Delete the empty line that we started with.
- if (curbuf->b_ml.ml_line_count > 1)
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
-
+ if (curbuf->b_ml.ml_line_count > 1) {
+ ml_delete(curbuf->b_ml.ml_line_count, false);
+ }
redraw_later(NOT_VALID);
}
@@ -7171,7 +7171,7 @@ spell_dump_compl (
if (do_region && region_names != NULL) {
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names);
- ml_append(lnum++, IObuff, (colnr_T)0, FALSE);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
} else
do_region = false;
@@ -7185,7 +7185,7 @@ spell_dump_compl (
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname);
- ml_append(lnum++, IObuff, (colnr_T)0, FALSE);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
// When matching with a pattern and there are no prefixes only use
@@ -7347,14 +7347,15 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int d
}
}
- ml_append(lnum, p, (colnr_T)0, FALSE);
+ ml_append(lnum, p, (colnr_T)0, false);
} else if (((dumpflags & DUMPFLAG_ICASE)
? mb_strnicmp(p, pat, STRLEN(pat)) == 0
: STRNCMP(p, pat, STRLEN(pat)) == 0)
&& ins_compl_add_infercase(p, (int)STRLEN(p),
- p_ic, NULL, *dir, 0) == OK)
+ p_ic, NULL, *dir, 0) == OK) {
// if dir was BACKWARD then honor it just once
*dir = FORWARD;
+ }
}
// For ":spelldump": Find matching prefixes for "word". Prepend each to
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 7a6f2fce39..117939e7e9 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -955,8 +955,9 @@ someerror:
break;
}
if (ml_append_buf(slang->sl_sugbuf, (linenr_T)wordnr,
- ga.ga_data, ga.ga_len, TRUE) == FAIL)
+ ga.ga_data, ga.ga_len, true) == FAIL) {
goto someerror;
+ }
}
ga_clear(&ga);
@@ -3227,7 +3228,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
prevp = p;
if (get_affitem(affile->af_flagtype, &p) != 0) {
// A flag is a postponed prefix flag if it appears in "af_pref"
- // and it's ID is not zero.
+ // and its ID is not zero.
STRLCPY(key, prevp, p - prevp + 1);
hi = hash_find(&affile->af_pref, key);
if (!HASHITEM_EMPTY(hi)) {
@@ -4920,9 +4921,10 @@ sug_filltable (
((char_u *)gap->ga_data)[gap->ga_len++] = NUL;
if (ml_append_buf(spin->si_spellbuf, (linenr_T)wordnr,
- gap->ga_data, gap->ga_len, TRUE) == FAIL)
+ gap->ga_data, gap->ga_len, true) == FAIL) {
return -1;
- ++wordnr;
+ }
+ wordnr++;
// Remove extra NUL entries, we no longer need them. We don't
// bother freeing the nodes, the won't be reused anyway.
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 96a8dfd295..3ba9354c67 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -678,12 +678,12 @@ static float_T tv_float(typval_T *const tvs, int *const idxp)
// are discarded. If "str_m" is greater than zero it is guaranteed
// the resulting string will be NUL-terminated.
-// vim_vsnprintf() can be invoked with either "va_list" or a list of
+// vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
// "typval_T". When the latter is not used it must be NULL.
/// Append a formatted value to the string
///
-/// @see vim_vsnprintf().
+/// @see vim_vsnprintf_typval().
int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
FUNC_ATTR_PRINTF(3, 4)
{
@@ -697,7 +697,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
}
va_list ap;
va_start(ap, fmt);
- const int str_l = vim_vsnprintf(str + len, space, fmt, ap, NULL);
+ const int str_l = vim_vsnprintf(str + len, space, fmt, ap);
va_end(ap);
return str_l;
}
@@ -715,7 +715,7 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- const int str_l = vim_vsnprintf(str, str_m, fmt, ap, NULL);
+ const int str_l = vim_vsnprintf(str, str_m, fmt, ap);
va_end(ap);
return str_l;
}
@@ -736,6 +736,10 @@ static const char *infinity_str(bool positive, char fmt_spec,
return table[idx];
}
+int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
+{
+ return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
+}
/// Write formatted value to the string
///
@@ -748,8 +752,8 @@ static const char *infinity_str(bool positive, char fmt_spec,
///
/// @return Number of bytes excluding NUL byte that would be written to the
/// string if str_m was greater or equal to the return value.
-int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
- typval_T *const tvs)
+int vim_vsnprintf_typval(
+ char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs)
{
size_t str_l = 0;
bool str_avail = str_l < str_m;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 0104f0d834..1b30161e94 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -356,15 +356,16 @@ static reg_extmatch_T *next_match_extmatch = NULL;
* The current state (within the line) of the recognition engine.
* When current_state.ga_itemsize is 0 the current state is invalid.
*/
-static win_T *syn_win; /* current window for highlighting */
-static buf_T *syn_buf; /* current buffer for highlighting */
-static synblock_T *syn_block; /* current buffer for highlighting */
-static linenr_T current_lnum = 0; /* lnum of current state */
-static colnr_T current_col = 0; /* column of current state */
-static int current_state_stored = 0; /* TRUE if stored current state
- * after setting current_finished */
-static int current_finished = 0; /* current line has been finished */
-static garray_T current_state /* current stack of state_items */
+static win_T *syn_win; // current window for highlighting
+static buf_T *syn_buf; // current buffer for highlighting
+static synblock_T *syn_block; // current buffer for highlighting
+static proftime_T *syn_tm; // timeout limit
+static linenr_T current_lnum = 0; // lnum of current state
+static colnr_T current_col = 0; // column of current state
+static int current_state_stored = 0; // TRUE if stored current state
+ // after setting current_finished
+static int current_finished = 0; // current line has been finished
+static garray_T current_state // current stack of state_items
= GA_EMPTY_INIT_VALUE;
static int16_t *current_next_list = NULL; // when non-zero, nextgroup list
static int current_next_flags = 0; // flags for current_next_list
@@ -375,7 +376,12 @@ static int current_line_id = 0; // unique number for current line
static int syn_time_on = FALSE;
# define IF_SYN_TIME(p) (p)
-
+// Set the timeout used for syntax highlighting.
+// Use NULL to reset, no timeout.
+void syn_set_timeout(proftime_T *tm)
+{
+ syn_tm = tm;
+}
/*
* Start the syntax recognition for a line. This function is normally called
@@ -2887,6 +2893,7 @@ static char_u *syn_getcurline(void)
static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st)
{
int r;
+ int timed_out = 0;
proftime_T pt;
const int l_syn_time_on = syn_time_on;
@@ -2902,7 +2909,8 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
}
rmp->rmm_maxcol = syn_buf->b_p_smc;
- r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL);
+ r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
+ syn_tm, &timed_out);
if (l_syn_time_on) {
pt = profile_end(pt);
@@ -2914,6 +2922,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (r > 0)
++st->match;
}
+ if (timed_out) {
+ syn_win->w_s->b_syn_slow = true;
+ }
if (r > 0) {
rmp->startpos[0].lnum += lnum;
@@ -3144,6 +3155,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
void syntax_clear(synblock_T *block)
{
block->b_syn_error = false; // clear previous error
+ block->b_syn_slow = false; // clear previous timeout
block->b_syn_ic = false; // Use case, by default
block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
block->b_syn_containedin = false;
@@ -5756,8 +5768,10 @@ int syn_get_foldlevel(win_T *wp, long lnum)
{
int level = 0;
- /* Return quickly when there are no fold items at all. */
- if (wp->w_s->b_syn_folditems != 0) {
+ // Return quickly when there are no fold items at all.
+ if (wp->w_s->b_syn_folditems != 0
+ && !wp->w_s->b_syn_error
+ && !wp->w_s->b_syn_slow) {
syntax_start(wp, lnum);
for (int i = 0; i < current_state.ga_len; ++i) {
@@ -5956,6 +5970,7 @@ static const char *highlight_init_both[] = {
"default link Substitute Search",
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
+ "default link NormalFloat Pmenu",
NULL
};
@@ -7445,7 +7460,7 @@ void highlight_attr_set_all(void)
void highlight_changed(void)
{
int id;
- char_u userhl[10];
+ char_u userhl[30]; // use 30 to avoid compiler warning
int id_SNC = -1;
int id_S = -1;
int hlcnt;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 410b9dfcbd..81af23f911 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -436,11 +436,15 @@ do_tag (
tagmatchname = vim_strsave(name);
}
- if (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
+ if (type == DT_SELECT || type == DT_JUMP
|| type == DT_LTAG) {
cur_match = MAXCOL - 1;
}
- max_num_matches = cur_match + 1;
+ if (type == DT_TAG) {
+ max_num_matches = MAXCOL;
+ } else {
+ max_num_matches = cur_match + 1;
+ }
/* when the argument starts with '/', use it as a regexp */
if (!no_regexp && *name == '/') {
@@ -495,7 +499,7 @@ do_tag (
if (type == DT_CSCOPE && num_matches > 1) {
cs_print_tags();
ask_for_selection = true;
- } else if (type == DT_TAG) {
+ } else if (type == DT_TAG && *tag != NUL) {
// If a count is supplied to the ":tag <name>" command, then
// jump to count'th matching tag.
cur_match = count > 0 ? count - 1 : 0;
@@ -1523,58 +1527,38 @@ line_read_in:
}
parse_line:
- /*
- * Figure out where the different strings are in this line.
- * For "normal" tags: Do a quick check if the tag matches.
- * This speeds up tag searching a lot!
- */
- if (orgpat.headlen
- ) {
+ // When the line is too long the NUL will not be in the
+ // last-but-one byte (see vim_fgets()).
+ // Has been reported for Mozilla JS with extremely long names.
+ // In that case we can't parse it and we ignore the line.
+ if (lbuf[LSIZE - 2] != NUL && !use_cscope) {
+ if (p_verbose >= 5) {
+ verbose_enter();
+ MSG(_("Ignoring long line in tags file"));
+ verbose_leave();
+ }
+ if (state != TS_LINEAR) {
+ // Avoid getting stuck.
+ linear = true;
+ state = TS_LINEAR;
+ vim_fseek(fp, search_info.low_offset, SEEK_SET);
+ }
+ continue;
+ }
+
+ // Figure out where the different strings are in this line.
+ // For "normal" tags: Do a quick check if the tag matches.
+ // This speeds up tag searching a lot!
+ if (orgpat.headlen) {
tagp.tagname = lbuf;
tagp.tagname_end = vim_strchr(lbuf, TAB);
- if (tagp.tagname_end == NULL)
- {
- if (vim_strchr(lbuf, NL) == NULL) {
- /* Truncated line, ignore it. Has been reported for
- * Mozilla JS with extremely long names. */
- if (p_verbose >= 5) {
- verbose_enter();
- MSG(_("Ignoring long line in tags file"));
- verbose_leave();
- }
- if (state != TS_LINEAR) {
- /* Avoid getting stuck. */
- linear = TRUE;
- state = TS_LINEAR;
- vim_fseek(fp, search_info.low_offset, SEEK_SET);
- }
- continue;
- }
-
- /* Corrupted tag line. */
- line_error = TRUE;
+ if (tagp.tagname_end == NULL) {
+ // Corrupted tag line.
+ line_error = true;
break;
}
/*
- * Check for old style static tag: "file:tag file .."
- */
- tagp.fname = NULL;
- for (p = lbuf; p < tagp.tagname_end; ++p) {
- if (*p == ':') {
- if (tagp.fname == NULL)
- tagp.fname = tagp.tagname_end + 1;
- if ( fnamencmp(lbuf, tagp.fname, p - lbuf) == 0
- && tagp.fname[p - lbuf] == TAB
- ) {
- /* found one */
- tagp.tagname = p + 1;
- break;
- }
- }
- }
-
- /*
* Skip this line if the length of the tag is different and
* there is no regexp, or the tag is too short.
*/
@@ -1673,11 +1657,8 @@ parse_line:
if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0)
continue;
- /*
- * Can be a matching tag, isolate the file name and command.
- */
- if (tagp.fname == NULL)
- tagp.fname = tagp.tagname_end + 1;
+ // Can be a matching tag, isolate the file name and command.
+ tagp.fname = tagp.tagname_end + 1;
tagp.fname_end = vim_strchr(tagp.fname, TAB);
tagp.command = tagp.fname_end + 1;
if (tagp.fname_end == NULL)
@@ -1744,19 +1725,12 @@ parse_line:
/* Don't change the ordering, always use the same table. */
mtt = MT_GL_OTH;
} else {
- /* Decide in which array to store this match. */
- is_current = test_for_current(
- tagp.fname, tagp.fname_end, tag_fname,
- buf_ffname);
- {
- if (tagp.tagname != lbuf)
- is_static = TRUE; /* detected static tag before */
- else
- is_static = test_for_static(&tagp);
- }
+ // Decide in which array to store this match.
+ is_current = test_for_current(tagp.fname, tagp.fname_end, tag_fname,
+ buf_ffname);
+ is_static = test_for_static(&tagp);
- /* decide in which of the sixteen tables to store this
- * match */
+ // Decide in which of the sixteen tables to store this match.
if (is_static) {
if (is_current)
mtt = MT_ST_CUR;
@@ -1858,9 +1832,9 @@ parse_line:
// Don't add identical matches.
// Add all cscope tags, because they are all listed.
// "mfp" is used as a hash key, there is a NUL byte to end
- // the part matters for comparing, more bytes may follow
- // after it. E.g. help tags store the priority after the
- // NUL.
+ // the part that matters for comparing, more bytes may
+ // follow after it. E.g. help tags store the priority
+ // after the NUL.
if (use_cscope) {
hash++;
} else {
@@ -1996,7 +1970,13 @@ static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
*/
static void found_tagfile_cb(char_u *fname, void *cookie)
{
- GA_APPEND(char_u *, &tag_fnames, vim_strsave(fname));
+ char_u *const tag_fname = vim_strsave(fname);
+
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(tag_fname);
+#endif
+ simplify_filename(tag_fname);
+ GA_APPEND(char_u *, &tag_fnames, tag_fname);
}
#if defined(EXITFREE)
@@ -2054,9 +2034,20 @@ get_tagfname (
++tnp->tn_hf_idx;
STRCPY(buf, p_hf);
STRCPY(path_tail(buf), "tags");
- } else
- STRLCPY(buf, ((char_u **)(tag_fnames.ga_data))[
- tnp->tn_hf_idx++], MAXPATHL);
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(buf);
+#endif
+ simplify_filename(buf);
+
+ for (int i = 0; i < tag_fnames.ga_len; i++) {
+ if (STRCMP(buf, ((char_u **)(tag_fnames.ga_data))[i]) == 0) {
+ return FAIL; // avoid duplicate file names
+ }
+ }
+ } else {
+ STRLCPY(buf, ((char_u **)(tag_fnames.ga_data))[tnp->tn_hf_idx++],
+ MAXPATHL);
+ }
return OK;
}
@@ -2188,25 +2179,9 @@ parse_tag_line (
*/
static bool test_for_static(tagptrs_T *tagp)
{
- char_u *p;
+ char_u *p;
- int len;
-
- /*
- * Check for old style static tag: "file:tag file .."
- */
- len = (int)(tagp->fname_end - tagp->fname);
- p = tagp->tagname + len;
- if ( p < tagp->tagname_end
- && *p == ':'
- && fnamencmp(tagp->tagname, tagp->fname, len) == 0) {
- tagp->tagname = p + 1;
- return TRUE;
- }
-
- /*
- * Check for new style static tag ":...<Tab>file:[<Tab>...]"
- */
+ // Check for new style static tag ":...<Tab>file:[<Tab>...]"
p = tagp->command;
while ((p = vim_strchr(p, '\t')) != NULL) {
++p;
@@ -2905,3 +2880,177 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
}
return ret;
}
+
+// Return information about 'tag' in dict 'retdict'.
+static void get_tag_details(taggy_T *tag, dict_T *retdict)
+{
+ list_T *pos;
+ fmark_T *fmark;
+
+ tv_dict_add_str(retdict, S_LEN("tagname"), (const char *)tag->tagname);
+ tv_dict_add_nr(retdict, S_LEN("matchnr"), tag->cur_match + 1);
+ tv_dict_add_nr(retdict, S_LEN("bufnr"), tag->cur_fnum);
+
+ pos = tv_list_alloc(4);
+ tv_dict_add_list(retdict, S_LEN("from"), pos);
+
+ fmark = &tag->fmark;
+ tv_list_append_number(pos,
+ (varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0));
+ tv_list_append_number(pos, (varnumber_T)fmark->mark.lnum);
+ tv_list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL
+ ? MAXCOL : fmark->mark.col + 1));
+ tv_list_append_number(pos, (varnumber_T)fmark->mark.coladd);
+}
+
+// Return the tag stack entries of the specified window 'wp' in dictionary
+// 'retdict'.
+void get_tagstack(win_T *wp, dict_T *retdict)
+{
+ list_T *l;
+ int i;
+ dict_T *d;
+
+ tv_dict_add_nr(retdict, S_LEN("length"), wp->w_tagstacklen);
+ tv_dict_add_nr(retdict, S_LEN("curidx"), wp->w_tagstackidx + 1);
+ l = tv_list_alloc(2);
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+
+ for (i = 0; i < wp->w_tagstacklen; i++) {
+ d = tv_dict_alloc();
+ tv_list_append_dict(l, d);
+ get_tag_details(&wp->w_tagstack[i], d);
+ }
+}
+
+// Free all the entries in the tag stack of the specified window
+static void tagstack_clear(win_T *wp)
+{
+ // Free the current tag stack
+ for (int i = 0; i < wp->w_tagstacklen; i++) {
+ xfree(wp->w_tagstack[i].tagname);
+ }
+ wp->w_tagstacklen = 0;
+ wp->w_tagstackidx = 0;
+}
+
+// Remove the oldest entry from the tag stack and shift the rest of
+// the entires to free up the top of the stack.
+static void tagstack_shift(win_T *wp)
+{
+ taggy_T *tagstack = wp->w_tagstack;
+ xfree(tagstack[0].tagname);
+ for (int i = 1; i < wp->w_tagstacklen; i++) {
+ tagstack[i - 1] = tagstack[i];
+ }
+ wp->w_tagstacklen--;
+}
+
+// Push a new item to the tag stack
+static void tagstack_push_item(
+ win_T *wp,
+ char_u *tagname,
+ int cur_fnum,
+ int cur_match,
+ pos_T mark,
+ int fnum)
+{
+ taggy_T *tagstack = wp->w_tagstack;
+ int idx = wp->w_tagstacklen; // top of the stack
+
+ // if the tagstack is full: remove the oldest entry
+ if (idx >= TAGSTACKSIZE) {
+ tagstack_shift(wp);
+ idx = TAGSTACKSIZE - 1;
+ }
+
+ wp->w_tagstacklen++;
+ tagstack[idx].tagname = tagname;
+ tagstack[idx].cur_fnum = cur_fnum;
+ tagstack[idx].cur_match = cur_match;
+ if (tagstack[idx].cur_match < 0) {
+ tagstack[idx].cur_match = 0;
+ }
+ tagstack[idx].fmark.mark = mark;
+ tagstack[idx].fmark.fnum = fnum;
+}
+
+// Add a list of items to the tag stack in the specified window
+static void tagstack_push_items(win_T *wp, list_T *l)
+{
+ listitem_T *li;
+ dictitem_T *di;
+ dict_T *itemdict;
+ char_u *tagname;
+ pos_T mark;
+ int fnum;
+
+ // Add one entry at a time to the tag stack
+ for (li = tv_list_first(l); li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
+ || TV_LIST_ITEM_TV(li)->vval.v_dict == NULL) {
+ continue; // Skip non-dict items
+ }
+ itemdict = TV_LIST_ITEM_TV(li)->vval.v_dict;
+
+ // parse 'from' for the cursor position before the tag jump
+ if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) {
+ continue;
+ }
+ if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) {
+ continue;
+ }
+ if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))
+ == NULL) {
+ continue;
+ }
+
+ if (mark.col > 0) {
+ mark.col--;
+ }
+ tagstack_push_item(wp, tagname,
+ (int)tv_dict_get_number(itemdict, "bufnr"),
+ (int)tv_dict_get_number(itemdict, "matchnr") - 1,
+ mark, fnum);
+ }
+}
+
+// Set the current index in the tag stack. Valid values are between 0
+// and the stack length (inclusive).
+static void tagstack_set_curidx(win_T *wp, int curidx)
+{
+ wp->w_tagstackidx = curidx;
+ if (wp->w_tagstackidx < 0) { // sanity check
+ wp->w_tagstackidx = 0;
+ }
+ if (wp->w_tagstackidx > wp->w_tagstacklen) {
+ wp->w_tagstackidx = wp->w_tagstacklen;
+ }
+}
+
+// Set the tag stack entries of the specified window.
+// 'action' is set to either 'a' for append or 'r' for replace.
+int set_tagstack(win_T *wp, dict_T *d, int action)
+{
+ dictitem_T *di;
+ list_T *l;
+
+ if ((di = tv_dict_find(d, "items", -1)) != NULL) {
+ if (di->di_tv.v_type != VAR_LIST) {
+ return FAIL;
+ }
+ l = di->di_tv.vval.v_list;
+
+ if (action == 'r') {
+ tagstack_clear(wp);
+ }
+
+ tagstack_push_items(wp, l);
+ }
+
+ if ((di = tv_dict_find(d, "curidx", -1)) != NULL) {
+ tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
+ }
+
+ return OK;
+}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index ffe650f416..d8d529d0f6 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -374,6 +374,7 @@ void terminal_enter(void)
TerminalState state, *s = &state;
memset(s, 0, sizeof(TerminalState));
s->term = buf->terminal;
+ stop_insert_mode = false;
// Ensure the terminal is properly sized. Ideally window size management
// code should always have resized the terminal already, but check here to
@@ -400,9 +401,9 @@ void terminal_enter(void)
showmode();
curwin->w_redr_status = true; // For mode() in statusline. #8323
ui_busy_start();
- redraw(false);
s->state.execute = terminal_execute;
+ s->state.check = terminal_check;
state_enter(&s->state);
restart_edit = 0;
@@ -415,8 +416,10 @@ void terminal_enter(void)
// draw the unfocused cursor
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
+ if (curbuf->terminal == s->term && !s->close) {
+ terminal_check_cursor();
+ }
unshowmode(true);
- redraw(curbuf->handle != s->term->buf_handle);
ui_busy_stop();
if (s->close) {
bool wipe = s->term->buf_handle != 0;
@@ -427,6 +430,45 @@ void terminal_enter(void)
}
}
+static void terminal_check_cursor(void)
+{
+ Terminal *term = curbuf->terminal;
+ curwin->w_wrow = term->cursor.row;
+ curwin->w_wcol = term->cursor.col + win_col_off(curwin);
+ curwin->w_cursor.lnum = MIN(curbuf->b_ml.ml_line_count,
+ row_to_linenr(term, term->cursor.row));
+ // Nudge cursor when returning to normal-mode.
+ int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1);
+ curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off);
+ curwin->w_cursor.coladd = 0;
+ mb_check_adjust_col(curwin);
+}
+
+// Function executed before each iteration of terminal mode.
+// Return:
+// 1 if the iteration should continue normally
+// 0 if the main loop must exit
+static int terminal_check(VimState *state)
+{
+ if (stop_insert_mode) {
+ return 0;
+ }
+
+ terminal_check_cursor();
+
+ if (must_redraw) {
+ update_screen(0);
+ }
+
+ if (need_maketitle) { // Update title in terminal-mode. #7248
+ maketitle();
+ }
+
+ setcursor();
+ ui_flush();
+ return 1;
+}
+
static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
@@ -878,15 +920,20 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_KINS: return VTERM_KEY_KP_0;
case K_K1: FALLTHROUGH;
case K_KEND: return VTERM_KEY_KP_1;
- case K_K2: return VTERM_KEY_KP_2;
+ case K_K2: FALLTHROUGH;
+ case K_KDOWN: return VTERM_KEY_KP_2;
case K_K3: FALLTHROUGH;
case K_KPAGEDOWN: return VTERM_KEY_KP_3;
- case K_K4: return VTERM_KEY_KP_4;
- case K_K5: return VTERM_KEY_KP_5;
- case K_K6: return VTERM_KEY_KP_6;
+ case K_K4: FALLTHROUGH;
+ case K_KLEFT: return VTERM_KEY_KP_4;
+ case K_K5: FALLTHROUGH;
+ case K_KORIGIN: return VTERM_KEY_KP_5;
+ case K_K6: FALLTHROUGH;
+ case K_KRIGHT: return VTERM_KEY_KP_6;
case K_K7: FALLTHROUGH;
case K_KHOME: return VTERM_KEY_KP_7;
- case K_K8: return VTERM_KEY_KP_8;
+ case K_K8: FALLTHROUGH;
+ case K_KUP: return VTERM_KEY_KP_8;
case K_K9: FALLTHROUGH;
case K_KPAGEUP: return VTERM_KEY_KP_9;
case K_KDEL: FALLTHROUGH;
@@ -977,6 +1024,9 @@ static bool send_mouse_event(Terminal *term, int c)
{
int row = mouse_row, col = mouse_col, grid = mouse_grid;
win_T *mouse_win = mouse_find_win(&grid, &row, &col);
+ if (mouse_win == NULL) {
+ goto end;
+ }
if (term->forward_mouse && mouse_win->w_buffer->terminal == term) {
// event in the terminal window and mouse events was enabled by the
@@ -1024,6 +1074,7 @@ static bool send_mouse_event(Terminal *term, int c)
return mouse_win == curwin;
}
+end:
ins_char_typebuf(c);
return true;
}
@@ -1128,11 +1179,7 @@ static void refresh_terminal(Terminal *term)
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
refresh_pending = false;
- if (exiting // Cannot redraw (requires event loop) during teardown/exit.
- || (State & CMDPREVIEW)
- // WM_LIST (^D) is not redrawn, unlike the normal wildmenu. So we must
- // skip redraws to keep it visible.
- || wild_menu_showing == WM_LIST) {
+ if (exiting) { // Cannot redraw (requires event loop) during teardown/exit.
return;
}
Terminal *term;
@@ -1142,12 +1189,8 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
map_foreach(invalidated_terminals, term, stub, {
refresh_terminal(term);
});
- bool any_visible = is_term_visible();
pmap_clear(ptr_t)(invalidated_terminals);
unblock_autocmds();
- if (any_visible) {
- redraw(true);
- }
}
static void refresh_size(Terminal *term, buf_T *buf)
@@ -1261,61 +1304,6 @@ static void refresh_screen(Terminal *term, buf_T *buf)
term->invalid_end = -1;
}
-/// @return true if any invalidated terminal buffer is visible to the user
-static bool is_term_visible(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->terminal
- && pmap_has(ptr_t)(invalidated_terminals, wp->w_buffer->terminal)) {
- return true;
- }
- }
- return false;
-}
-
-static void redraw(bool restore_cursor)
-{
- Terminal *term = curbuf->terminal;
- if (!term) {
- restore_cursor = true;
- }
-
- int save_row = 0;
- int save_col = 0;
- if (restore_cursor) {
- // save the current row/col to restore after updating screen when not
- // focused
- save_row = ui_current_row();
- save_col = ui_current_col();
- }
- block_autocmds();
-
- if (must_redraw) {
- update_screen(0);
- }
-
- if (need_maketitle) { // Update title in terminal-mode. #7248
- maketitle();
- }
-
- if (restore_cursor) {
- ui_cursor_goto(save_row, save_col);
- } else if (term) {
- curwin->w_wrow = term->cursor.row;
- curwin->w_wcol = term->cursor.col + win_col_off(curwin);
- curwin->w_cursor.lnum = MIN(curbuf->b_ml.ml_line_count,
- row_to_linenr(term, term->cursor.row));
- // Nudge cursor when returning to normal-mode.
- int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1);
- curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off);
- curwin->w_cursor.coladd = 0;
- mb_check_adjust_col(curwin);
- }
-
- unblock_autocmds();
- ui_flush();
-}
-
static void adjust_topline(Terminal *term, buf_T *buf, long added)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -1351,23 +1339,16 @@ static bool is_focused(Terminal *term)
return State & TERM_FOCUS && curbuf->terminal == term;
}
-#define GET_CONFIG_VALUE(k, o) \
- do { \
- Error err = ERROR_INIT; \
- /* Only called from terminal_open where curbuf->terminal is the */ \
- /* context */ \
- o = dict_get_value(curbuf->b_vars, cstr_as_string(k), &err); \
- api_clear_error(&err); \
- if (o.type == kObjectTypeNil) { \
- o = dict_get_value(&globvardict, cstr_as_string(k), &err); \
- api_clear_error(&err); \
- } \
- } while (0)
-
static char *get_config_string(char *key)
{
- Object obj;
- GET_CONFIG_VALUE(key, obj);
+ Error err = ERROR_INIT;
+ // Only called from terminal_open where curbuf->terminal is the context.
+ Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err);
+ api_clear_error(&err);
+ if (obj.type == kObjectTypeNil) {
+ obj = dict_get_value(&globvardict, cstr_as_string(key), &err);
+ api_clear_error(&err);
+ }
if (obj.type == kObjectTypeString) {
return obj.data.string.data;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index fac27aa346..7b1f0f59cc 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -14,8 +14,6 @@ export NVIM_PRG := $(NVIM_PRG)
export TMPDIR := $(abspath ../../../Xtest-tmpdir)
SCRIPTS_DEFAULT = \
- test14.out \
- test37.out \
test42.out \
test48.out \
test52.out \
@@ -23,7 +21,6 @@ SCRIPTS_DEFAULT = \
ifneq ($(OS),Windows_NT)
SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
- test17.out \
test49.out \
endif
@@ -50,8 +47,6 @@ NEW_TESTS_IGNORE := $(NEW_TESTS_IN_ALOT) $(NEW_TESTS_ALOT) \
NEW_TESTS ?= $(addsuffix .res,$(sort $(filter-out $(NEW_TESTS_IGNORE),$(basename $(notdir $(wildcard test_*.vim))))) $(NEW_TESTS_ALOT))
-SCRIPTS_GUI := test16.out
-
ifdef VALGRIND_GDB
VGDB := --vgdb=yes \
@@ -83,8 +78,6 @@ endif
nongui: nolog $(SCRIPTS) newtests report
-gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report
-
.gdbinit:
@echo "[OLDTEST-PREP] Setting up .gdbinit"
@echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit
@@ -102,7 +95,7 @@ report:
test1.out: $(NVIM_PRG)
-$(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out
+$(SCRIPTS): $(NVIM_PRG) test1.out
RM_ON_RUN := test.out X* viminfo
RM_ON_START := test.ok
@@ -164,5 +157,6 @@ newtestssilent: $(NEW_TESTS)
%.res: %.vim .gdbinit
@echo "[OLDTEST] Running" $*
+ @rm -rf $*.failed test.ok $(RM_ON_RUN)
@mkdir -p $(TMPDIR)
@/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim
diff --git a/src/nvim/testdir/load.vim b/src/nvim/testdir/load.vim
index 2e01338dd0..6369b8f45e 100644
--- a/src/nvim/testdir/load.vim
+++ b/src/nvim/testdir/load.vim
@@ -1,3 +1,5 @@
+" Also used by: test/functional/helpers.lua
+
function! s:load_factor() abort
let timeout = 200
let times = []
@@ -23,8 +25,8 @@ function! s:load_factor() abort
endfunction
" Compute load factor only once.
-let s:load_factor = s:load_factor()
+let g:test_load_factor = s:load_factor()
function! LoadAdjust(num) abort
- return float2nr(ceil(a:num * s:load_factor))
+ return float2nr(ceil(a:num * g:test_load_factor))
endfunction
diff --git a/src/nvim/testdir/test14.in b/src/nvim/testdir/test14.in
deleted file mode 100644
index bef2e45431..0000000000
--- a/src/nvim/testdir/test14.in
+++ /dev/null
@@ -1,94 +0,0 @@
-Tests for "vaBiB", end could be wrong.
-Also test ":s/pat/sub/" with different ~s in sub.
-Also test for ^Vxff and ^Vo123 in Insert mode.
-Also test "[m", "]m", "[M" and "]M"
-Also test search()
-
-STARTTEST
-/Start cursor here
-vaBiBD:?Bug?,/Piece/-2w! test.out
-/^- Bug
-:s/u/~u~/
-:s/i/~u~/
-:s/o/~~~/
-:.w >>test.out
-:let tt = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
-:exe "normal " . tt
-:unlet tt
-:.w >>test.out
-:set vb
-/^Piece
-2]maA:.w >>test.out
-j]maB:.w >>test.out
-]maC:.w >>test.out
-[maD:.w >>test.out
-k2[maE:.w >>test.out
-3[maF:.w >>test.out
-]MaG:.w >>test.out
-j2]MaH:.w >>test.out
-]M]MaI:.w >>test.out
-2[MaJ:.w >>test.out
-k[MaK:.w >>test.out
-3[MaL:.w >>test.out
-:"
-/^foobar
-:let startline = line('.')
-:call search('foobar', 'c')
-:call append(line('$'), line('.') - startline)
-j:call search('^$', 'c')
-:call append(line('$'), line('.') - startline)
-:call search('^$', 'bc')
-:call append(line('$'), line('.') - startline)
-/two
-:call search('.', 'c')
-:call append(line('$'), getline('.')[col('.') - 1:])
-:"
-/^substitute
-:s/foo/bar/
-:$put =@/
-/^substitute
-:keeppatterns s/asdf/xyz/
-:$put =@/
-/^substitute
-Y:$put =@0
-/bar /e
-:$put =@0
--:keeppatterns /xyz
-0dn:/^search()/,$w >>test.out
-:qa!
-ENDTEST
-
-- Bug in "vPPPP" on this text (Webb):
- {
- cmd;
- {
- cmd; /* <-- Start cursor here */
- {
- }
- }
- }
-
-Piece of Java
-{
- tt m1 {
- t1;
- } e1
-
- tt m2 {
- t2;
- } e2
-
- tt m3 {
- if (x)
- {
- t3;
- }
- } e3
-}
-
-foobar
-
-substitute foo asdf
-
-one two
-search()
diff --git a/src/nvim/testdir/test14.ok b/src/nvim/testdir/test14.ok
deleted file mode 100644
index 0aa2db3f97..0000000000
--- a/src/nvim/testdir/test14.ok
+++ /dev/null
@@ -1,26 +0,0 @@
-- Bug in "vPPPP" on this text (Webb):
- {
- }
-- Bug uuun "vPPPP" uuuuuuuuun this text (Webb):
-ABC !ag8
- tt m1 {A
- tt m2 {B
- tt m3 {C
- tt m3 {DC
- tt m1 {EA
-{F
- }G e1
- }H e3
-}I
- }JH e3
- }K e2
-{LF
-search()
-0
-1
-1
-two
-foo
-^substitute
-substitute bar xyz
-xyz
diff --git a/src/nvim/testdir/test16.in b/src/nvim/testdir/test16.in
deleted file mode 100644
index b2cd159a8c..0000000000
--- a/src/nvim/testdir/test16.in
+++ /dev/null
@@ -1,15 +0,0 @@
-Tests for resetting "secure" flag after GUI has started.
-For KDE set a font, empty 'guifont' may cause a hang.
-
-STARTTEST
-:if $DISPLAY == "" | e! test.ok | wq! test.out | endif
-:set exrc secure
-:if has("gui_kde")
-: set guifont=Courier\ 10\ Pitch/8/-1/5/50/0/0/0/0/0
-:endif
-:gui -f
-:.,$w! test.out
-:qa!
-ENDTEST
-
- just some text
diff --git a/src/nvim/testdir/test16.ok b/src/nvim/testdir/test16.ok
deleted file mode 100644
index 25e2eea5c0..0000000000
--- a/src/nvim/testdir/test16.ok
+++ /dev/null
@@ -1,2 +0,0 @@
-
- just some text
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
deleted file mode 100644
index 1a4ac6b6d1..0000000000
--- a/src/nvim/testdir/test17.in
+++ /dev/null
@@ -1,126 +0,0 @@
-Tests for:
-- "gf" on ${VAR},
-- ":checkpath!" with various 'include' settings.
-
-STARTTEST
-:set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
-:"
-:if has("unix")
-:let $CDIR = "."
-/CDIR
-:else
-:let $TDIR = "."
-/TDIR
-:endif
-:" Dummy writing for making that sure gf doesn't fail even if the current
-:" file is modified. It can be occurred when executing the following command
-:" directly on Windows without fixing the 'fileformat':
-:" > nmake -f Make_dos.mak test17.out
-:w! test.out
-gf
-:set ff=unix
-:w! test.out
-:brewind
-ENDTEST
-
- ${CDIR}/test17a.in
- $TDIR/test17a.in
-
-STARTTEST
-:" check for 'include' without \zs or \ze
-:lang C
-:call delete("./Xbase.a")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:e! Xdir1/dir2/foo.a
-i#include "bar.a":
-:w
-:e Xdir1/dir2/bar.a
-i#include "baz.a":
-:w
-:e Xdir1/dir2/baz.a
-i#include "foo.a":
-:w
-:e Xbase.a
-:set path=Xdir1/dir2
-i#include <foo.a>:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-ENDTEST
-
-STARTTEST
-:" check for 'include' with \zs and \ze
-:call delete("./Xbase.b")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:let &include='^\s*%inc\s*/\zs[^/]\+\ze'
-:function! DotsToSlashes()
-: return substitute(v:fname, '\.', '/', 'g') . '.b'
-:endfunction
-:let &includeexpr='DotsToSlashes()'
-:e! Xdir1/dir2/foo.b
-i%inc /bar/:
-:w
-:e Xdir1/dir2/bar.b
-i%inc /baz/:
-:w
-:e Xdir1/dir2/baz.b
-i%inc /foo/:
-:w
-:e Xbase.b
-:set path=Xdir1/dir2
-i%inc /foo/:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-ENDTEST
-
-STARTTEST
-:" check for 'include' with \zs and no \ze
-:call delete("./Xbase.c")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze'
-:function! StripNewlineChar()
-: if v:fname =~ '\n$'
-: return v:fname[:-2]
-: endif
-: return v:fname
-:endfunction
-:let &includeexpr='StripNewlineChar()'
-:e! Xdir1/dir2/foo.c
-i%inc bar.c:
-:w
-:e Xdir1/dir2/bar.c
-i%inc baz.c:
-:w
-:e Xdir1/dir2/baz.c
-i%inc foo.c:
-:w
-:e Xdir1/dir2/FALSE.c
-i%inc foo.c:
-:w
-:e Xbase.c
-:set path=Xdir1/dir2
-i%inc FALSE.c foo.c:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-:" change "\" to "/" for Windows and fix 'fileformat'
-:e test.out
-:%s#\\#/#g
-:set ff&
-:w
-:q
-ENDTEST
-
diff --git a/src/nvim/testdir/test17.ok b/src/nvim/testdir/test17.ok
deleted file mode 100644
index b2a66d5f85..0000000000
--- a/src/nvim/testdir/test17.ok
+++ /dev/null
@@ -1,33 +0,0 @@
-This file is just to test "gf" in test 17.
-The contents is not important.
-Just testing!
-
-
---- Included files in path ---
-Xdir1/dir2/foo.a
-Xdir1/dir2/foo.a -->
- Xdir1/dir2/bar.a
- Xdir1/dir2/bar.a -->
- Xdir1/dir2/baz.a
- Xdir1/dir2/baz.a -->
- "foo.a" (Already listed)
-
-
---- Included files in path ---
-Xdir1/dir2/foo.b
-Xdir1/dir2/foo.b -->
- Xdir1/dir2/bar.b
- Xdir1/dir2/bar.b -->
- Xdir1/dir2/baz.b
- Xdir1/dir2/baz.b -->
- foo (Already listed)
-
-
---- Included files in path ---
-Xdir1/dir2/foo.c
-Xdir1/dir2/foo.c -->
- Xdir1/dir2/bar.c
- Xdir1/dir2/bar.c -->
- Xdir1/dir2/baz.c
- Xdir1/dir2/baz.c -->
- foo.c (Already listed)
diff --git a/src/nvim/testdir/test17a.in b/src/nvim/testdir/test17a.in
deleted file mode 100644
index 7e89364797..0000000000
--- a/src/nvim/testdir/test17a.in
+++ /dev/null
@@ -1,3 +0,0 @@
-This file is just to test "gf" in test 17.
-The contents is not important.
-Just testing!
diff --git a/src/nvim/testdir/test37.in b/src/nvim/testdir/test37.in
deleted file mode 100644
index 156bf74a10..0000000000
--- a/src/nvim/testdir/test37.in
+++ /dev/null
@@ -1,116 +0,0 @@
-Test for 'scrollbind'. <eralston@computer.org> Do not add a line below!
-STARTTEST
-:
-:set noscrollbind
-:set scrollopt=ver,jump
-:set scrolloff=2
-:set nowrap
-:set noequalalways
-:set splitbelow
-:" TEST using two windows open to one buffer, one extra empty window
-:split
-:new
-t:
-:resize 8
-/^start of window 1$/
-zt:
-:set scrollbind
-j:
-:resize 7
-/^start of window 2$/
-zt:
-:set scrollbind
-:" -- start of tests --
-:" TEST scrolling down
-L5jHyybpr0tHyybpr1tL6jHyybpr2kHyybpr3:
-:" TEST scrolling up
-tH4kjHtHyybpr4kHyybpr5k3ktHjHyybpr6tHyybpr7:
-:" TEST horizontal scrolling
-:set scrollopt+=hor
-gg"zyyG"zpGt015zly$bp"zpGky$bp"zpG:
-k10jH7zhg0y$bp"zpGtHg0y$bp"zpG:
-:set scrollopt-=hor
-:" ****** tests using two different buffers *****
-tj:
-:close
-t:
-:set noscrollbind
-:/^start of window 2$/,/^end of window 2$/y
-:new
-tj4"zpGp:
-t/^start of window 1$/
-zt:
-:set scrollbind
-j:
-/^start of window 2$/
-zt:
-:set scrollbind
-:" -- start of tests --
-:" TEST scrolling down
-L5jHyybpr0tHyybpr1tL6jHyybpr2kHyybpr3:
-:" TEST scrolling up
-tH4kjHtHyybpr4kHyybpr5k3ktHjHyybpr6tHyybpr7:
-:" TEST horizontal scrolling
-:set scrollopt+=hor
-gg"zyyG"zpGt015zly$bp"zpGky$bp"zpG:
-k10jH7zhg0y$bp"zpGtHg0y$bp"zpG:
-:set scrollopt-=hor
-:" TEST syncbind
-t:set noscb
-ggLj:set noscb
-ggL:set scb
-t:set scb
-GjG:syncbind
-HktHjHyybptyybp:
-t:set noscb
-ggLj:set noscb
-ggL:set scb
-t:set scb
-tGjGt:syncbind
-HkjHtHyybptjyybp:
-tH3kjHtHyybptjyybp:
-:" ***** done with tests *****
-:w! test.out " Write contents of this file
-:qa!
-ENDTEST
-
-
-start of window 1
-. line 01 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 01
-. line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-. line 03 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 03
-. line 04 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 04
-. line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-. line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-. line 07 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 07
-. line 08 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 08
-. line 09 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 09
-. line 10 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 10
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-. line 12 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 12
-. line 13 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 13
-. line 14 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 14
-. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15
-end of window 1
-
-
-start of window 2
-. line 01 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 01
-. line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-. line 03 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 03
-. line 04 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 04
-. line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-. line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-. line 07 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 07
-. line 08 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 08
-. line 09 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 09
-. line 10 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 10
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
-. line 13 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 13
-. line 14 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 14
-. line 15 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 15
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-end of window 2
-
-end of test37.in (please don't delete this line)
diff --git a/src/nvim/testdir/test37.ok b/src/nvim/testdir/test37.ok
deleted file mode 100644
index d0b74853b3..0000000000
--- a/src/nvim/testdir/test37.ok
+++ /dev/null
@@ -1,33 +0,0 @@
-
-0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-UTSRQPONMLKJIHGREDCBA9876543210 02
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-
-0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-UTSRQPONMLKJIHGREDCBA9876543210 02
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-:set scrollbind
-:set scrollbind
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-j:
-. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
diff --git a/src/nvim/testdir/test50.in b/src/nvim/testdir/test50.in
deleted file mode 100644
index 392177b808..0000000000
--- a/src/nvim/testdir/test50.in
+++ /dev/null
@@ -1,89 +0,0 @@
-Test for shortpathname ':8' extension.
-Only for use on Win32 systems!
-
-STARTTEST
-:fun! TestIt(file, bits, expected)
- let res=fnamemodify(a:file,a:bits)
- if a:expected == ''
- echo "'".a:file."'->(".a:bits.")->'".res."'"
- else
- if substitute(res,'/','\\', 'g') != substitute( a:expected, '/','\\', 'g')
- echo "FAILED: '".a:file."'->(".a:bits.")->'".res."'"
- echo "Expected: '".a:expected."'"
- else
- echo "OK"
- endif
- endif
-endfun
-:fun! MakeDir( dirname )
- "exe '!mkdir '.substitute(a:dirname,'/','\\','g')
- call system('mkdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! RMDir( dirname)
- "exe '!rmdir '.substitute(a:dirname,'/','\\','g')
- call system('rmdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! MakeFile( filename)
- "exe '!copy nul '.substitute(a:filename,'/','\\','g')
- call system('copy nul '.substitute(a:filename,'/','\\','g'))
-endfun
-:fun! TestColonEight()
- redir! >test.out
- " This could change for CygWin to //cygdrive/c
- let dir1='c:/x.x.y'
- if filereadable(dir1) || isdirectory(dir1)
- echo "FATAL: '".dir1."' exists, cannot run test"
- return
- endif
- let file1=dir1.'/zz.y.txt'
- let nofile1=dir1.'/z.y.txt'
- let dir2=dir1.'/VimIsTheGreatestSinceSlicedBread'
- let file2=dir2.'/z.txt'
- let nofile2=dir2.'/zz.txt'
- call MakeDir( dir1 )
- let resdir1 = substitute(fnamemodify(dir1, ':p:8'), '\\$', '', '')
- if resdir1 !~ '\V\^c:/XX\x\x\x\x~1.Y\$'
- echo "FATAL: unexpected short name: " . resdir1
- echo "INFO: please report your OS to vim-dev"
- return
- endif
- let resfile1=resdir1.'/ZZY~1.TXT'
- let resnofile1=resdir1.'/z.y.txt'
- let resdir2=resdir1.'/VIMIST~1'
- let resfile2=resdir2.'/z.txt'
- let resnofile2=resdir2.'/zz.txt'
- call MakeDir( dir2 )
- call MakeFile( file1 )
- call MakeFile( file2 )
- call TestIt(file1, ':p:8', resfile1)
- call TestIt(nofile1, ':p:8', resnofile1)
- call TestIt(file2, ':p:8', resfile2)
- call TestIt(nofile2, ':p:8', resnofile2)
- call TestIt(nofile2, ':p:8:h', fnamemodify(resnofile2,':h'))
- exe 'cd '.dir1
- call TestIt(file1, ':.:8', strpart(resfile1,strlen(resdir1)+1))
- call TestIt(nofile1, ':.:8', strpart(resnofile1,strlen(resdir1)+1))
- call TestIt(file2, ':.:8', strpart(resfile2,strlen(resdir1)+1))
- call TestIt(nofile2, ':.:8', strpart(resnofile2,strlen(resdir1)+1))
- let $HOME=dir1
- call TestIt(file1, ':~:8', '~'.strpart(resfile1,strlen(resdir1)))
- call TestIt(nofile1, ':~:8', '~'.strpart(resnofile1,strlen(resdir1)))
- call TestIt(file2, ':~:8', '~'.strpart(resfile2,strlen(resdir1)))
- call TestIt(nofile2, ':~:8', '~'.strpart(resnofile2,strlen(resdir1)))
- cd c:/
- call delete( file2 )
- call delete( file1 )
- call RMDir( dir2 )
- call RMDir( dir1 )
- echo
- redir END
-endfun
-:let dir = getcwd()
-:call TestColonEight()
-:exe "cd " . dir
-:edit! test.out
-:set ff=dos
-:w
-:qa!
-ENDTEST
-
diff --git a/src/nvim/testdir/test50.ok b/src/nvim/testdir/test50.ok
deleted file mode 100644
index 91ef1d6604..0000000000
--- a/src/nvim/testdir/test50.ok
+++ /dev/null
@@ -1,14 +0,0 @@
-
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 368fc9810d..3a9ffbdbf3 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -240,13 +240,53 @@ func Test_arglistid()
call assert_equal(0, arglistid())
endfunc
-" Test for argv()
+" Tests for argv() and argc()
func Test_argv()
call Reset_arglist()
call assert_equal([], argv())
call assert_equal("", argv(2))
+ call assert_equal(0, argc())
argadd a b c d
+ call assert_equal(4, argc())
call assert_equal('c', argv(2))
+
+ let w1_id = win_getid()
+ split
+ let w2_id = win_getid()
+ arglocal
+ args e f g
+ tabnew
+ let w3_id = win_getid()
+ split
+ let w4_id = win_getid()
+ argglobal
+ tabfirst
+ call assert_equal(4, argc(w1_id))
+ call assert_equal('b', argv(1, w1_id))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, w1_id))
+
+ call assert_equal(3, argc(w2_id))
+ call assert_equal('f', argv(1, w2_id))
+ call assert_equal(['e', 'f', 'g'], argv(-1, w2_id))
+
+ call assert_equal(3, argc(w3_id))
+ call assert_equal('e', argv(0, w3_id))
+ call assert_equal(['e', 'f', 'g'], argv(-1, w3_id))
+
+ call assert_equal(4, argc(w4_id))
+ call assert_equal('c', argv(2, w4_id))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, w4_id))
+
+ call assert_equal(4, argc(-1))
+ call assert_equal(3, argc())
+ call assert_equal('d', argv(3, -1))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, -1))
+ tabonly | only | enew!
+ " Negative test cases
+ call assert_equal(-1, argc(100))
+ call assert_equal('', argv(1, 100))
+ call assert_equal([], argv(-1, 100))
+ call assert_equal('', argv(10, -1))
endfunc
" Test for the :argedit command
@@ -289,6 +329,18 @@ func Test_argedit()
%argd
bwipe! C
bwipe! D
+
+ " :argedit reuses the current buffer if it is empty
+ %argd
+ " make sure to use a new buffer number for x when it is loaded
+ bw! x
+ new
+ let a = bufnr('')
+ argedit x
+ call assert_equal(a, bufnr(''))
+ call assert_equal('x', bufname(''))
+ %argd
+ bw! x
endfunc
" Test for the :argdelete command
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index f1fb8e67b9..8182c6973b 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -34,6 +34,28 @@ if has('timers')
call timer_start(LoadAdjust(100), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
+ unlet g:triggered
+ au! CursorHoldI
+ set updatetime&
+ endfunc
+
+ func Test_cursorhold_insert_with_timer_interrupt()
+ if !has('job')
+ return
+ endif
+ " Need to move the cursor.
+ call feedkeys("ggG", "xt")
+
+ " Confirm the timer invoked in exit_cb of the job doesn't disturb
+ " CursorHoldI event.
+ let g:triggered = 0
+ au CursorHoldI * let g:triggered += 1
+ set updatetime=500
+ call job_start(has('win32') ? 'cmd /c echo:' : 'echo',
+ \ {'exit_cb': {j, s -> timer_start(1000, 'ExitInsertMode')}})
+ call feedkeys('a', 'x!')
+ call assert_equal(1, g:triggered)
+ unlet g:triggered
au! CursorHoldI
set updatetime&
endfunc
@@ -46,10 +68,35 @@ if has('timers')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
+ unlet g:triggered
au! CursorHoldI
set updatetime&
endfunc
-endif
+
+ func Test_OptionSet_modeline()
+ throw 'skipped: Nvim does not support test_override()'
+ call test_override('starting', 1)
+ au! OptionSet
+ augroup set_tabstop
+ au OptionSet tabstop call timer_start(1, {-> execute("echo 'Handler called'", "")})
+ augroup END
+ call writefile(['vim: set ts=7 sw=5 :', 'something'], 'XoptionsetModeline')
+ set modeline
+ let v:errmsg = ''
+ call assert_fails('split XoptionsetModeline', 'E12:')
+ call assert_equal(7, &ts)
+ call assert_equal('', v:errmsg)
+
+ augroup set_tabstop
+ au!
+ augroup END
+ bwipe!
+ set ts&
+ call delete('XoptionsetModeline')
+ call test_override('starting', 0)
+ endfunc
+
+endif "has('timers')
func Test_bufunload()
augroup test_bufunload_group
@@ -457,7 +504,7 @@ endfunc
func Test_OptionSet()
throw 'skipped: Nvim does not support test_override()'
- if !has("eval") || !has("autocmd") || !exists("+autochdir")
+ if !has("eval") || !exists("+autochdir")
return
endif
@@ -598,7 +645,7 @@ endfunc
func Test_OptionSet_diffmode()
throw 'skipped: Nvim does not support test_override()'
call test_override('starting', 1)
- " 18: Changing an option when enetering diff mode
+ " 18: Changing an option when entering diff mode
new
au OptionSet diff :let &l:cul=v:option_new
@@ -1161,23 +1208,23 @@ func Test_TextYankPost()
norm "ayiw
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
\g:event)
norm y_
call assert_equal(
- \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'},
+ \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'},
\g:event)
call feedkeys("\<C-V>y", 'x')
call assert_equal(
- \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"},
+ \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"},
\g:event)
norm "xciwbar
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
\g:event)
norm "bdiw
call assert_equal(
- \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
+ \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
\g:event)
call assert_equal({}, v:event)
@@ -1308,3 +1355,319 @@ func Test_Changed_FirstTime()
call delete('Xchanged.txt')
bwipe!
endfunc
+
+func Test_autocmd_nested()
+ let g:did_nested = 0
+ augroup Testing
+ au WinNew * edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(0, g:did_nested)
+ close
+ bwipe! somefile
+
+ " old nested argument still works
+ augroup Testing
+ au!
+ au WinNew * nested edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(1, g:did_nested)
+ close
+ bwipe! somefile
+
+ " New ++nested argument works
+ augroup Testing
+ au!
+ au WinNew * ++nested edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(1, g:did_nested)
+ close
+ bwipe! somefile
+
+ augroup Testing
+ au!
+ augroup END
+
+ call assert_fails('au WinNew * ++nested ++nested echo bad', 'E983:')
+ call assert_fails('au WinNew * nested nested echo bad', 'E983:')
+endfunc
+
+func Test_autocmd_once()
+ " Without ++once WinNew triggers twice
+ let g:did_split = 0
+ augroup Testing
+ au WinNew * let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(2, g:did_split)
+ call assert_true(exists('#WinNew'))
+ close
+ close
+
+ " With ++once WinNew triggers once
+ let g:did_split = 0
+ augroup Testing
+ au!
+ au WinNew * ++once let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(1, g:did_split)
+ call assert_false(exists('#WinNew'))
+ close
+ close
+
+ call assert_fails('au WinNew * ++once ++once echo bad', 'E983:')
+endfunc
+
+func Test_autocmd_bufreadpre()
+ new
+ let b:bufreadpre = 1
+ call append(0, range(100))
+ w! XAutocmdBufReadPre.txt
+ autocmd BufReadPre <buffer> :let b:bufreadpre += 1
+ norm! 50gg
+ sp
+ norm! 100gg
+ wincmd p
+ let g:wsv1 = winsaveview()
+ wincmd p
+ let g:wsv2 = winsaveview()
+ " triggers BufReadPre, should not move the cursor in either window
+ " The topline may change one line in a large window.
+ edit
+ call assert_inrange(g:wsv2.topline - 1, g:wsv2.topline + 1, winsaveview().topline)
+ call assert_equal(g:wsv2.lnum, winsaveview().lnum)
+ call assert_equal(2, b:bufreadpre)
+ wincmd p
+ call assert_equal(g:wsv1.topline, winsaveview().topline)
+ call assert_equal(g:wsv1.lnum, winsaveview().lnum)
+ call assert_equal(2, b:bufreadpre)
+ " Now set the cursor position in an BufReadPre autocommand
+ " (even though the position will be invalid, this should make Vim reset the
+ " cursor position in the other window.
+ wincmd p
+ 1
+ " won't do anything, but try to set the cursor on an invalid lnum
+ autocmd BufReadPre <buffer> :norm! 70gg
+ " triggers BufReadPre, should not move the cursor in either window
+ e
+ call assert_equal(1, winsaveview().topline)
+ call assert_equal(1, winsaveview().lnum)
+ call assert_equal(3, b:bufreadpre)
+ wincmd p
+ call assert_equal(g:wsv1.topline, winsaveview().topline)
+ call assert_equal(g:wsv1.lnum, winsaveview().lnum)
+ call assert_equal(3, b:bufreadpre)
+ close
+ close
+ call delete('XAutocmdBufReadPre.txt')
+endfunc
+
+" Tests for the following autocommands:
+" - FileWritePre writing a compressed file
+" - FileReadPost reading a compressed file
+" - BufNewFile reading a file template
+" - BufReadPre decompressing the file to be read
+" - FilterReadPre substituting characters in the temp file
+" - FilterReadPost substituting characters after filtering
+" - FileReadPre set options for decompression
+" - FileReadPost decompress the file
+func Test_ReadWrite_Autocmds()
+ " Run this test only on Unix-like systems and if gzip is available
+ if !has('unix') || !executable("gzip")
+ return
+ endif
+
+ " Make $GZIP empty, "-v" would cause trouble.
+ let $GZIP = ""
+
+ " Use a FileChangedShell autocommand to avoid a prompt for 'Xtestfile.gz'
+ " being modified outside of Vim (noticed on Solaris).
+ au FileChangedShell * echo 'caught FileChangedShell'
+
+ " Test for the FileReadPost, FileWritePre and FileWritePost autocmds
+ augroup Test1
+ au!
+ au FileWritePre *.gz '[,']!gzip
+ au FileWritePost *.gz undo
+ au FileReadPost *.gz '[,']!gzip -d
+ augroup END
+
+ new
+ set bin
+ call append(0, [
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ])
+ 1,9write! Xtestfile.gz
+ enew! | close
+
+ new
+ " Read and decompress the testfile
+ 0read Xtestfile.gz
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+ enew! | close
+
+ augroup Test1
+ au!
+ augroup END
+
+ " Test for the FileAppendPre and FileAppendPost autocmds
+ augroup Test2
+ au!
+ au BufNewFile *.c read Xtest.c
+ au FileAppendPre *.out '[,']s/new/NEW/
+ au FileAppendPost *.out !cat Xtest.c >> test.out
+ augroup END
+
+ call writefile(['/*', ' * Here is a new .c file', ' */'], 'Xtest.c')
+ new foo.c " should load Xtest.c
+ call assert_equal(['/*', ' * Here is a new .c file', ' */'], getline(2, 4))
+ w! >> test.out " append it to the output file
+
+ let contents = readfile('test.out')
+ call assert_equal(' * Here is a NEW .c file', contents[2])
+ call assert_equal(' * Here is a new .c file', contents[5])
+
+ call delete('test.out')
+ enew! | close
+ augroup Test2
+ au!
+ augroup END
+
+ " Test for the BufReadPre and BufReadPost autocmds
+ augroup Test3
+ au!
+ " setup autocommands to decompress before reading and re-compress
+ " afterwards
+ au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand("<afile>"))
+ au BufReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
+ au BufReadPost *.gz call rename(expand("<afile>"), expand("<afile>:r"))
+ au BufReadPost *.gz exe '!gzip ' . shellescape(expand("<afile>:r"))
+ augroup END
+
+ e! Xtestfile.gz " Edit compressed file
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+
+ w! >> test.out " Append it to the output file
+
+ augroup Test3
+ au!
+ augroup END
+
+ " Test for the FilterReadPre and FilterReadPost autocmds.
+ set shelltemp " need temp files here
+ augroup Test4
+ au!
+ au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t")
+ au FilterReadPre *.out exe 'silent !sed s/e/E/ ' . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>"))
+ au FilterReadPre *.out exe 'silent !rm ' . shellescape(expand("<afile>")) . '.t'
+ au FilterReadPost *.out '[,']s/x/X/g
+ augroup END
+
+ e! test.out " Edit the output file
+ 1,$!cat
+ call assert_equal([
+ \ 'linE 2 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 4 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 6 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 8 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 10 AbcdefghijklmnopqrstuvwXyz'
+ \ ], getline(1, 9))
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], readfile('test.out'))
+
+ augroup Test4
+ au!
+ augroup END
+ set shelltemp&vim
+
+ " Test for the FileReadPre and FileReadPost autocmds.
+ augroup Test5
+ au!
+ au FileReadPre *.gz exe 'silent !gzip -d ' . shellescape(expand("<afile>"))
+ au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
+ au FileReadPost *.gz '[,']s/l/L/
+ augroup END
+
+ new
+ 0r Xtestfile.gz " Read compressed file
+ call assert_equal([
+ \ 'Line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], readfile('Xtestfile.gz'))
+
+ augroup Test5
+ au!
+ augroup END
+
+ au! FileChangedShell
+ call delete('Xtestfile.gz')
+ call delete('Xtest.c')
+ call delete('test.out')
+endfunc
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index 770ed55b8d..4436ebbf31 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -24,7 +24,10 @@ func Test_cd_no_arg()
call assert_equal(path, getcwd())
else
" Test that cd without argument echoes cwd on non-Unix systems.
+ let shellslash = &shellslash
+ set shellslash
call assert_match(getcwd(), execute('cd'))
+ let &shellslash = shellslash
endif
endfunc
diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim
new file mode 100644
index 0000000000..a7557a107d
--- /dev/null
+++ b/src/nvim/testdir/test_checkpath.vim
@@ -0,0 +1,113 @@
+" Tests for the :checkpath command
+
+" Test for 'include' without \zs or \ze
+func Test_checkpath1()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['#include "bar.a"'], 'Xdir1/dir2/foo.a')
+ call writefile(['#include "baz.a"'], 'Xdir1/dir2/bar.a')
+ call writefile(['#include "foo.a"'], 'Xdir1/dir2/baz.a')
+ call writefile(['#include <foo.a>'], 'Xbase.a')
+
+ edit Xbase.a
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.a',
+ \ 'Xdir1/dir2/foo.a -->',
+ \ ' Xdir1/dir2/bar.a',
+ \ ' Xdir1/dir2/bar.a -->',
+ \ ' Xdir1/dir2/baz.a',
+ \ ' Xdir1/dir2/baz.a -->',
+ \ ' "foo.a" (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.a")
+ call delete("Xdir1", "rf")
+ set path&
+ let &shellslash = save_shellslash
+endfunc
+
+func DotsToSlashes()
+ return substitute(v:fname, '\.', '/', 'g') . '.b'
+endfunc
+
+" Test for 'include' with \zs and \ze
+func Test_checkpath2()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['%inc /bar/'], 'Xdir1/dir2/foo.b')
+ call writefile(['%inc /baz/'], 'Xdir1/dir2/bar.b')
+ call writefile(['%inc /foo/'], 'Xdir1/dir2/baz.b')
+ call writefile(['%inc /foo/'], 'Xbase.b')
+
+ let &include='^\s*%inc\s*/\zs[^/]\+\ze'
+ let &includeexpr='DotsToSlashes()'
+
+ edit Xbase.b
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.b',
+ \ 'Xdir1/dir2/foo.b -->',
+ \ ' Xdir1/dir2/bar.b',
+ \ ' Xdir1/dir2/bar.b -->',
+ \ ' Xdir1/dir2/baz.b',
+ \ ' Xdir1/dir2/baz.b -->',
+ \ ' foo (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.b")
+ call delete("Xdir1", "rf")
+ set path&
+ set include&
+ set includeexpr&
+ let &shellslash = save_shellslash
+endfunc
+
+func StripNewlineChar()
+ if v:fname =~ '\n$'
+ return v:fname[:-2]
+ endif
+ return v:fname
+endfunc
+
+" Test for 'include' with \zs and no \ze
+func Test_checkpath3()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['%inc bar.c'], 'Xdir1/dir2/foo.c')
+ call writefile(['%inc baz.c'], 'Xdir1/dir2/bar.c')
+ call writefile(['%inc foo.c'], 'Xdir1/dir2/baz.c')
+ call writefile(['%inc foo.c'], 'Xdir1/dir2/FALSE.c')
+ call writefile(['%inc FALSE.c foo.c'], 'Xbase.c')
+
+ let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze'
+ let &includeexpr='StripNewlineChar()'
+
+ edit Xbase.c
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.c',
+ \ 'Xdir1/dir2/foo.c -->',
+ \ ' Xdir1/dir2/bar.c',
+ \ ' Xdir1/dir2/bar.c -->',
+ \ ' Xdir1/dir2/baz.c',
+ \ ' Xdir1/dir2/baz.c -->',
+ \ ' foo.c (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.c")
+ call delete("Xdir1", "rf")
+ set path&
+ set include&
+ set includeexpr&
+ let &shellslash = save_shellslash
+endfunc
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 60ef20e0b2..02840de743 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -35,7 +35,8 @@ func Test_client_server()
endif
" Takes a short while for the server to be active.
- call WaitFor('serverlist() =~ "' . name . '"')
+ " When using valgrind it takes much longer.
+ call WaitFor('serverlist() =~ "' . name . '"', 5000)
call assert_match(name, serverlist())
call remote_foreground(name)
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index 2d793ed88f..7262789ab4 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -173,7 +173,6 @@ func Test_command_count_4()
only!
exe bufnr . 'buf'
- bnext
let bufnr = bufnr('%')
let buffers = []
.,$-bufdo call add(buffers, bufnr('%'))
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
new file mode 100644
index 0000000000..db18f7695d
--- /dev/null
+++ b/src/nvim/testdir/test_debugger.vim
@@ -0,0 +1,232 @@
+" Tests for the Vim script debug commands
+
+source shared.vim
+" source screendump.vim
+
+" Run a Vim debugger command
+" If the expected output argument is supplied, then check for it.
+func RunDbgCmd(buf, cmd, ...)
+ call term_sendkeys(a:buf, a:cmd . "\r")
+ call term_wait(a:buf)
+
+ if a:0 != 0
+ " Verify the expected output
+ let lnum = 20 - len(a:1)
+ for l in a:1
+ call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))})
+ let lnum += 1
+ endfor
+ endif
+endfunc
+
+" Debugger tests
+func Test_Debugger()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ " Create a Vim script with some functions
+ call writefile([
+ \ 'func Foo()',
+ \ ' let var1 = 1',
+ \ ' let var2 = Bar(var1) + 9',
+ \ ' return var2',
+ \ 'endfunc',
+ \ 'func Bar(var)',
+ \ ' let var1 = 2 + a:var',
+ \ ' let var2 = Bazz(var1) + 4',
+ \ ' return var2',
+ \ 'endfunc',
+ \ 'func Bazz(var)',
+ \ ' let var1 = 3 + a:var',
+ \ ' let var3 = "another var"',
+ \ ' let var3 = "value2"',
+ \ ' let var3 = "value3"',
+ \ ' return var1',
+ \ 'endfunc'], 'Xtest.vim')
+
+ " Start Vim in a terminal
+ let buf = RunVimInTerminal('-S Xtest.vim', {})
+
+ " Start the Vim debugger
+ call RunDbgCmd(buf, ':debug echo Foo()')
+
+ " Create a few stack frames by stepping through functions
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+
+ " check backtrace
+ call RunDbgCmd(buf, 'backtrace', [
+ \ ' 2 function Foo[2]',
+ \ ' 1 Bar[2]',
+ \ '->0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+
+ " Check variables in different stack frames
+ call RunDbgCmd(buf, 'echo var1', ['6'])
+
+ call RunDbgCmd(buf, 'up')
+ call RunDbgCmd(buf, 'back', [
+ \ ' 2 function Foo[2]',
+ \ '->1 Bar[2]',
+ \ ' 0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+ call RunDbgCmd(buf, 'echo var1', ['3'])
+
+ call RunDbgCmd(buf, 'u')
+ call RunDbgCmd(buf, 'bt', [
+ \ '->2 function Foo[2]',
+ \ ' 1 Bar[2]',
+ \ ' 0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+ call RunDbgCmd(buf, 'echo var1', ['1'])
+
+ " Undefined variables
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'frame 2')
+ call RunDbgCmd(buf, 'echo var3', [
+ \ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:',
+ \ 'line 3:',
+ \ 'E121: Undefined variable: var3'])
+
+ " var3 is defined in this level with some other value
+ call RunDbgCmd(buf, 'fr 0')
+ call RunDbgCmd(buf, 'echo var3', ['another var'])
+
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step', [
+ \ 'function Foo[2]..Bar',
+ \ 'line 3: End of function'])
+ call RunDbgCmd(buf, 'up')
+
+ " Undefined var2
+ call RunDbgCmd(buf, 'echo var2', [
+ \ 'Error detected while processing function Foo[2]..Bar:',
+ \ 'line 3:',
+ \ 'E121: Undefined variable: var2'])
+
+ " Var2 is defined with 10
+ call RunDbgCmd(buf, 'down')
+ call RunDbgCmd(buf, 'echo var2', ['10'])
+
+ " Backtrace movements
+ call RunDbgCmd(buf, 'b', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ " next command cannot go down, we are on bottom
+ call RunDbgCmd(buf, 'down', ['frame is zero'])
+ call RunDbgCmd(buf, 'up')
+
+ " next command cannot go up, we are on top
+ call RunDbgCmd(buf, 'up', ['frame at highest level: 1'])
+ call RunDbgCmd(buf, 'where', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ " fil is not frame or finish, it is file
+ call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--'])
+
+ " relative backtrace movement
+ call RunDbgCmd(buf, 'fr -1')
+ call RunDbgCmd(buf, 'frame', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ call RunDbgCmd(buf, 'fr +1')
+ call RunDbgCmd(buf, 'fram', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ " go beyond limits does not crash
+ call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1'])
+ call RunDbgCmd(buf, 'fra', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ call RunDbgCmd(buf, 'frame -40', ['frame is zero'])
+ call RunDbgCmd(buf, 'fram', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ " final result 19
+ call RunDbgCmd(buf, 'cont', ['19'])
+
+ " breakpoints tests
+
+ " Start a debug session, so that reading the last line from the terminal
+ " works properly.
+ call RunDbgCmd(buf, ':debug echo Foo()')
+
+ " No breakpoints
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ " Place some breakpoints
+ call RunDbgCmd(buf, 'breaka func Bar')
+ call RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1'])
+ call RunDbgCmd(buf, 'breakadd func 3 Bazz')
+ call RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1',
+ \ ' 2 func Bazz line 3'])
+
+ " Check whether the breakpoints are hit
+ call RunDbgCmd(buf, 'cont', [
+ \ 'Breakpoint in "Bar" line 1',
+ \ 'function Foo[2]..Bar',
+ \ 'line 1: let var1 = 2 + a:var'])
+ call RunDbgCmd(buf, 'cont', [
+ \ 'Breakpoint in "Bazz" line 3',
+ \ 'function Foo[2]..Bar[2]..Bazz',
+ \ 'line 3: let var3 = "value2"'])
+
+ " Delete the breakpoints
+ call RunDbgCmd(buf, 'breakd 1')
+ call RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3'])
+ call RunDbgCmd(buf, 'breakdel func 3 Bazz')
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ call RunDbgCmd(buf, 'cont')
+
+ " Make sure the breakpoints are removed
+ call RunDbgCmd(buf, ':echo Foo()', ['19'])
+
+ " Delete a non-existing breakpoint
+ call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2'])
+
+ " Expression breakpoint
+ call RunDbgCmd(buf, ':breakadd func 2 Bazz')
+ call RunDbgCmd(buf, ':echo Bazz(1)')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'breaka expr var3')
+ call RunDbgCmd(buf, 'breakl', [' 4 expr var3'])
+ call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 4',
+ \ 'Oldval = "''another var''"',
+ \ 'Newval = "''value2''"',
+ \ 'function Bazz',
+ \ 'line 4: let var3 = "value3"'])
+
+ call RunDbgCmd(buf, 'breakdel *')
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ " finish the current function
+ call RunDbgCmd(buf, 'finish', [
+ \ 'function Bazz',
+ \ 'line 5: End of function'])
+ call RunDbgCmd(buf, 'cont')
+
+ call StopVimInTerminal(buf)
+
+ call delete('Xtest.vim')
+endfunc
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index ad3eec3274..00f4563f3d 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -527,7 +527,7 @@ func Test_setting_cursor()
new Xtest2
put =range(1,100)
wq
-
+
tabe Xtest2
$
diffsp Xtest1
@@ -672,6 +672,35 @@ func Test_diff_filler()
%bwipe!
endfunc
+func Test_diff_hlID()
+ new
+ call setline(1, [1, 2, 3])
+ diffthis
+ vnew
+ call setline(1, ['1x', 2, 'x', 3])
+ diffthis
+ redraw
+
+ call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "")
+
+ call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
+ call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
+ call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
+ call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText")
+ call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
+ call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
+ call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd")
+ call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "")
+
+ wincmd w
+ call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
+ call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
+ call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
+ call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "")
+
+ %bwipe!
+endfunc
+
func Test_diff_lastline()
enew!
only!
@@ -734,6 +763,9 @@ func Test_diff_of_diff()
call VerifyScreenDump(buf, 'Test_diff_of_diff_01', {})
+ call term_sendkeys(buf, ":set rightleft\<cr>")
+ call VerifyScreenDump(buf, 'Test_diff_of_diff_02', {})
+
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_diff_diff')
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index f8946c6929..7f3994300f 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -402,8 +402,19 @@ func! Test_edit_13()
call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix')
call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$'))
set smartindent& autoindent&
- bw!
+ bwipe!
endif
+
+ " Test autoindent removing indent of blank line.
+ new
+ call setline(1, ' foo bar baz')
+ set autoindent
+ exe "normal 0eea\<CR>\<CR>\<Esc>"
+ call assert_equal(" foo bar", getline(1))
+ call assert_equal("", getline(2))
+ call assert_equal(" baz", getline(3))
+ set autoindent&
+ bwipe!
endfunc
func! Test_edit_CR()
@@ -1374,9 +1385,26 @@ func Test_edit_complete_very_long_name()
return
endtry
- " Try to get the Vim window position before setting 'columns'.
+ " Try to get the Vim window position before setting 'columns', so that we can
+ " move the window back to where it was.
let winposx = getwinposx()
let winposy = getwinposy()
+
+ if winposx >= 0 && winposy >= 0 && !has('gui_running')
+ " We did get the window position, but xterm may report the wrong numbers.
+ " Move the window to the reported position and compute any offset.
+ exe 'winpos ' . winposx . ' ' . winposy
+ sleep 100m
+ let x = getwinposx()
+ if x >= 0
+ let winposx += winposx - x
+ endif
+ let y = getwinposy()
+ if y >= 0
+ let winposy += winposy - y
+ endif
+ endif
+
let save_columns = &columns
" Need at least about 1100 columns to reproduce the problem.
set columns=2000
@@ -1443,3 +1471,19 @@ func Test_leave_insert_autocmd()
au! InsertLeave
iunmap x
endfunc
+
+" Test for inserting characters using CTRL-V followed by a number.
+func Test_edit_special_chars()
+ new
+
+ if has("ebcdic")
+ let t = "o\<C-V>193\<C-V>xc2\<C-V>o303 \<C-V>90a\<C-V>xfg\<C-V>o578\<Esc>"
+ else
+ let t = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
+ endif
+
+ exe "normal " . t
+ call assert_equal("ABC !a\<C-O>g\<C-G>8", getline(2))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 111c85bb95..1850fb0cf1 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -21,3 +21,31 @@ func Test_E963()
call assert_fails("let v:oldfiles=''", 'E963:')
call assert_equal(v_o, v:oldfiles)
endfunc
+
+func Test_mkdir_p()
+ call mkdir('Xmkdir/nested', 'p')
+ call assert_true(isdirectory('Xmkdir/nested'))
+ try
+ " Trying to make existing directories doesn't error
+ call mkdir('Xmkdir', 'p')
+ call mkdir('Xmkdir/nested', 'p')
+ catch /E739:/
+ call assert_report('mkdir(..., "p") failed for an existing directory')
+ endtry
+ " 'p' doesn't suppress real errors
+ call writefile([], 'Xfile')
+ call assert_fails('call mkdir("Xfile", "p")', 'E739')
+ call delete('Xfile')
+ call delete('Xmkdir', 'rf')
+endfunc
+
+func Test_line_continuation()
+ let array = [5,
+ "\ ignore this
+ \ 6,
+ "\ more to ignore
+ "\ more moreto ignore
+ \ ]
+ "\ and some more
+ call assert_equal([5, 6], array)
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 5d4a0ff3cb..7a99a37be4 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -200,7 +200,7 @@ let s:filename_checks = {
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hostconf': ['/etc/host.conf'],
\ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny'],
- \ 'htmlcheetah': ['file.tmpl'],
+ \ 'template': ['file.tmpl'],
\ 'htmlm4': ['file.html.m4'],
\ 'httest': ['file.htt', 'file.htb'],
\ 'ibasic': ['file.iba', 'file.ibi'],
@@ -450,6 +450,7 @@ let s:filename_checks = {
\ 'tssgm': ['file.tssgm'],
\ 'tssop': ['file.tssop'],
\ 'twig': ['file.twig'],
+ \ 'typescript': ['file.ts'],
\ 'uc': ['file.uc'],
\ 'udevconf': ['/etc/udev/udev.conf'],
\ 'udevperm': ['/etc/udev/permissions.d/file.permissions'],
@@ -484,7 +485,7 @@ let s:filename_checks = {
\ 'xhtml': ['file.xhtml', 'file.xht'],
\ 'xinetd': ['/etc/xinetd.conf'],
\ 'xmath': ['file.msc', 'file.msf'],
- \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ts', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl'],
+ \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl'],
\ 'xmodmap': ['anyXmodmap'],
\ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
\ 'xpm2': ['file.xpm2'],
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index 0bae161a8b..f5488a6a27 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -133,6 +133,7 @@ func Test_finddir()
let save_shellslash = &shellslash
let save_dir = getcwd()
set path=,,
+ set shellslash
call CreateFiles()
cd Xdir1
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 824baffbc9..46c2d0f4cd 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -881,14 +881,25 @@ func Test_Executable()
call assert_equal(1, executable('notepad'))
call assert_equal(1, executable('notepad.exe'))
call assert_equal(0, executable('notepad.exe.exe'))
- call assert_equal(1, executable('shell32.dll'))
- call assert_equal(1, executable('win.ini'))
+ call assert_equal(0, executable('shell32.dll'))
+ call assert_equal(0, executable('win.ini'))
elseif has('unix')
call assert_equal(1, executable('cat'))
call assert_equal(0, executable('nodogshere'))
endif
endfunc
+func Test_executable_longname()
+ if !has('win32')
+ return
+ endif
+
+ let fname = 'X' . repeat('ã‚', 200) . '.bat'
+ call writefile([], fname)
+ call assert_equal(1, executable(fname))
+ call delete(fname)
+endfunc
+
func Test_hostname()
let hostname_vim = hostname()
if has('unix')
@@ -1049,3 +1060,19 @@ func Test_func_range_with_edit()
call delete('Xfuncrange2')
bwipe!
endfunc
+
+sandbox function Fsandbox()
+ normal ix
+endfunc
+
+func Test_func_sandbox()
+ sandbox let F = {-> 'hello'}
+ call assert_equal('hello', F())
+
+ sandbox let F = {-> execute("normal ix\<Esc>")}
+ call assert_fails('call F()', 'E48:')
+ unlet F
+
+ call assert_fails('call Fsandbox()', 'E48:')
+ delfunc Fsandbox
+endfunc
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index d233046d2b..accd21e9a3 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -64,3 +64,38 @@ func Test_gF()
bwipe Xfile
bwipe Xfile2
endfunc
+
+" Test for invoking 'gf' on a ${VAR} variable
+func Test_gf()
+ if has("ebcdic")
+ set isfname=@,240-249,/,.,-,_,+,,,$,:,~,{,}
+ else
+ set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
+ endif
+
+ call writefile(["Test for gf command"], "Xtest1")
+ if has("unix")
+ call writefile([" ${CDIR}/Xtest1"], "Xtestgf")
+ else
+ call writefile([" $TDIR/Xtest1"], "Xtestgf")
+ endif
+ new Xtestgf
+ if has("unix")
+ let $CDIR = "."
+ /CDIR
+ else
+ if has("amiga")
+ let $TDIR = "/testdir"
+ else
+ let $TDIR = "."
+ endif
+ /TDIR
+ endif
+
+ normal gf
+ call assert_equal('Xtest1', fnamemodify(bufname(''), ":t"))
+ close!
+
+ call delete('Xtest1')
+ call delete('Xtestgf')
+endfunc
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 33df79581c..e751fb5d16 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -129,10 +129,6 @@ func Test_highlight_eol_with_cursorline()
endfunc
func Test_highlight_eol_with_cursorline_vertsplit()
- if !has('vertsplit')
- return
- endif
-
let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine()
call NewWindow('topleft 5', 5)
@@ -533,3 +529,45 @@ func Test_termguicolors()
set t_Co=0
redraw
endfunc
+
+func Test_cursorline_after_yank()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call writefile([
+ \ 'set cul rnu',
+ \ 'call setline(1, ["","1","2","3",""])',
+ \ ], 'Xtest_cursorline_yank')
+ let buf = RunVimInTerminal('-S Xtest_cursorline_yank', {'rows': 8})
+ call term_wait(buf)
+ call term_sendkeys(buf, "Gy3k")
+ call term_wait(buf)
+ call term_sendkeys(buf, "jj")
+
+ call VerifyScreenDump(buf, 'Test_cursorline_yank_01', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_cursorline_yank')
+endfunc
+
+func Test_cursorline_with_visualmode()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call writefile([
+ \ 'set cul',
+ \ 'call setline(1, repeat(["abc"], 50))',
+ \ ], 'Xtest_cursorline_with_visualmode')
+ let buf = RunVimInTerminal('-S Xtest_cursorline_with_visualmode', {'rows': 12})
+ call term_wait(buf)
+ call term_sendkeys(buf, "V\<C-f>kkkjk")
+
+ call VerifyScreenDump(buf, 'Test_cursorline_with_visualmode_01', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_cursorline_with_visualmode')
+endfunc
diff --git a/src/nvim/testdir/test_jumplist.vim b/src/nvim/testdir/test_jumplist.vim
new file mode 100644
index 0000000000..be1af5e705
--- /dev/null
+++ b/src/nvim/testdir/test_jumplist.vim
@@ -0,0 +1,66 @@
+" Tests for the jumplist functionality
+
+" Tests for the getjumplist() function
+func Test_getjumplist()
+ if !has("jumplist")
+ return
+ endif
+
+ %bwipe
+ clearjumps
+ call assert_equal([[], 0], getjumplist())
+ call assert_equal([[], 0], getjumplist(1))
+ call assert_equal([[], 0], getjumplist(1, 1))
+
+ call assert_equal([], getjumplist(100))
+ call assert_equal([], getjumplist(1, 100))
+
+ let lines = []
+ for i in range(1, 100)
+ call add(lines, "Line " . i)
+ endfor
+ call writefile(lines, "Xtest")
+
+ " Jump around and create a jump list
+ edit Xtest
+ let bnr = bufnr('%')
+ normal 50%
+ normal G
+ normal gg
+
+ let expected = [[
+ \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 50, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 100, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 3]
+ call assert_equal(expected, getjumplist())
+ " jumplist doesn't change in between calls
+ call assert_equal(expected, getjumplist())
+
+ " Traverse the jump list and verify the results
+ 5
+ exe "normal \<C-O>"
+ call assert_equal(2, getjumplist(1)[1])
+ exe "normal 2\<C-O>"
+ call assert_equal(0, getjumplist(1, 1)[1])
+ exe "normal 3\<C-I>"
+ call assert_equal(3, getjumplist()[1])
+ exe "normal \<C-O>"
+ normal 20%
+ let expected = [[
+ \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 50, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 5, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 100, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 4]
+ call assert_equal(expected, getjumplist())
+ " jumplist doesn't change in between calls
+ call assert_equal(expected, getjumplist())
+
+ let l = getjumplist()
+ call test_garbagecollect_now()
+ call assert_equal(4, l[1])
+ clearjumps
+ call test_garbagecollect_now()
+ call assert_equal(4, l[1])
+
+ call delete("Xtest")
+endfunc
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index ada25da4a8..bc7817cef8 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -291,3 +291,9 @@ func Test_named_function_closure()
call garbagecollect()
call assert_equal(14, s:Abar())
endfunc
+
+func Test_lambda_with_index()
+ let List = {x -> [x]}
+ let Extract = {-> function(List, ['foobar'])()[0]}
+ call assert_equal('foobar', Extract())
+endfunc
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 4899f59910..57cfaa298e 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -90,6 +90,45 @@ func Test_listchars()
\ '.....h>-$',
\ 'iii<<<<><<$', '$'], l)
+
+ " test nbsp
+ normal ggdG
+ set listchars=nbsp:X,trail:Y
+ set list
+ " Non-breaking space
+ let nbsp = nr2char(0xa0)
+ call append(0, [ ">".nbsp."<" ])
+
+ let expected = '>X< '
+
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, virtcol('$')))
+
+ set listchars=nbsp:X
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, virtcol('$')))
+
+ " test extends
+ normal ggdG
+ set listchars=extends:Z
+ set nowrap
+ set nolist
+ call append(0, [ repeat('A', &columns + 1) ])
+
+ let expected = repeat('A', &columns)
+
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, &columns))
+
+ set list
+ let expected = expected[:-2] . 'Z'
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, &columns))
+
enew!
set listchars& ff&
endfunc
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 32593a423a..67f07ff6de 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -4,6 +4,8 @@ if !has('multi_byte')
finish
endif
+source shared.vim
+
func Test_abbreviation()
" abbreviation with 0x80 should work
inoreab чкпр vim
@@ -173,6 +175,9 @@ func Test_abbr_after_line_join()
endfunc
func Test_map_timeout()
+ if !has('timers')
+ return
+ endif
nnoremap aaaa :let got_aaaa = 1<CR>
nnoremap bb :let got_bb = 1<CR>
nmap b aaa
@@ -182,7 +187,7 @@ func Test_map_timeout()
call feedkeys("\<Esc>", "t")
endfunc
set timeout timeoutlen=200
- call timer_start(300, 'ExitInsert')
+ let timer = timer_start(300, 'ExitInsert')
" After the 'b' Vim waits for another character to see if it matches 'bb'.
" When it times out it is expanded to "aaa", but there is no wait for
" "aaaa". Can't check that reliably though.
@@ -197,6 +202,39 @@ func Test_map_timeout()
nunmap b
set timeoutlen&
delfunc ExitInsert
+ call timer_stop(timer)
+endfunc
+
+func Test_map_timeout_with_timer_interrupt()
+ if !has('job') || !has('timers')
+ return
+ endif
+
+ " Confirm the timer invoked in exit_cb of the job doesn't disturb mapped key
+ " sequence.
+ new
+ let g:val = 0
+ nnoremap \12 :let g:val = 1<CR>
+ nnoremap \123 :let g:val = 2<CR>
+ set timeout timeoutlen=1000
+
+ func ExitCb(job, status)
+ let g:timer = timer_start(1, {_ -> feedkeys("3\<Esc>", 't')})
+ endfunc
+
+ call job_start([&shell, &shellcmdflag, 'echo'], {'exit_cb': 'ExitCb'})
+ call feedkeys('\12', 'xt!')
+ call assert_equal(2, g:val)
+
+ bwipe!
+ nunmap \12
+ nunmap \123
+ set timeoutlen&
+ call WaitFor({-> exists('g:timer')})
+ call timer_stop(g:timer)
+ unlet g:timer
+ unlet g:val
+ delfunc ExitCb
endfunc
func Test_cabbr_visual_mode()
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index e608a2e58b..e926b946a9 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -202,6 +202,28 @@ func Test_matchaddpos()
set hlsearch&
endfunc
+func Test_matchaddpos_otherwin()
+ syntax on
+ new
+ call setline(1, ['12345', 'NP'])
+ let winid = win_getid()
+
+ wincmd w
+ call matchadd('Search', '4', 10, -1, {'window': winid})
+ call matchaddpos('Error', [[1,2], [2,2]], 10, -1, {'window': winid})
+ redraw!
+ call assert_notequal(screenattr(1,2), 0)
+ call assert_notequal(screenattr(1,4), 0)
+ call assert_notequal(screenattr(2,2), 0)
+ call assert_equal(screenattr(1,2), screenattr(2,2))
+ call assert_notequal(screenattr(1,2), screenattr(1,4))
+
+ wincmd w
+ bwipe!
+ call clearmatches()
+ syntax off
+endfunc
+
func Test_matchaddpos_using_negative_priority()
set hlsearch
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index c2e7bb7bf9..c790bd32e3 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -17,9 +17,9 @@ func Test_mksession()
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
- \ 'one ä multibyteCharacter',
- \ 'aä Ä two multiByte characters',
- \ 'Aäöü three mulTibyte characters',
+ \ 'one ä multibyteCharacter',
+ \ 'aä Ä two multiByte characters',
+ \ 'Aäöü three mulTibyte characters',
\ 'short line',
\ ])
let tmpfile = 'Xtemp'
@@ -240,13 +240,14 @@ endfunc
func Test_mksession_quote_in_filename()
let v:errmsg = ''
+ let filename = has('win32') ? 'x''y' : 'x''y"z'
%bwipe!
split another
- split x'y\"z
+ execute 'split' escape(filename, '"')
mksession! Xtest_mks_quoted.out
%bwipe!
source Xtest_mks_quoted.out
- call assert_true(bufexists("x'y\"z"))
+ call assert_true(bufexists(filename))
%bwipe!
call delete('Xtest_mks_quoted.out')
diff --git a/src/nvim/testdir/test_modeline.vim b/src/nvim/testdir/test_modeline.vim
new file mode 100644
index 0000000000..75fe1d993c
--- /dev/null
+++ b/src/nvim/testdir/test_modeline.vim
@@ -0,0 +1,84 @@
+func Test_modeline_invalid()
+ let modeline = &modeline
+ set modeline
+ call assert_fails('set Xmodeline', 'E518:')
+
+ let &modeline = modeline
+ bwipe!
+ call delete('Xmodeline')
+ endfunc
+
+func Test_modeline_filetype()
+ call writefile(['vim: set ft=c :', 'nothing'], 'Xmodeline_filetype')
+ let modeline = &modeline
+ set modeline
+ filetype plugin on
+ split Xmodeline_filetype
+ call assert_equal("c", &filetype)
+ call assert_equal(1, b:did_ftplugin)
+ call assert_equal("ccomplete#Complete", &ofu)
+
+ bwipe!
+ call delete('Xmodeline_filetype')
+ let &modeline = modeline
+ filetype plugin off
+endfunc
+
+func Test_modeline_syntax()
+ call writefile(['vim: set syn=c :', 'nothing'], 'Xmodeline_syntax')
+ let modeline = &modeline
+ set modeline
+ syntax enable
+ split Xmodeline_syntax
+ call assert_equal("c", &syntax)
+ call assert_equal("c", b:current_syntax)
+
+ bwipe!
+ call delete('Xmodeline_syntax')
+ let &modeline = modeline
+ syntax off
+endfunc
+
+func Test_modeline_keymap()
+ call writefile(['vim: set keymap=greek :', 'nothing'], 'Xmodeline_keymap')
+ let modeline = &modeline
+ set modeline
+ split Xmodeline_keymap
+ call assert_equal("greek", &keymap)
+ call assert_match('greek\|grk', b:keymap_name)
+
+ bwipe!
+ call delete('Xmodeline_keymap')
+ let &modeline = modeline
+ set keymap= iminsert=0 imsearch=-1
+endfunc
+
+func s:modeline_fails(what, text)
+ let fname = "Xmodeline_fails_" . a:what
+ call writefile(['vim: set ' . a:text . ' :', 'nothing'], fname)
+ let modeline = &modeline
+ set modeline
+ filetype plugin on
+ syntax enable
+ call assert_fails('split ' . fname, 'E474:')
+ call assert_equal("", &filetype)
+ call assert_equal("", &syntax)
+
+ bwipe!
+ call delete(fname)
+ let &modeline = modeline
+ filetype plugin off
+ syntax off
+endfunc
+
+func Test_modeline_filetype_fails()
+ call s:modeline_fails('filetype', 'ft=evil$CMD')
+endfunc
+
+func Test_modeline_syntax_fails()
+ call s:modeline_fails('syntax', 'syn=evil$CMD')
+endfunc
+
+func Test_modeline_keymap_fails()
+ call s:modeline_fails('keymap', 'keymap=evil$CMD')
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index d07b3fdbce..ef17209f74 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -82,7 +82,6 @@ fun! Test_normal00_optrans()
endfunc
func! Test_normal01_keymodel()
- throw "skipped: Nvim regression: 'keymodel'"
call Setup_NewWindow()
" Test 1: depending on 'keymodel' <s-down> does something different
50
@@ -1353,11 +1352,21 @@ func! Test_normal23_K()
bw!
return
endif
- set keywordprg=man\ --pager=cat
+
+ if has('mac')
+ " In MacOS, the option for specifying a pager is different
+ set keywordprg=man\ -P\ cat
+ else
+ set keywordprg=man\ --pager=cat
+ endif
" Test for using man
2
let a = execute('unsilent norm! K')
- call assert_match("man --pager=cat 'man'", a)
+ if has('mac')
+ call assert_match("man -P cat 'man'", a)
+ else
+ call assert_match("man --pager=cat 'man'", a)
+ endif
" clean up
let &keywordprg = k
@@ -2272,6 +2281,8 @@ endfunc
func! Test_normal45_drop()
if !has('dnd')
+ " The ~ register does not exist
+ call assert_beeps('norm! "~')
return
endif
@@ -2540,3 +2551,81 @@ func Test_delete_until_paragraph()
call assert_equal('', getline(1))
bwipe!
endfunc
+
+" Test for '[m', ']m', '[M' and ']M'
+" Jumping to beginning and end of methods in Java-like languages
+func Test_java_motion()
+ new
+ a
+Piece of Java
+{
+ tt m1 {
+ t1;
+ } e1
+
+ tt m2 {
+ t2;
+ } e2
+
+ tt m3 {
+ if (x)
+ {
+ t3;
+ }
+ } e3
+}
+.
+
+ normal gg
+
+ normal 2]maA
+ call assert_equal("\ttt m1 {A", getline('.'))
+ call assert_equal([3, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal j]maB
+ call assert_equal("\ttt m2 {B", getline('.'))
+ call assert_equal([7, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal ]maC
+ call assert_equal("\ttt m3 {C", getline('.'))
+ call assert_equal([11, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal [maD
+ call assert_equal("\ttt m3 {DC", getline('.'))
+ call assert_equal([11, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal k2[maE
+ call assert_equal("\ttt m1 {EA", getline('.'))
+ call assert_equal([3, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal 3[maF
+ call assert_equal("{F", getline('.'))
+ call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ normal ]MaG
+ call assert_equal("\t}G e1", getline('.'))
+ call assert_equal([5, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal j2]MaH
+ call assert_equal("\t}H e3", getline('.'))
+ call assert_equal([16, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal ]M]M
+ normal aI
+ call assert_equal("}I", getline('.'))
+ call assert_equal([17, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ normal 2[MaJ
+ call assert_equal("\t}JH e3", getline('.'))
+ call assert_equal([16, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal k[MaK
+ call assert_equal("\t}K e2", getline('.'))
+ call assert_equal([9, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal 3[MaL
+ call assert_equal("{LF", getline('.'))
+ call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 6c43cbc1dc..96f4bfc71b 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -5,7 +5,7 @@ source shared.vim
let g:months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
let g:setting = ''
-func! ListMonths()
+func ListMonths()
if g:setting != ''
exe ":set" g:setting
endif
@@ -18,7 +18,7 @@ func! ListMonths()
return ''
endfunc
-func! Test_popup_complete2()
+func Test_popup_complete2()
" Although the popupmenu is not visible, this does not mean completion mode
" has ended. After pressing <f5> to complete the currently typed char, Vim
" still stays in the first state of the completion (:h ins-completion-menu),
@@ -33,9 +33,9 @@ func! Test_popup_complete2()
call assert_equal(["Dece", "", "December2015"], getline(1,3))
%d
bw!
-endfu
+endfunc
-func! Test_popup_complete()
+func Test_popup_complete()
new
inoremap <f5> <c-r>=ListMonths()<cr>
@@ -214,10 +214,10 @@ func! Test_popup_complete()
call feedkeys("aM\<f5>\<enter>\<esc>", 'tx')
call assert_equal(["March", "M", "March"], getline(1,4))
%d
-endfu
+endfunc
-func! Test_popup_completion_insertmode()
+func Test_popup_completion_insertmode()
new
inoremap <F5> <C-R>=ListMonths()<CR>
@@ -246,20 +246,16 @@ func! Test_popup_completion_insertmode()
iunmap <F5>
endfunc
-" TODO: Fix what breaks after this line.
-" - Do not use "q!", it may exit Vim if there is an error
-finish
-
func Test_noinsert_complete()
- function! s:complTest1() abort
+ func! s:complTest1() abort
call complete(1, ['source', 'soundfold'])
return ''
- endfunction
+ endfunc
- function! s:complTest2() abort
+ func! s:complTest2() abort
call complete(1, ['source', 'soundfold'])
return ''
- endfunction
+ endfunc
new
set completeopt+=noinsert
@@ -279,10 +275,42 @@ func Test_noinsert_complete()
iunmap <F5>
endfunc
+func Test_complete_no_filter()
+ func! s:complTest1() abort
+ call complete(1, [{'word': 'foobar'}])
+ return ''
+ endfunc
+ func! s:complTest2() abort
+ call complete(1, [{'word': 'foobar', 'equal': 1}])
+ return ''
+ endfunc
+
+ let completeopt = &completeopt
+
+ " without equal=1
+ new
+ set completeopt=menuone,noinsert,menu
+ inoremap <F5> <C-R>=s:complTest1()<CR>
+ call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx')
+ call assert_equal('z', getline(1))
+ bwipe!
+
+ " with equal=1
+ new
+ set completeopt=menuone,noinsert,menu
+ inoremap <F5> <C-R>=s:complTest2()<CR>
+ call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx')
+ call assert_equal('foobar', getline(1))
+ bwipe!
+
+ let &completeopt = completeopt
+ iunmap <F5>
+endfunc
+
func Test_compl_vim_cmds_after_register_expr()
- function! s:test_func()
+ func! s:test_func()
return 'autocmd '
- endfunction
+ endfunc
augroup AAAAA_Group
au!
augroup END
@@ -329,7 +357,7 @@ func DummyCompleteTwo(findstart, base)
else
return ['twodef', 'twoDEF']
endif
-endfunction
+endfunc
" Test that nothing happens if the 'completefunc' opens
" a new window (no completion, no crash)
@@ -406,7 +434,7 @@ func Test_omnifunc_with_check()
q!
endfunc
-function UndoComplete()
+func UndoComplete()
call complete(1, ['January', 'February', 'March',
\ 'April', 'May', 'June', 'July', 'August', 'September',
\ 'October', 'November', 'December'])
@@ -443,7 +471,7 @@ func Test_complete_no_undo()
q!
endfunc
-function! DummyCompleteFive(findstart, base)
+func DummyCompleteFive(findstart, base)
if a:findstart
return 0
else
@@ -547,7 +575,7 @@ func Test_completion_comment_formatting()
bwipe!
endfunc
-function! DummyCompleteSix()
+func DummyCompleteSix()
call complete(1, ['Hello', 'World'])
return ''
endfunction
@@ -623,7 +651,7 @@ func Test_popup_and_preview_autocommand()
bw!
endfunc
-fun MessCompleteMonths()
+func MessCompleteMonths()
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep")
call complete_add(m)
if complete_check()
@@ -631,14 +659,14 @@ fun MessCompleteMonths()
endif
endfor
return []
-endfun
+endfunc
-fun MessCompleteMore()
+func MessCompleteMore()
call complete(1, split("Oct Nov Dec"))
return []
-endfun
+endfunc
-fun MessComplete(findstart, base)
+func MessComplete(findstart, base)
if a:findstart
let line = getline('.')
let start = col('.') - 1
@@ -651,7 +679,7 @@ fun MessComplete(findstart, base)
call MessCompleteMore()
return []
endif
-endf
+endfunc
func Test_complete_func_mess()
" Calling complete() after complete_add() in 'completefunc' is wrong, but it
@@ -712,4 +740,105 @@ func Test_popup_and_window_resize()
bwipe!
endfunc
+func Test_popup_complete_info_01()
+ new
+ inoremap <buffer><F5> <C-R>=complete_info().mode<CR>
+ func s:complTestEval() abort
+ call complete(1, ['aa', 'ab'])
+ return ''
+ endfunc
+ inoremap <buffer><F6> <C-R>=s:complTestEval()<CR>
+ call writefile([
+ \ 'dummy dummy.txt 1',
+ \], 'Xdummy.txt')
+ setlocal tags=Xdummy.txt
+ setlocal dictionary=Xdummy.txt
+ setlocal thesaurus=Xdummy.txt
+ setlocal omnifunc=syntaxcomplete#Complete
+ setlocal completefunc=syntaxcomplete#Complete
+ setlocal spell
+ for [keys, mode_name] in [
+ \ ["", ''],
+ \ ["\<C-X>", 'ctrl_x'],
+ \ ["\<C-X>\<C-N>", 'keyword'],
+ \ ["\<C-X>\<C-P>", 'keyword'],
+ \ ["\<C-X>\<C-L>", 'whole_line'],
+ \ ["\<C-X>\<C-F>", 'files'],
+ \ ["\<C-X>\<C-]>", 'tags'],
+ \ ["\<C-X>\<C-D>", 'path_defines'],
+ \ ["\<C-X>\<C-I>", 'path_patterns'],
+ \ ["\<C-X>\<C-K>", 'dictionary'],
+ \ ["\<C-X>\<C-T>", 'thesaurus'],
+ \ ["\<C-X>\<C-V>", 'cmdline'],
+ \ ["\<C-X>\<C-U>", 'function'],
+ \ ["\<C-X>\<C-O>", 'omni'],
+ \ ["\<C-X>s", 'spell'],
+ \ ["\<F6>", 'eval'],
+ \]
+ call feedkeys("i" . keys . "\<F5>\<Esc>", 'tx')
+ call assert_equal(mode_name, getline('.'))
+ %d
+ endfor
+ call delete('Xdummy.txt')
+ bwipe!
+endfunc
+
+func UserDefinedComplete(findstart, base)
+ if a:findstart
+ return 0
+ else
+ return [
+ \ { 'word': 'Jan', 'menu': 'January' },
+ \ { 'word': 'Feb', 'menu': 'February' },
+ \ { 'word': 'Mar', 'menu': 'March' },
+ \ { 'word': 'Apr', 'menu': 'April' },
+ \ { 'word': 'May', 'menu': 'May' },
+ \ ]
+ endif
+endfunc
+
+func GetCompleteInfo()
+ if empty(g:compl_what)
+ let g:compl_info = complete_info()
+ else
+ let g:compl_info = complete_info(g:compl_what)
+ endif
+ return ''
+endfunc
+
+func Test_popup_complete_info_02()
+ new
+ inoremap <buffer><F5> <C-R>=GetCompleteInfo()<CR>
+ setlocal completefunc=UserDefinedComplete
+
+ let d = {
+ \ 'mode': 'function',
+ \ 'pum_visible': 1,
+ \ 'items': [
+ \ {'word': 'Jan', 'menu': 'January', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Feb', 'menu': 'February', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Mar', 'menu': 'March', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Apr', 'menu': 'April', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}
+ \ ],
+ \ 'selected': 0,
+ \ }
+
+ let g:compl_what = []
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+
+ let g:compl_what = ['mode', 'pum_visible', 'selected']
+ call remove(d, 'items')
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+
+ let g:compl_what = ['mode']
+ call remove(d, 'selected')
+ call remove(d, 'pum_visible')
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
index 0b8961c52b..43a5d18cb3 100644
--- a/src/nvim/testdir/test_put.vim
+++ b/src/nvim/testdir/test_put.vim
@@ -1,3 +1,4 @@
+" Tests for put commands, e.g. ":put", "p", "gp", "P", "gP", etc.
func Test_put_block()
if !has('multi_byte')
@@ -47,3 +48,59 @@ func Test_put_expr()
call assert_equal(['A1','A2','A3','4A','5A','6A'], getline(1,'$'))
bw!
endfunc
+
+func Test_put_lines()
+ new
+ let a = [ getreg('a'), getregtype('a') ]
+ call setline(1, ['Line 1', 'Line2', 'Line 3', ''])
+ exe 'norm! gg"add"AddG""p'
+ call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1,'$'))
+ " clean up
+ bw!
+ call setreg('a', a[0], a[1])
+endfunc
+
+func Test_put_fails_when_nomodifiable()
+ new
+ setlocal nomodifiable
+
+ normal! yy
+ call assert_fails(':put', 'E21')
+ call assert_fails(':put!', 'E21')
+ call assert_fails(':normal! p', 'E21')
+ call assert_fails(':normal! gp', 'E21')
+ call assert_fails(':normal! P', 'E21')
+ call assert_fails(':normal! gP', 'E21')
+
+ if has('mouse')
+ set mouse=n
+ call assert_fails('execute "normal! \<MiddleMouse>"', 'E21')
+ set mouse&
+ endif
+
+ bwipeout!
+endfunc
+
+" A bug was discovered where the Normal mode put commands (e.g., "p") would
+" output duplicate error messages when invoked in a non-modifiable buffer.
+func Test_put_p_errmsg_nodup()
+ new
+ setlocal nomodifiable
+
+ normal! yy
+
+ func Capture_p_error()
+ redir => s:p_err
+ normal! p
+ redir END
+ endfunc
+
+ silent! call Capture_p_error()
+
+ " Error message output within a function should be three lines (the function
+ " name, the line number, and the error message).
+ call assert_equal(3, count(s:p_err, "\n"))
+
+ delfunction Capture_p_error
+ bwipeout!
+endfunc
diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim
index 5ba9fd68cf..0c5809f7a4 100644
--- a/src/nvim/testdir/test_python2.vim
+++ b/src/nvim/testdir/test_python2.vim
@@ -52,3 +52,92 @@ func Test_vim_function()
py del f
delfunc s:foo
endfunc
+
+func _SetUpHiddenBuffer()
+ py import vim
+ new
+ edit hidden
+ setlocal bufhidden=hide
+
+ enew
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func _CleanUpHiddenBuffer()
+ bwipe! hidden
+ bwipe!
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Clear()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_List()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = [ 'test' ]
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Str()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = 'test'
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func _SetUpVisibleBuffer()
+ py import vim
+ new
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Clear()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[:] = None
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_List()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[:] = [ 'test' ]
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunction
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Str()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[-1] = None
+ call assert_equal( line( '.' ), 10 )
+
+ bwipe!
+endfunction
diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim
index 2e3fc93674..d090dcc99c 100644
--- a/src/nvim/testdir/test_python3.vim
+++ b/src/nvim/testdir/test_python3.vim
@@ -52,3 +52,92 @@ func Test_vim_function()
py3 del f
delfunc s:foo
endfunc
+
+func _SetUpHiddenBuffer()
+ py3 import vim
+ new
+ edit hidden
+ setlocal bufhidden=hide
+
+ enew
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func _CleanUpHiddenBuffer()
+ bwipe! hidden
+ bwipe!
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Clear()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_List()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = [ 'test' ]
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Str()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = 'test'
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func _SetUpVisibleBuffer()
+ py3 import vim
+ new
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Clear()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[:] = None
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_List()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[:] = [ 'test' ]
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunction
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Str()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[-1] = None
+ call assert_equal( line( '.' ), 10 )
+
+ bwipe!
+endfunction
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index cb3e7ca8f6..c6d083dfcc 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -136,6 +136,19 @@ func XlistTests(cchar)
\ ' 4:40 col 20 x 44: Other',
\ ' 5:50 col 25 55: one'], l)
+ " Test for module names, one needs to explicitly set `'valid':v:true` so
+ let save_shellslash = &shellslash
+ set shellslash
+ call g:Xsetlist([
+ \ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true},
+ \ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true},
+ \ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}])
+ let l = split(execute('Xlist', ""), "\n")
+ call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning',
+ \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning',
+ \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l)
+ let &shellslash = save_shellslash
+
" Error cases
call assert_fails('Xlist abc', 'E488:')
endfunc
@@ -430,6 +443,19 @@ func Xtest_browse(cchar)
call delete('Xqftestfile1')
call delete('Xqftestfile2')
+
+ " Should be able to use next/prev with invalid entries
+ Xexpr ""
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ Xaddexpr ['foo', 'bar', 'baz', 'quux', 'shmoo']
+ call assert_equal(5, g:Xgetlist({'size' : 0}).size)
+ Xlast
+ call assert_equal(5, g:Xgetlist({'idx' : 0}).idx)
+ Xfirst
+ call assert_equal(1, g:Xgetlist({'idx' : 0}).idx)
+ 2Xnext
+ call assert_equal(3, g:Xgetlist({'idx' : 0}).idx)
endfunc
func Test_browse()
@@ -469,6 +495,19 @@ func s:test_xhelpgrep(cchar)
" This wipes out the buffer, make sure that doesn't cause trouble.
Xclose
+ if a:cchar == 'l'
+ " When a help window is present, running :lhelpgrep should reuse the
+ " help window and not the current window
+ new | only
+ call g:Xsetlist([], 'f')
+ help index.txt
+ wincmd w
+ lhelpgrep quickfix
+ call assert_equal(1, winnr())
+ call assert_notequal([], getloclist(1))
+ call assert_equal([], getloclist(2))
+ endif
+
new | only
" Search for non existing help string
@@ -1067,6 +1106,21 @@ func Test_efm2()
call assert_equal(1, l[4].valid)
call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr))
+ " Test for %o
+ set efm=%f(%o):%l\ %m
+ cgetexpr ['Xtestfile(Language.PureScript.Types):20 Error']
+ call writefile(['Line1'], 'Xtestfile')
+ let l = getqflist()
+ call assert_equal(1, len(l), string(l))
+ call assert_equal('Language.PureScript.Types', l[0].module)
+ copen
+ call assert_equal('Language.PureScript.Types|20| Error', getline(1))
+ call feedkeys("\<CR>", 'xn')
+ call assert_equal('Xtestfile', expand('%:t'))
+ cclose
+ bd
+ call delete("Xtestfile")
+
" The following sequence of commands used to crash Vim
set efm=%W%m
cgetexpr ['msg1']
@@ -1353,6 +1407,11 @@ func XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
+func Test_setqflist_invalid_nr()
+ " The following command used to crash Vim
+ call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST})
+endfunc
+
func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
@@ -1666,6 +1725,10 @@ func HistoryTest(cchar)
call assert_equal(' error list 1 of 3; 1 ' . common, res[0])
call assert_equal(' error list 2 of 3; 2 ' . common, res[1])
call assert_equal('> error list 3 of 3; 3 ' . common, res[2])
+
+ call g:Xsetlist([], 'f')
+ let l = split(execute(a:cchar . 'hist'), "\n")
+ call assert_equal('No entries', l[0])
endfunc
func Test_history()
@@ -1749,8 +1812,8 @@ func Xproperty_tests(cchar)
call assert_equal(-1, s)
call assert_equal({}, g:Xgetlist({'abc':1}))
- call assert_equal({}, g:Xgetlist({'nr':99, 'title':1}))
- call assert_equal({}, g:Xgetlist({'nr':[], 'title':1}))
+ call assert_equal('', g:Xgetlist({'nr':99, 'title':1}).title)
+ call assert_equal('', g:Xgetlist({'nr':[], 'title':1}).title)
if a:cchar == 'l'
call assert_equal({}, getloclist(99, {'title': 1}))
@@ -1786,7 +1849,7 @@ func Xproperty_tests(cchar)
call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
only
call setloclist(0, [], 'f')
- call assert_equal({}, getloclist(0, {'context':1}))
+ call assert_equal('', getloclist(0, {'context':1}).context)
endif
" Test for changing the context of previous quickfix lists
@@ -1844,6 +1907,11 @@ func Xproperty_tests(cchar)
let l = g:Xgetlist({'items':1})
call assert_equal(0, len(l.items))
+ call g:Xsetlist([], 'r', {'title' : 'TestTitle'})
+ call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]})
+ call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]})
+ call assert_equal('TestTitle', g:Xgetlist({'title' : 1}).title)
+
" The following used to crash Vim with address sanitizer
call g:Xsetlist([], 'f')
call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]})
@@ -1886,10 +1954,10 @@ func Xproperty_tests(cchar)
call g:Xsetlist([], 'r', l2)
let newl1=g:Xgetlist({'nr':1,'all':1})
let newl2=g:Xgetlist({'nr':2,'all':1})
- call assert_equal(':Fruits', newl1.title)
+ call assert_equal('Fruits', newl1.title)
call assert_equal(['Fruits'], newl1.context)
call assert_equal('Line20', newl1.items[0].text)
- call assert_equal(':Colors', newl2.title)
+ call assert_equal('Colors', newl2.title)
call assert_equal(['Colors'], newl2.context)
call assert_equal('Line10', newl2.items[0].text)
call g:Xsetlist([], 'f')
@@ -1913,6 +1981,30 @@ func Test_Autocmd()
cexpr "F1:10:Line 10"
caddexpr "F1:20:Line 20"
cgetexpr "F1:30:Line 30"
+ cexpr ""
+ caddexpr ""
+ cgetexpr ""
+ silent! cexpr non_existing_func()
+ silent! caddexpr non_existing_func()
+ silent! cgetexpr non_existing_func()
+ let l = ['precexpr',
+ \ 'postcexpr',
+ \ 'precaddexpr',
+ \ 'postcaddexpr',
+ \ 'precgetexpr',
+ \ 'postcgetexpr',
+ \ 'precexpr',
+ \ 'postcexpr',
+ \ 'precaddexpr',
+ \ 'postcaddexpr',
+ \ 'precgetexpr',
+ \ 'postcgetexpr',
+ \ 'precexpr',
+ \ 'precaddexpr',
+ \ 'precgetexpr']
+ call assert_equal(l, g:acmds)
+
+ let g:acmds = []
enew! | call append(0, "F2:10:Line 10")
cbuffer!
enew! | call append(0, "F2:20:Line 20")
@@ -1920,19 +2012,108 @@ func Test_Autocmd()
enew! | call append(0, "F2:30:Line 30")
caddbuffer
- let l = ['precexpr',
- \ 'postcexpr',
- \ 'precaddexpr',
- \ 'postcaddexpr',
- \ 'precgetexpr',
- \ 'postcgetexpr',
- \ 'precbuffer',
+ new
+ let bnum = bufnr('%')
+ bunload
+ exe 'silent! cbuffer! ' . bnum
+ exe 'silent! cgetbuffer ' . bnum
+ exe 'silent! caddbuffer ' . bnum
+ enew!
+ let l = ['precbuffer',
\ 'postcbuffer',
\ 'precgetbuffer',
\ 'postcgetbuffer',
\ 'precaddbuffer',
- \ 'postcaddbuffer']
+ \ 'postcaddbuffer',
+ \ 'precbuffer',
+ \ 'precgetbuffer',
+ \ 'precaddbuffer']
+ call assert_equal(l, g:acmds)
+
+ call writefile(['Xtest:1:Line1'], 'Xtest')
+ call writefile([], 'Xempty')
+ let g:acmds = []
+ cfile Xtest
+ caddfile Xtest
+ cgetfile Xtest
+ cfile Xempty
+ caddfile Xempty
+ cgetfile Xempty
+ silent! cfile do_not_exist
+ silent! caddfile do_not_exist
+ silent! cgetfile do_not_exist
+ let l = ['precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile',
+ \ 'precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile',
+ \ 'precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile']
+ call assert_equal(l, g:acmds)
+
+ let g:acmds = []
+ helpgrep quickfix
+ silent! helpgrep non_existing_help_topic
+ vimgrep test Xtest
+ vimgrepadd test Xtest
+ silent! vimgrep non_existing_test Xtest
+ silent! vimgrepadd non_existing_test Xtest
+ set makeprg=
+ silent! make
+ set makeprg&
+ let l = ['prehelpgrep',
+ \ 'posthelpgrep',
+ \ 'prehelpgrep',
+ \ 'posthelpgrep',
+ \ 'previmgrep',
+ \ 'postvimgrep',
+ \ 'previmgrepadd',
+ \ 'postvimgrepadd',
+ \ 'previmgrep',
+ \ 'postvimgrep',
+ \ 'previmgrepadd',
+ \ 'postvimgrepadd',
+ \ 'premake',
+ \ 'postmake']
call assert_equal(l, g:acmds)
+
+ if has('unix')
+ " Run this test only on Unix-like systems. The grepprg may not be set on
+ " non-Unix systems.
+ " The following lines are used for the grep test. Don't remove.
+ " Grep_Autocmd_Text: Match 1
+ " GrepAdd_Autocmd_Text: Match 2
+ let g:acmds = []
+ silent grep Grep_Autocmd_Text test_quickfix.vim
+ silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim
+ silent grep abc123def Xtest
+ silent grepadd abc123def Xtest
+ let l = ['pregrep',
+ \ 'postgrep',
+ \ 'pregrepadd',
+ \ 'postgrepadd',
+ \ 'pregrep',
+ \ 'postgrep',
+ \ 'pregrepadd',
+ \ 'postgrepadd']
+ call assert_equal(l, g:acmds)
+ endif
+
+ call delete('Xtest')
+ call delete('Xempty')
+ au! QuickFixCmdPre
+ au! QuickFixCmdPost
endfunc
func Test_Autocmd_Exception()
@@ -2184,8 +2365,8 @@ func XsizeTests(cchar)
call g:Xsetlist([], 'f')
call assert_equal(0, g:Xgetlist({'nr':'$'}).nr)
- call assert_equal(1, len(g:Xgetlist({'nr':'$', 'all':1})))
- call assert_equal(0, len(g:Xgetlist({'nr':0})))
+ call assert_equal('', g:Xgetlist({'nr':'$', 'all':1}).title)
+ call assert_equal(0, g:Xgetlist({'nr':0}).nr)
Xexpr "File1:10:Line1"
Xexpr "File2:20:Line2"
@@ -2405,6 +2586,29 @@ func Xmultifilestack_tests(cchar)
call assert_equal(3, l1.items[1].lnum)
call assert_equal('two.txt', bufname(l2.items[1].bufnr))
call assert_equal(5, l2.items[1].lnum)
+
+ " Test for start of a new error line in the same line where a previous
+ " error line ends with a file stack.
+ let efm_val = 'Error\ l%l\ in\ %f,'
+ let efm_val .= '%-P%>(%f%r,Error\ l%l\ in\ %m,%-Q)%r'
+ let l = g:Xgetlist({'lines' : [
+ \ '(one.txt',
+ \ 'Error l4 in one.txt',
+ \ ') (two.txt',
+ \ 'Error l6 in two.txt',
+ \ ')',
+ \ 'Error l8 in one.txt'
+ \ ], 'efm' : efm_val})
+ call assert_equal(3, len(l.items))
+ call assert_equal('one.txt', bufname(l.items[0].bufnr))
+ call assert_equal(4, l.items[0].lnum)
+ call assert_equal('one.txt', l.items[0].text)
+ call assert_equal('two.txt', bufname(l.items[1].bufnr))
+ call assert_equal(6, l.items[1].lnum)
+ call assert_equal('two.txt', l.items[1].text)
+ call assert_equal('one.txt', bufname(l.items[2].bufnr))
+ call assert_equal(8, l.items[2].lnum)
+ call assert_equal('', l.items[2].text)
endfunc
func Test_multifilestack()
@@ -2584,7 +2788,7 @@ func Xqfid_tests(cchar)
call s:setup_commands(a:cchar)
call g:Xsetlist([], 'f')
- call assert_equal({}, g:Xgetlist({'id':0}))
+ call assert_equal(0, g:Xgetlist({'id':0}).id)
Xexpr ''
let start_id = g:Xgetlist({'id' : 0}).id
Xexpr '' | Xexpr ''
@@ -2592,10 +2796,10 @@ func Xqfid_tests(cchar)
call assert_equal(start_id, g:Xgetlist({'id':0, 'nr':1}).id)
call assert_equal(start_id + 1, g:Xgetlist({'id':0, 'nr':0}).id)
call assert_equal(start_id + 2, g:Xgetlist({'id':0, 'nr':'$'}).id)
- call assert_equal({}, g:Xgetlist({'id':0, 'nr':99}))
+ call assert_equal(0, g:Xgetlist({'id':0, 'nr':99}).id)
call assert_equal(2, g:Xgetlist({'id':start_id + 1, 'nr':0}).nr)
- call assert_equal({}, g:Xgetlist({'id':99, 'nr':0}))
- call assert_equal({}, g:Xgetlist({'id':"abc", 'nr':0}))
+ call assert_equal(0, g:Xgetlist({'id':99, 'nr':0}).id)
+ call assert_equal(0, g:Xgetlist({'id':"abc", 'nr':0}).id)
call g:Xsetlist([], 'a', {'id':start_id, 'context':[1,2]})
call assert_equal([1,2], g:Xgetlist({'nr':1, 'context':1}).context)
@@ -2606,7 +2810,7 @@ func Xqfid_tests(cchar)
let qfid = g:Xgetlist({'id':0, 'nr':0})
call g:Xsetlist([], 'f')
- call assert_equal({}, g:Xgetlist({'id':qfid, 'nr':0}))
+ call assert_equal(0, g:Xgetlist({'id':qfid, 'nr':0}).id)
endfunc
func Test_qf_id()
@@ -2614,6 +2818,15 @@ func Test_qf_id()
call Xqfid_tests('l')
endfunc
+func Test_getqflist_invalid_nr()
+ " The following commands used to crash Vim
+ cexpr ""
+ call getqflist({'nr' : $XXX_DOES_NOT_EXIST_XXX})
+
+ " Cleanup
+ call setqflist([], 'r')
+endfunc
+
" Test for shortening/simplifying the file name when opening the
" quickfix window or when displaying the quickfix list
func Test_shorten_fname()
@@ -2638,6 +2851,99 @@ func Test_shorten_fname()
call assert_equal('test_quickfix.vim', bufname('test_quickfix.vim'))
endfunc
+" Quickfix title tests
+" In the below tests, 'exe "cmd"' is used to invoke the quickfix commands.
+" Otherwise due to indentation, the title is set with spaces at the beginning
+" of the command.
+func Test_qftitle()
+ call writefile(["F1:1:Line1"], 'Xerr')
+
+ " :cexpr
+ exe "cexpr readfile('Xerr')"
+ call assert_equal(":cexpr readfile('Xerr')", getqflist({'title' : 1}).title)
+
+ " :cgetexpr
+ exe "cgetexpr readfile('Xerr')"
+ call assert_equal(":cgetexpr readfile('Xerr')",
+ \ getqflist({'title' : 1}).title)
+
+ " :caddexpr
+ call setqflist([], 'f')
+ exe "caddexpr readfile('Xerr')"
+ call assert_equal(":caddexpr readfile('Xerr')",
+ \ getqflist({'title' : 1}).title)
+
+ " :cbuffer
+ new Xerr
+ exe "cbuffer"
+ call assert_equal(':cbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :cgetbuffer
+ edit Xerr
+ exe "cgetbuffer"
+ call assert_equal(':cgetbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :caddbuffer
+ call setqflist([], 'f')
+ edit Xerr
+ exe "caddbuffer"
+ call assert_equal(':caddbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :cfile
+ exe "cfile Xerr"
+ call assert_equal(':cfile Xerr', getqflist({'title' : 1}).title)
+
+ " :cgetfile
+ exe "cgetfile Xerr"
+ call assert_equal(':cgetfile Xerr', getqflist({'title' : 1}).title)
+
+ " :caddfile
+ call setqflist([], 'f')
+ exe "caddfile Xerr"
+ call assert_equal(':caddfile Xerr', getqflist({'title' : 1}).title)
+
+ " :grep
+ set grepprg=internal
+ exe "grep F1 Xerr"
+ call assert_equal(':grep F1 Xerr', getqflist({'title' : 1}).title)
+
+ " :grepadd
+ call setqflist([], 'f')
+ exe "grepadd F1 Xerr"
+ call assert_equal(':grepadd F1 Xerr', getqflist({'title' : 1}).title)
+ set grepprg&vim
+
+ " :vimgrep
+ exe "vimgrep F1 Xerr"
+ call assert_equal(':vimgrep F1 Xerr', getqflist({'title' : 1}).title)
+
+ " :vimgrepadd
+ call setqflist([], 'f')
+ exe "vimgrepadd F1 Xerr"
+ call assert_equal(':vimgrepadd F1 Xerr', getqflist({'title' : 1}).title)
+
+ call setqflist(['F1:10:L10'], ' ')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ call setqflist([], 'f')
+ call setqflist(['F1:10:L10'], 'a')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ call setqflist([], 'f')
+ call setqflist(['F1:10:L10'], 'r')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ close
+ call delete('Xerr')
+
+ call setqflist([], ' ', {'title' : 'Errors'})
+ copen
+ call assert_equal('Errors', w:quickfix_title)
+ call setqflist([], 'r', {'items' : [{'filename' : 'a.c', 'lnum' : 10}]})
+ call assert_equal('Errors', w:quickfix_title)
+ cclose
+endfunc
+
" Test for the position of the quickfix and location list window
func Test_qfwin_pos()
" Open two windows
@@ -2664,3 +2970,198 @@ func Test_qfwin_pos()
call assert_equal(3, winnr())
close
endfunc
+
+" The following test used to crash Vim
+func Test_lhelpgrep_autocmd()
+ lhelpgrep quickfix
+ autocmd QuickFixCmdPost * call setloclist(0, [], 'f')
+ lhelpgrep buffer
+ call assert_equal('help', &filetype)
+ call assert_equal(0, getloclist(0, {'nr' : '$'}).nr)
+ lhelpgrep tabpage
+ call assert_equal('help', &filetype)
+ call assert_equal(1, getloclist(0, {'nr' : '$'}).nr)
+ au! QuickFixCmdPost
+ new | only
+endfunc
+
+" Test to make sure that an empty quickfix buffer is not reused for loading
+" a normal buffer.
+func Test_empty_qfbuf()
+ enew | only
+ call writefile(["Test"], 'Xfile1')
+ call setqflist([], 'f')
+ copen | only
+ let qfbuf = bufnr('')
+ edit Xfile1
+ call assert_notequal(qfbuf, bufnr(''))
+ enew
+ call delete('Xfile1')
+endfunc
+
+" Tests for the getqflist() and getloclist() functions when the list is not
+" present or is empty
+func Xgetlist_empty_tests(cchar)
+ call s:setup_commands(a:cchar)
+
+ " Empty quickfix stack
+ call g:Xsetlist([], 'f')
+ call assert_equal('', g:Xgetlist({'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick': 0}, g:Xgetlist({'all' : 0}))
+
+ " Quickfix window with empty stack
+ silent! Xopen
+ let qfwinid = (a:cchar == 'c') ? win_getid() : 0
+ call assert_equal(qfwinid, g:Xgetlist({'winid' : 0}).winid)
+ Xclose
+
+ " Empty quickfix list
+ Xexpr ""
+ call assert_equal('', g:Xgetlist({'context' : 0}).context)
+ call assert_notequal(0, g:Xgetlist({'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'items' : 0}).items)
+ call assert_notequal(0, g:Xgetlist({'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ call assert_notequal('', g:Xgetlist({'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ let qfid = g:Xgetlist({'id' : 0}).id
+ call g:Xsetlist([], 'f')
+
+ " Non-existing quickfix identifier
+ call assert_equal('', g:Xgetlist({'id' : qfid, 'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'id' : qfid}).id)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'id' : qfid, 'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'id' : qfid, 'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
+
+ " Non-existing quickfix list number
+ call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'nr' : 5}).nr)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'nr' : 5, 'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'nr' : 5, 'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
+endfunc
+
+func Test_getqflist()
+ call Xgetlist_empty_tests('c')
+ call Xgetlist_empty_tests('l')
+endfunc
+
+" Tests for the quickfix/location list changedtick
+func Xqftick_tests(cchar)
+ call s:setup_commands(a:cchar)
+
+ call g:Xsetlist([], 'f')
+
+ Xexpr "F1:10:Line10"
+ let qfid = g:Xgetlist({'id' : 0}).id
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ Xaddexpr "F2:20:Line20\nF2:21:Line21"
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'a', {'lines' : ["F3:30:Line30", "F3:31:Line31"]})
+ call assert_equal(3, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'r', {'lines' : ["F4:40:Line40"]})
+ call assert_equal(4, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'a', {'title' : 'New Title'})
+ call assert_equal(5, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ enew!
+ call append(0, ["F5:50:L50", "F6:60:L60"])
+ Xaddbuffer
+ call assert_equal(6, g:Xgetlist({'changedtick' : 0}).changedtick)
+ enew!
+
+ call g:Xsetlist([], 'a', {'context' : {'bus' : 'pci'}})
+ call assert_equal(7, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'a')
+ call assert_equal(8, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], ' ')
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r')
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ call writefile(["F8:80:L80", "F8:81:L81"], "Xone")
+ Xfile Xone
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ Xaddfile Xone
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ " Test case for updating a non-current quickfix list
+ call g:Xsetlist([], 'f')
+ Xexpr "F1:1:L1"
+ Xexpr "F2:2:L2"
+ call g:Xsetlist([], 'a', {'nr' : 1, "lines" : ["F10:10:L10"]})
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call assert_equal(2, g:Xgetlist({'nr' : 1, 'changedtick' : 0}).changedtick)
+
+ call delete("Xone")
+endfunc
+
+func Test_qf_tick()
+ call Xqftick_tests('c')
+ call Xqftick_tests('l')
+endfunc
+
+" The following test used to crash vim
+func Test_lbuffer_crash()
+ sv Xtest
+ augroup QF_Test
+ au!
+ au * * bw
+ augroup END
+ lbuffer
+ augroup QF_Test
+ au!
+ augroup END
+endfunc
+
+" The following test used to crash vim
+func Test_lexpr_crash()
+ augroup QF_Test
+ au!
+ au * * call setloclist(0, [], 'f')
+ augroup END
+ lexpr ""
+ augroup QF_Test
+ au!
+ augroup END
+ enew | only
+endfunc
+
+" The following test used to crash Vim
+func Test_lvimgrep_crash()
+ sv Xtest
+ augroup QF_Test
+ au!
+ au * * call setloclist(0, [], 'f')
+ augroup END
+ lvimgrep quickfix test_quickfix.vim
+ augroup QF_Test
+ au!
+ augroup END
+ enew | only
+endfunc
diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim
index 2192b5e8fc..7873502943 100644
--- a/src/nvim/testdir/test_regex_char_classes.vim
+++ b/src/nvim/testdir/test_regex_char_classes.vim
@@ -1,5 +1,11 @@
" Tests for regexp with backslash and other special characters inside []
" Also test backslash for hex/octal numbered character.
+"
+if !has('multi_byte')
+ finish
+endif
+
+scriptencoding utf-8
function RunSTest(value, calls, expected)
new
@@ -56,3 +62,237 @@ function Test_s_search()
call RunSTest(" xyz", "s/~/bcd/", " bcd")
call RunSTest(" bcdbcdbcd", "s/~\\+/BB/", " BB")
endfunction
+
+" Test character classes in regexp using regexpengine 0, 1, 2.
+func Test_regex_char_classes()
+ new
+ let save_enc = &encoding
+ set encoding=utf-8
+
+ let input = "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"
+
+ " Format is [cmd_to_run, expected_output]
+ let tests = [
+ \ [':s/\%#=0\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=1\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=2\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=0[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=1[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=2[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=0\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\O//g',
+ \ "01234567"],
+ \ [':s/\%#=1\O//g',
+ \ "01234567"],
+ \ [':s/\%#=2\O//g',
+ \ "01234567"],
+ \ [':s/\%#=0[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=1[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=2[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=0\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=1[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=2[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b¦±¼ÇÓé"],
+ \ [':s/\%#=0[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"]
+ \]
+
+ for [cmd, expected] in tests
+ call append(0, input)
+ call cursor(1, 1)
+ exe cmd
+ call assert_equal(expected, getline(1), cmd)
+ endfor
+
+ let &encoding = save_enc
+ enew!
+ close
+endfunc
diff --git a/src/nvim/testdir/test_scrollbind.vim b/src/nvim/testdir/test_scrollbind.vim
index baa24f1979..6c5488be05 100644
--- a/src/nvim/testdir/test_scrollbind.vim
+++ b/src/nvim/testdir/test_scrollbind.vim
@@ -30,3 +30,243 @@ func Test_scrollbind()
setl noscrollbind
call assert_equal(0, topLineLeft - topLineRight)
endfunc
+
+" Test for 'scrollbind'
+func Test_scrollbind_opt()
+ new | only
+ set noscrollbind
+ set scrollopt=ver,jump scrolloff=2 nowrap noequalalways splitbelow
+
+ " Insert the text used for the test
+ append
+
+
+start of window 1
+. line 01 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 01
+. line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
+. line 03 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 03
+. line 04 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 04
+. line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
+. line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
+. line 07 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 07
+. line 08 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 08
+. line 09 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 09
+. line 10 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 10
+. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
+. line 12 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 12
+. line 13 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 13
+. line 14 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 14
+. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15
+end of window 1
+
+
+start of window 2
+. line 01 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 01
+. line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
+. line 03 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 03
+. line 04 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 04
+. line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
+. line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
+. line 07 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 07
+. line 08 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 08
+. line 09 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 09
+. line 10 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 10
+. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
+. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
+. line 13 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 13
+. line 14 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 14
+. line 15 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 15
+. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
+end of window 2
+
+.
+
+ " Test using two windows open to one buffer, one extra empty window
+ split
+ new
+ wincmd t
+ resize 8
+ call search('^start of window 1$')
+ normal zt
+ set scrollbind
+ wincmd j
+ resize 7
+ call search('^start of window 2$')
+ normal zt
+ set scrollbind
+
+ " -- start of tests --
+ " Test scrolling down
+ normal L5jHyy
+ wincmd b | normal pr0
+ wincmd t | normal Hyy
+ wincmd b | normal pr1
+ wincmd t | normal L6jHyy
+ wincmd b | normal pr2
+ wincmd k | normal Hyy
+ wincmd b | normal pr3
+
+ " Test scrolling up
+ wincmd t | normal H4k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal pr4
+ wincmd k | normal Hyy
+ wincmd b | normal pr5
+ wincmd k | normal 3k
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal pr6
+ wincmd t | normal Hyy
+ wincmd b | normal pr7
+
+ " Test horizontal scrolling
+ set scrollopt+=hor
+ normal gg"zyyG"zpG
+ wincmd t | normal 015zly$
+ wincmd b | normal p"zpG
+ wincmd k | normal y$
+ wincmd b | normal p"zpG
+ wincmd k | normal 10jH7zhg0y$
+ wincmd b | normal p"zpG
+ wincmd t | normal Hg0y$
+ wincmd b | normal p"zpG
+ set scrollopt-=hor
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05',
+ \ '1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05',
+ \ '2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ '3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06',
+ \ '5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06',
+ \ '6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ '56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ 'UTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ ''], getline(1, '$'))
+ enew!
+
+ " ****** tests using two different buffers *****
+ wincmd t | wincmd j | close
+ wincmd t | set noscrollbind
+ /start of window 2$/,/^end of window 2$/y
+ new
+ wincmd t | wincmd j | normal 4"zpGp
+ wincmd t
+ call search('^start of window 1$')
+ normal zt
+ set scrollbind
+ wincmd j
+ call search('^start of window 2$')
+ normal zt
+ set scrollbind
+
+ " -- start of tests --
+ " Test scrolling down
+ normal L5jHyy
+ wincmd b | normal pr0
+ wincmd t | normal Hyy
+ wincmd b | normal pr1
+ wincmd t | normal L6jHyy
+ wincmd b | normal pr2
+ wincmd k | normal Hyy
+ wincmd b | normal pr3
+
+ " Test scrolling up
+ wincmd t | normal H4k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal pr4
+ wincmd k | normal Hyy
+ wincmd b | normal pr5
+ wincmd k | normal 3k
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal pr6
+ wincmd t | normal Hyy
+ wincmd b | normal pr7
+
+ " Test horizontal scrolling
+ set scrollopt+=hor
+ normal gg"zyyG"zpG
+ wincmd t | normal 015zly$
+ wincmd b | normal p"zpG
+ wincmd k | normal y$
+ wincmd b | normal p"zpG
+ wincmd k | normal 10jH7zhg0y$
+ wincmd b | normal p"zpG
+ wincmd t | normal Hg0y$
+ wincmd b | normal p"zpG
+ set scrollopt-=hor
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05',
+ \ '1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05',
+ \ '2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ '3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06',
+ \ '5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06',
+ \ '6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ '56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ 'UTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ ''], getline(1, '$'))
+ enew!
+
+ " Test 'syncbind'
+ wincmd t | set noscrollbind | normal ggL
+ wincmd j | set noscrollbind | normal ggL
+ set scrollbind
+ wincmd t | set scrollbind | normal G
+ wincmd j | normal G
+ syncbind
+ normal Hk
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal p
+ wincmd t | normal yy
+ wincmd b | normal p
+ wincmd t | set noscrollbind | normal ggL
+ wincmd j | set noscrollbind
+ normal ggL
+ set scrollbind
+ wincmd t | set scrollbind
+ wincmd t | normal G
+ wincmd j | normal G
+ wincmd t | syncbind | normal Hk
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal p
+ wincmd t | wincmd j | normal yy
+ wincmd b | normal p
+ wincmd t | normal H3k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal p
+ wincmd t | wincmd j | normal yy
+ wincmd b | normal p
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16',
+ \ 'start of window 2',
+ \ 'start of window 2',
+ \ '. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16',
+ \ '. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15',
+ \ '. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12',
+ \ ], getline(1, '$'))
+ enew!
+
+ new | only!
+ set scrollbind& scrollopt& scrolloff& wrap& equalalways& splitbelow&
+endfunc
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 08ccc8d4fe..f4fe4051e3 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -585,3 +585,27 @@ func Test_one_error_msg()
" This was also giving an internal error
call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:')
endfunc
+
+" Test for the search() function with match at the cursor position
+func Test_search_match_at_curpos()
+ new
+ call append(0, ['foobar', '', 'one two', ''])
+
+ normal gg
+
+ call search('foobar', 'c')
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ normal j
+ call search('^$', 'c')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call search('^$', 'bc')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ exe "normal /two\<CR>"
+ call search('.', 'c')
+ call assert_equal([3, 5], [line('.'), col('.')])
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index d02454fbf0..8b306192f0 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -107,6 +107,32 @@ function! Test_substitute_variants()
endfor
endfunction
+" Test the l, p, # flags.
+func Test_substitute_flags_lp()
+ new
+ call setline(1, "abc\tdef\<C-h>ghi")
+
+ let a = execute('s/a/a/p')
+ call assert_equal("\nabc def^Hghi", a)
+
+ let a = execute('s/a/a/l')
+ call assert_equal("\nabc^Idef^Hghi$", a)
+
+ let a = execute('s/a/a/#')
+ call assert_equal("\n 1 abc def^Hghi", a)
+
+ let a = execute('s/a/a/p#')
+ call assert_equal("\n 1 abc def^Hghi", a)
+
+ let a = execute('s/a/a/l#')
+ call assert_equal("\n 1 abc^Idef^Hghi$", a)
+
+ let a = execute('s/a/a/')
+ call assert_equal("", a)
+
+ bwipe!
+endfunc
+
func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
@@ -585,3 +611,93 @@ func Test_sub_replace_10()
call assert_equal('aa2a3a', substitute('123', '1\|\ze', 'a', 'g'))
call assert_equal('1aaa', substitute('123', '1\zs\|[23]', 'a', 'g'))
endfunc
+
+func Test_nocatch_sub_failure_handling()
+ " normal error results in all replacements
+ func! Foo()
+ foobar
+ endfunc
+ new
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ %s/aaa/\=Foo()/g
+ call assert_equal(['1 0', '2 0', '3 0'], getline(1, 3))
+
+ " Trow without try-catch causes abort after the first line.
+ " We cannot test this, since it would stop executing the test script.
+
+ " try/catch does not result in any changes
+ func! Foo()
+ throw 'error'
+ endfunc
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ let error_caught = 0
+ try
+ %s/aaa/\=Foo()/g
+ catch
+ let error_caught = 1
+ endtry
+ call assert_equal(1, error_caught)
+ call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
+
+ " Same, but using "n" flag so that "sandbox" gets set
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ let error_caught = 0
+ try
+ %s/aaa/\=Foo()/gn
+ catch
+ let error_caught = 1
+ endtry
+ call assert_equal(1, error_caught)
+ call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
+
+ bwipe!
+endfunc
+
+" Test ":s/pat/sub/" with different ~s in sub.
+func Test_replace_with_tilde()
+ new
+ " Set the last replace string to empty
+ s/^$//
+ call append(0, ['- Bug in "vPPPP" on this text:'])
+ normal gg
+ s/u/~u~/
+ call assert_equal('- Bug in "vPPPP" on this text:', getline(1))
+ s/i/~u~/
+ call assert_equal('- Bug uuun "vPPPP" on this text:', getline(1))
+ s/o/~~~/
+ call assert_equal('- Bug uuun "vPPPP" uuuuuuuuun this text:', getline(1))
+ close!
+endfunc
+
+func Test_replace_keeppatterns()
+ new
+ a
+foobar
+
+substitute foo asdf
+
+one two
+.
+
+ normal gg
+ /^substitute
+ s/foo/bar/
+ call assert_equal('foo', @/)
+ call assert_equal('substitute bar asdf', getline('.'))
+
+ /^substitute
+ keeppatterns s/asdf/xyz/
+ call assert_equal('^substitute', @/)
+ call assert_equal('substitute bar xyz', getline('.'))
+
+ exe "normal /bar /e\<CR>"
+ call assert_equal(15, col('.'))
+ normal -
+ keeppatterns /xyz
+ call assert_equal('bar ', @/)
+ call assert_equal('substitute bar xyz', getline('.'))
+ exe "normal 0dn"
+ call assert_equal('xyz', getline('.'))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index bc7b7c00d3..3db438cf4b 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -1,5 +1,9 @@
" Tests for the swap feature
+func s:swapname()
+ return trim(execute('swapname'))
+endfunc
+
" Tests for 'directory' option.
func Test_swap_directory()
if !has("unix")
@@ -17,7 +21,7 @@ func Test_swap_directory()
" Verify that the swap file doesn't exist in the current directory
call assert_equal([], glob(".Xtest1*.swp", 1, 1, 1))
edit Xtest1
- let swfname = split(execute("swapname"))[0]
+ let swfname = s:swapname()
call assert_equal([swfname], glob(swfname, 1, 1, 1))
" './dir', swap file in a directory relative to the file
@@ -27,7 +31,7 @@ func Test_swap_directory()
edit Xtest1
call assert_equal([], glob(swfname, 1, 1, 1))
let swfname = "Xtest2/Xtest1.swp"
- call assert_equal(swfname, split(execute("swapname"))[0])
+ call assert_equal(swfname, s:swapname())
call assert_equal([swfname], glob("Xtest2/*", 1, 1, 1))
" 'dir', swap file in directory relative to the current dir
@@ -38,7 +42,7 @@ func Test_swap_directory()
edit Xtest2/Xtest3
call assert_equal(["Xtest2/Xtest3"], glob("Xtest2/*", 1, 1, 1))
let swfname = "Xtest.je/Xtest3.swp"
- call assert_equal(swfname, split(execute("swapname"))[0])
+ call assert_equal(swfname, s:swapname())
call assert_equal([swfname], glob("Xtest.je/*", 1, 1, 1))
set dir&
@@ -47,6 +51,42 @@ func Test_swap_directory()
call delete("Xtest.je", "rf")
endfunc
+func Test_swap_group()
+ if !has("unix")
+ return
+ endif
+ let groups = split(system('groups'))
+ if len(groups) <= 1
+ throw 'Skipped: need at least two groups, got ' . string(groups)
+ endif
+
+ try
+ call delete('Xtest')
+ split Xtest
+ call setline(1, 'just some text')
+ wq
+ if system('ls -l Xtest') !~ ' ' . groups[0] . ' \d'
+ throw 'Skipped: test file does not have the first group'
+ else
+ silent !chmod 640 Xtest
+ call system('chgrp ' . groups[1] . ' Xtest')
+ if system('ls -l Xtest') !~ ' ' . groups[1] . ' \d'
+ throw 'Skipped: cannot set second group on test file'
+ else
+ split Xtest
+ let swapname = substitute(execute('swapname'), '[[:space:]]', '', 'g')
+ call assert_match('Xtest', swapname)
+ " Group of swapfile must now match original file.
+ call assert_match(' ' . groups[1] . ' \d', system('ls -l ' . swapname))
+
+ bwipe!
+ endif
+ endif
+ finally
+ call delete('Xtest')
+ endtry
+endfunc
+
func Test_missing_dir()
call mkdir('Xswapdir')
exe 'set directory=' . getcwd() . '/Xswapdir'
@@ -61,3 +101,120 @@ func Test_missing_dir()
set directory&
call delete('Xswapdir', 'rf')
endfunc
+
+func Test_swapinfo()
+ new Xswapinfo
+ call setline(1, ['one', 'two', 'three'])
+ w
+ let fname = s:swapname()
+ call assert_match('Xswapinfo', fname)
+ let info = swapinfo(fname)
+
+ let ver = printf('VIM %d.%d', v:version / 100, v:version % 100)
+ call assert_equal(ver, info.version)
+
+ call assert_match('\w', info.user)
+ " host name is truncated to 39 bytes in the swap file
+ call assert_equal(hostname()[:38], info.host)
+ call assert_match('Xswapinfo', info.fname)
+ call assert_match(0, info.dirty)
+ call assert_equal(getpid(), info.pid)
+ call assert_match('^\d*$', info.mtime)
+ if has_key(info, 'inode')
+ call assert_match('\d', info.inode)
+ endif
+ bwipe!
+ call delete(fname)
+ call delete('Xswapinfo')
+
+ let info = swapinfo('doesnotexist')
+ call assert_equal('Cannot open file', info.error)
+
+ call writefile(['burp'], 'Xnotaswapfile')
+ let info = swapinfo('Xnotaswapfile')
+ call assert_equal('Cannot read file', info.error)
+ call delete('Xnotaswapfile')
+
+ call writefile([repeat('x', 10000)], 'Xnotaswapfile')
+ let info = swapinfo('Xnotaswapfile')
+ call assert_equal('Not a swap file', info.error)
+ call delete('Xnotaswapfile')
+endfunc
+
+func Test_swapname()
+ edit Xtest1
+ let expected = s:swapname()
+ call assert_equal(expected, swapname('%'))
+
+ new Xtest2
+ let buf = bufnr('%')
+ let expected = s:swapname()
+ wincmd p
+ call assert_equal(expected, swapname(buf))
+
+ new Xtest3
+ setlocal noswapfile
+ call assert_equal('', swapname('%'))
+
+ bwipe!
+ call delete('Xtest1')
+ call delete('Xtest2')
+ call delete('Xtest3')
+endfunc
+
+func Test_swapfile_delete()
+ throw 'skipped: need the "blob" feature for this test'
+ autocmd! SwapExists
+ function s:swap_exists()
+ let v:swapchoice = s:swap_choice
+ let s:swapname = v:swapname
+ let s:filename = expand('<afile>')
+ endfunc
+ augroup test_swapfile_delete
+ autocmd!
+ autocmd SwapExists * call s:swap_exists()
+ augroup END
+
+
+ " Create a valid swapfile by editing a file.
+ split XswapfileText
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close the file and recreate the swap file.
+ " Now editing the file will run into the process still existing
+ quit
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swap_choice = 'e'
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
+
+ " Write the swapfile with a modified PID, now it will be automatically
+ " deleted. Process one should never be Vim.
+ let swapfile_bytes[24:27] = 0z01000000
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal('', s:swapname)
+
+ " Now set the modified flag, the swap file will not be deleted
+ let swapfile_bytes[28 + 80 + 899] = 0x55
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
+
+ call delete('XswapfileText')
+ call delete(swapfile_name)
+ augroup test_swapfile_delete
+ autocmd!
+ augroup END
+ augroup! test_swapfile_delete
+endfunc
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 6978faeb7b..850947f89f 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -503,3 +503,37 @@ func Test_syn_wrong_z_one()
" call test_override("ALL", 0)
bwipe!
endfunc
+
+func Test_syntax_hangs()
+ if !has('reltime') || !has('float') || !has('syntax')
+ return
+ endif
+
+ " This pattern takes a long time to match, it should timeout.
+ new
+ call setline(1, ['aaa', repeat('abc ', 1000), 'ccc'])
+ let start = reltime()
+ set nolazyredraw redrawtime=101
+ syn match Error /\%#=1a*.*X\@<=b*/
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed > 0.1)
+ call assert_true(elapsed < 1.0)
+
+ " second time syntax HL is disabled
+ let start = reltime()
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed < 0.1)
+
+ " after CTRL-L the timeout flag is reset
+ let start = reltime()
+ exe "normal \<C-L>"
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed > 0.1)
+ call assert_true(elapsed < 1.0)
+
+ set redrawtime&
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index add9b3d7cf..8043d13433 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -1,5 +1,6 @@
" Tests for tabpage
+" source screendump.vim
function Test_tabpage()
bw!
@@ -105,6 +106,14 @@ function Test_tabpage()
call assert_equal(4, tabpagenr())
7tabmove 5
call assert_equal(5, tabpagenr())
+ -tabmove
+ call assert_equal(4, tabpagenr())
+ +tabmove
+ call assert_equal(5, tabpagenr())
+ -2tabmove
+ call assert_equal(3, tabpagenr())
+ +3tabmove
+ call assert_equal(6, tabpagenr())
" The following are a no-op
norm! 2gt
@@ -547,4 +556,27 @@ func Test_tabs()
bw!
endfunc
+func Test_tabpage_cmdheight()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: only works with terminal'
+ endif
+ call writefile([
+ \ 'set laststatus=2',
+ \ 'set cmdheight=2',
+ \ 'tabnew',
+ \ 'set cmdheight=3',
+ \ 'tabnext',
+ \ 'redraw!',
+ \ 'echo "hello\nthere"',
+ \ 'tabnext',
+ \ 'redraw',
+ \ ], 'XTest_tabpage_cmdheight')
+ " Check that cursor line is concealed
+ let buf = RunVimInTerminal('-S XTest_tabpage_cmdheight', {'statusoff': 3})
+ call VerifyScreenDump(buf, 'Test_tabpage_cmdheight', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XTest_tabpage_cmdheight')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index f9bd8b5246..21ea00cab7 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -258,4 +258,209 @@ func Test_tagjump_etags()
bwipe!
endfunc
+" Test for getting and modifying the tag stack
+func Test_getsettagstack()
+ call writefile(['line1', 'line2', 'line3'], 'Xfile1')
+ call writefile(['line1', 'line2', 'line3'], 'Xfile2')
+ call writefile(['line1', 'line2', 'line3'], 'Xfile3')
+
+ enew | only
+ call settagstack(1, {'items' : []})
+ call assert_equal(0, gettagstack(1).length)
+ call assert_equal([], gettagstack(1).items)
+ " Error cases
+ call assert_equal({}, gettagstack(100))
+ call assert_equal(-1, settagstack(100, {'items' : []}))
+ call assert_fails('call settagstack(1, [1, 10])', 'E715')
+ call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
+ call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
+ call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile1\t1",
+ \ "three\tXfile3\t3",
+ \ "two\tXfile2\t2"],
+ \ 'Xtags')
+
+ let stk = []
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'one',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag one
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'two',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag two
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'three',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag three
+ call assert_equal(3, gettagstack(1).length)
+ call assert_equal(stk, gettagstack(1).items)
+ " Check for default - current window
+ call assert_equal(3, gettagstack().length)
+ call assert_equal(stk, gettagstack().items)
+
+ " Try to set current index to invalid values
+ call settagstack(1, {'curidx' : -1})
+ call assert_equal(1, gettagstack().curidx)
+ call settagstack(1, {'curidx' : 50})
+ call assert_equal(4, gettagstack().curidx)
+
+ " Try pushing invalid items onto the stack
+ call settagstack(1, {'items' : []})
+ call settagstack(1, {'items' : ["plate"]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"tagname" : "abc"}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"from" : 100}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"from" : [2, 1, 0, 0]}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+
+ " Push one item at a time to the stack
+ call settagstack(1, {'items' : []})
+ call settagstack(1, {'items' : [stk[0]]}, 'a')
+ call settagstack(1, {'items' : [stk[1]]}, 'a')
+ call settagstack(1, {'items' : [stk[2]]}, 'a')
+ call settagstack(1, {'curidx' : 4})
+ call assert_equal({'length' : 3, 'curidx' : 4, 'items' : stk},
+ \ gettagstack(1))
+
+ " Try pushing items onto a full stack
+ for i in range(7)
+ call settagstack(1, {'items' : stk}, 'a')
+ endfor
+ call assert_equal(20, gettagstack().length)
+ call settagstack(1,
+ \ {'items' : [{'tagname' : 'abc', 'from' : [1, 10, 1, 0]}]}, 'a')
+ call assert_equal('abc', gettagstack().items[19].tagname)
+
+ " Tag with multiple matches
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "two\tXfile1\t1",
+ \ "two\tXfile2\t3",
+ \ "two\tXfile3\t2"],
+ \ 'Xtags')
+ call settagstack(1, {'items' : []})
+ tag two
+ tnext
+ tnext
+ call assert_equal(1, gettagstack().length)
+ call assert_equal(3, gettagstack().items[0].matchnr)
+
+ call settagstack(1, {'items' : []})
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ call delete('Xtags')
+ set tags&
+endfunc
+
+func Test_tag_with_count()
+ call writefile([
+ \ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()',
+ \ ], 'Xtags')
+ call writefile([
+ \ 'main Xtest.c /^int main()$/;" f typeref:typename:int signature:()',
+ \ 'test Xtest.c /^void test()$/;" f typeref:typename:void signature:()',
+ \ ], 'Ytags')
+ cal writefile([
+ \ 'int main()',
+ \ 'void test()',
+ \ ], 'Xtest.c')
+ cal writefile([
+ \ 'void test();',
+ \ ], 'Xtest.h')
+ set tags=Xtags,Ytags
+
+ new Xtest.c
+ let tl = taglist('test', 'Xtest.c')
+ call assert_equal(tl[0].filename, 'Xtest.c')
+ call assert_equal(tl[1].filename, 'Xtest.h')
+
+ tag test
+ call assert_equal(bufname('%'), 'Xtest.c')
+ 1tag test
+ call assert_equal(bufname('%'), 'Xtest.c')
+ 2tag test
+ call assert_equal(bufname('%'), 'Xtest.h')
+
+ set tags&
+ call delete('Xtags')
+ call delete('Ytags')
+ bwipe Xtest.h
+ bwipe Xtest.c
+ call delete('Xtest.h')
+ call delete('Xtest.c')
+endfunc
+
+func Test_tagnr_recall()
+ call writefile([
+ \ 'test Xtest.h /^void test();$/;" p',
+ \ 'main Xtest.c /^int main()$/;" f',
+ \ 'test Xtest.c /^void test()$/;" f',
+ \ ], 'Xtags')
+ cal writefile([
+ \ 'int main()',
+ \ 'void test()',
+ \ ], 'Xtest.c')
+ cal writefile([
+ \ 'void test();',
+ \ ], 'Xtest.h')
+ set tags=Xtags
+
+ new Xtest.c
+ let tl = taglist('test', 'Xtest.c')
+ call assert_equal(tl[0].filename, 'Xtest.c')
+ call assert_equal(tl[1].filename, 'Xtest.h')
+
+ 2tag test
+ call assert_equal(bufname('%'), 'Xtest.h')
+ pop
+ call assert_equal(bufname('%'), 'Xtest.c')
+ tag
+ call assert_equal(bufname('%'), 'Xtest.h')
+
+ set tag&
+ call delete('Xtags')
+ bwipe Xtest.h
+ bwipe Xtest.c
+ call delete('Xtest.h')
+ call delete('Xtest.c')
+endfunc
+
+func Test_tag_line_toolong()
+ call writefile([
+ \ '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 django/contrib/admin/templates/admin/edit_inline/stacked.html 16;" j line:16 language:HTML'
+ \ ], 'Xtags')
+ set tags=Xtags
+ let old_vbs = &verbose
+ set verbose=5
+ " ":tjump" should give "tag not found" not "Format error in tags file"
+ call assert_fails('tj /foo', 'E426')
+ try
+ tj /foo
+ catch /^Vim\%((\a\+)\)\=:E431/
+ call assert_report(v:exception)
+ catch /.*/
+ endtry
+ call assert_equal('Ignoring long line in tags file', split(execute('messages'), '\n')[-1])
+ call writefile([
+ \ '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567 django/contrib/admin/templates/admin/edit_inline/stacked.html 16;" j line:16 language:HTML'
+ \ ], 'Xtags')
+ call assert_fails('tj /foo', 'E426')
+ try
+ tj /foo
+ catch /^Vim\%((\a\+)\)\=:E431/
+ call assert_report(v:exception)
+ catch /.*/
+ endtry
+ call assert_equal('Ignoring long line in tags file', split(execute('messages'), '\n')[-1])
+ call delete('Xtags')
+ let &verbose = old_vbs
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index 3ad2025915..ea0a6b9678 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -1,4 +1,4 @@
-" test 'taglist' function and :tags command
+" test taglist(), tagfiles() functions and :tags command
func Test_taglist()
call writefile([
@@ -61,3 +61,39 @@ func Test_tags_too_long()
call assert_fails('tag ' . repeat('x', 1020), 'E426')
tags
endfunc
+
+" For historical reasons we support a tags file where the last line is missing
+" the newline.
+func Test_tagsfile_without_trailing_newline()
+ call writefile(["Foo\tfoo\t1"], 'Xtags', 'b')
+ set tags=Xtags
+
+ let tl = taglist('.*')
+ call assert_equal(1, len(tl))
+ call assert_equal('Foo', tl[0].name)
+
+ call delete('Xtags')
+endfunc
+
+func Test_tagfiles()
+ call assert_equal([], tagfiles())
+
+ call writefile(["FFoo\tXfoo\t1"], 'Xtags1')
+ call writefile(["FBar\tXbar\t1"], 'Xtags2')
+ set tags=Xtags1,Xtags2
+ call assert_equal(['Xtags1', 'Xtags2'], tagfiles())
+
+ help
+ let tf = tagfiles()
+ call assert_equal(1, len(tf))
+ call assert_equal(fnamemodify(expand('$VIMRUNTIME/doc/tags'), ':p:gs?\\?/?'),
+ \ fnamemodify(tf[0], ':p:gs?\\?/?'))
+ helpclose
+ call assert_equal(['Xtags1', 'Xtags2'], tagfiles())
+ set tags&
+ call assert_equal([], tagfiles())
+
+ call delete('Xtags1')
+ call delete('Xtags2')
+ bd
+endfunc
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 999566c6ac..13fb50b985 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -163,6 +163,329 @@ func Test_text_format()
\ '# 1 xxxxx',
\ '# foobar'], getline(1, 2))
+ " Test the 'p' flag for 'formatoptions'
+ " First test without the flag: that it will break "Mr. Feynman" at the space
+ normal ggdG
+ setl tw=28 fo=tcq
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+ " Now test with the flag: that it will push the name with the title onto the
+ " next line
+ normal ggdG
+ setl fo+=p
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking,',
+ \ 'Mr. Feynman!'], getline(1, 2))
+ " Ensure that it will still break if two spaces are entered
+ normal ggdG
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+
setl ai& tw& fo& si& comments&
enew!
endfunc
+
+" Tests for :right, :center and :left on text with embedded TAB.
+func Test_format_align()
+ enew!
+ set tw=65
+
+ " :left alignment
+ call append(0, [
+ \ " test for :left",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ "asxxdfa a",
+ \ ])
+ %left
+ call assert_equal([
+ \ "test for :left",
+ \ "a a",
+ \ "fa a",
+ \ "dfa a",
+ \ "sdfa a",
+ \ "asdfa a",
+ \ "xasdfa a",
+ \ "asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :center alignment
+ call append(0, [
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ "asxxdfa a"
+ \ ])
+ %center
+ call assert_equal([
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ " asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :right alignment
+ call append(0, [
+ \ " test for :right",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ " asxxdfa a",
+ \ " asxa;ofa a",
+ \ " asdfaqwer a",
+ \ " a ax",
+ \ " fa ax",
+ \ " dfa ax",
+ \ " sdfa ax",
+ \ " asdfa ax",
+ \ " xasdfa ax",
+ \ " asxxdfa ax",
+ \ " asxa;ofa ax",
+ \ " asdfaqwer ax",
+ \ " a axx",
+ \ " fa axx",
+ \ " dfa axx",
+ \ " sdfa axx",
+ \ " asdfa axx",
+ \ " xasdfa axx",
+ \ " asxxdfa axx",
+ \ " asxa;ofa axx",
+ \ " asdfaqwer axx",
+ \ " a axxx",
+ \ " fa axxx",
+ \ " dfa axxx",
+ \ " sdfa axxx",
+ \ " asdfa axxx",
+ \ " xasdfa axxx",
+ \ " asxxdfa axxx",
+ \ " asxa;ofa axxx",
+ \ " asdfaqwer axxx",
+ \ " a axxxo",
+ \ " fa axxxo",
+ \ " dfa axxxo",
+ \ " sdfa axxxo",
+ \ " asdfa axxxo",
+ \ " xasdfa axxxo",
+ \ " asxxdfa axxxo",
+ \ " asxa;ofa axxxo",
+ \ " asdfaqwer axxxo",
+ \ " a axxxoi",
+ \ " fa axxxoi",
+ \ " dfa axxxoi",
+ \ " sdfa axxxoi",
+ \ " asdfa axxxoi",
+ \ " xasdfa axxxoi",
+ \ " asxxdfa axxxoi",
+ \ " asxa;ofa axxxoi",
+ \ " asdfaqwer axxxoi",
+ \ " a axxxoik",
+ \ " fa axxxoik",
+ \ " dfa axxxoik",
+ \ " sdfa axxxoik",
+ \ " asdfa axxxoik",
+ \ " xasdfa axxxoik",
+ \ " asxxdfa axxxoik",
+ \ " asxa;ofa axxxoik",
+ \ " asdfaqwer axxxoik",
+ \ " a axxxoike",
+ \ " fa axxxoike",
+ \ " dfa axxxoike",
+ \ " sdfa axxxoike",
+ \ " asdfa axxxoike",
+ \ " xasdfa axxxoike",
+ \ " asxxdfa axxxoike",
+ \ " asxa;ofa axxxoike",
+ \ " asdfaqwer axxxoike",
+ \ " a axxxoikey",
+ \ " fa axxxoikey",
+ \ " dfa axxxoikey",
+ \ " sdfa axxxoikey",
+ \ " asdfa axxxoikey",
+ \ " xasdfa axxxoikey",
+ \ " asxxdfa axxxoikey",
+ \ " asxa;ofa axxxoikey",
+ \ " asdfaqwer axxxoikey",
+ \ ])
+ %right
+ call assert_equal([
+ \ "\t\t\t\t test for :right",
+ \ "\t\t\t\t a a",
+ \ "\t\t\t\t fa a",
+ \ "\t\t\t\t dfa a",
+ \ "\t\t\t\t sdfa a",
+ \ "\t\t\t\t asdfa a",
+ \ "\t\t\t\t xasdfa a",
+ \ "\t\t\t\t asxxdfa a",
+ \ "\t\t\t\t asxa;ofa a",
+ \ "\t\t\t\t asdfaqwer a",
+ \ "\t\t\t\t a ax",
+ \ "\t\t\t\t fa ax",
+ \ "\t\t\t\t dfa ax",
+ \ "\t\t\t\t sdfa ax",
+ \ "\t\t\t\t asdfa ax",
+ \ "\t\t\t\t xasdfa ax",
+ \ "\t\t\t\t asxxdfa ax",
+ \ "\t\t\t\t asxa;ofa ax",
+ \ "\t\t\t\t asdfaqwer ax",
+ \ "\t\t\t\t a axx",
+ \ "\t\t\t\t fa axx",
+ \ "\t\t\t\t dfa axx",
+ \ "\t\t\t\t sdfa axx",
+ \ "\t\t\t\t asdfa axx",
+ \ "\t\t\t\t xasdfa axx",
+ \ "\t\t\t\t asxxdfa axx",
+ \ "\t\t\t\t asxa;ofa axx",
+ \ "\t\t\t\t asdfaqwer axx",
+ \ "\t\t\t\t a axxx",
+ \ "\t\t\t\t fa axxx",
+ \ "\t\t\t\t dfa axxx",
+ \ "\t\t\t\t sdfa axxx",
+ \ "\t\t\t\t asdfa axxx",
+ \ "\t\t\t\t xasdfa axxx",
+ \ "\t\t\t\t asxxdfa axxx",
+ \ "\t\t\t\t asxa;ofa axxx",
+ \ "\t\t\t\t asdfaqwer axxx",
+ \ "\t\t\t\t a axxxo",
+ \ "\t\t\t\t fa axxxo",
+ \ "\t\t\t\t dfa axxxo",
+ \ "\t\t\t\t sdfa axxxo",
+ \ "\t\t\t\t asdfa axxxo",
+ \ "\t\t\t\t xasdfa axxxo",
+ \ "\t\t\t\t asxxdfa axxxo",
+ \ "\t\t\t\t asxa;ofa axxxo",
+ \ "\t\t\t\t asdfaqwer axxxo",
+ \ "\t\t\t\t a axxxoi",
+ \ "\t\t\t\t fa axxxoi",
+ \ "\t\t\t\t dfa axxxoi",
+ \ "\t\t\t\t sdfa axxxoi",
+ \ "\t\t\t\t asdfa axxxoi",
+ \ "\t\t\t\t xasdfa axxxoi",
+ \ "\t\t\t\t asxxdfa axxxoi",
+ \ "\t\t\t\t asxa;ofa axxxoi",
+ \ "\t\t\t\t asdfaqwer axxxoi",
+ \ "\t\t\t\t a axxxoik",
+ \ "\t\t\t\t fa axxxoik",
+ \ "\t\t\t\t dfa axxxoik",
+ \ "\t\t\t\t sdfa axxxoik",
+ \ "\t\t\t\t asdfa axxxoik",
+ \ "\t\t\t\t xasdfa axxxoik",
+ \ "\t\t\t\t asxxdfa axxxoik",
+ \ "\t\t\t\t asxa;ofa axxxoik",
+ \ "\t\t\t\t asdfaqwer axxxoik",
+ \ "\t\t\t\t a axxxoike",
+ \ "\t\t\t\t fa axxxoike",
+ \ "\t\t\t\t dfa axxxoike",
+ \ "\t\t\t\t sdfa axxxoike",
+ \ "\t\t\t\t asdfa axxxoike",
+ \ "\t\t\t\t xasdfa axxxoike",
+ \ "\t\t\t\t asxxdfa axxxoike",
+ \ "\t\t\t\t asxa;ofa axxxoike",
+ \ "\t\t\t\t asdfaqwer axxxoike",
+ \ "\t\t\t\t a axxxoikey",
+ \ "\t\t\t\t fa axxxoikey",
+ \ "\t\t\t\t dfa axxxoikey",
+ \ "\t\t\t\t sdfa axxxoikey",
+ \ "\t\t\t\t asdfa axxxoikey",
+ \ "\t\t\t\t xasdfa axxxoikey",
+ \ "\t\t\t\t asxxdfa axxxoikey",
+ \ "\t\t\t\t asxa;ofa axxxoikey",
+ \ "\t\t\t\t asdfaqwer axxxoikey",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ set tw&
+endfunc
+
+" Test formatting a paragraph.
+func Test_format_para()
+ enew!
+ set fo+=tcroql tw=72
+
+ call append(0, [
+ \ "xxxxx xx xxxxxx ",
+ \ "xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx",
+ \ "xx xxxxxxx. xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx",
+ \ "> xxxxxx xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx"
+ \ ])
+ exe "normal /xxxxxxxx$\<CR>"
+ normal 0gq6kk
+ call assert_equal([
+ \ "xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx.",
+ \ "xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx xxxxxx",
+ \ "> xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx",
+ \ ""
+ \ ], getline(1, '$'))
+
+ set fo& tw&
+ enew!
+endfunc
+
+" Test undo after ":%s" and formatting.
+func Test_format_undo()
+ enew!
+ map gg :.,.+2s/^/x/<CR>kk:set tw=3<CR>gqq
+
+ call append(0, [
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc"
+ \ ])
+ " undo/redo here to make the next undo only work on the following changes
+ exe "normal i\<C-G>u"
+ call cursor(1,1)
+ normal ggu
+ call assert_equal([
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc",
+ \ ""
+ \ ], getline(1, '$'))
+
+ unmap gg
+ set tw&
+ enew!
+endfunc
+
+func Test_format_list_auto()
+ new
+ call setline(1, ['1. abc', '2. def', '3. ghi'])
+ set fo=tan ai bs=2
+ call feedkeys("3G0lli\<BS>\<BS>x\<Esc>", 'tx')
+ call assert_equal('2. defx ghi', getline(2))
+ bwipe!
+ set fo& ai& bs&
+endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 62ddad5dce..9384989a35 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -15,17 +15,13 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
-func s:assert_inrange(lower, upper, actual)
- return assert_inrange(a:lower, LoadAdjust(a:upper), a:actual)
-endfunc
-
func Test_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(40, 120, slept)
+ call assert_inrange(40, LoadAdjust(120), slept)
else
call assert_inrange(20, 120, slept)
endif
@@ -37,7 +33,7 @@ func Test_repeat_three()
let slept = WaitFor('g:val == 3')
call assert_equal(3, g:val)
if has('reltime')
- call s:assert_inrange(120, 250, slept)
+ call assert_inrange(120, LoadAdjust(250), slept)
else
call assert_inrange(80, 200, slept)
endif
@@ -47,9 +43,12 @@ func Test_repeat_many()
call timer_stopall()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1})
+ if has('mac')
+ sleep 200m
+ endif
sleep 200m
call timer_stop(timer)
- call assert_inrange((has('mac') ? 1 : 2), 4, g:val)
+ call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(4), g:val)
endfunc
func Test_with_partial_callback()
@@ -63,7 +62,7 @@ func Test_with_partial_callback()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(40, 130, slept)
+ call assert_inrange(40, LoadAdjust(130), slept)
else
call assert_inrange(20, 100, slept)
endif
@@ -126,7 +125,7 @@ func Test_paused()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(0, 140, slept)
+ call assert_inrange(0, LoadAdjust(140), slept)
else
call assert_inrange(0, 10, slept)
endif
@@ -197,6 +196,23 @@ func Test_input_in_timer()
call assert_equal('hello', g:val)
endfunc
+func FuncWithError(timer)
+ let g:call_count += 1
+ if g:call_count == 4
+ return
+ endif
+ doesnotexist
+endfunc
+
+func Test_timer_errors()
+ let g:call_count = 0
+ let timer = timer_start(10, 'FuncWithError', {'repeat': -1})
+ " Timer will be stopped after failing 3 out of 3 times.
+ call WaitFor('g:call_count == 3')
+ sleep 50m
+ call assert_equal(3, g:call_count)
+endfunc
+
func FuncWithCaughtError(timer)
let g:call_count += 1
try
diff --git a/src/nvim/testdir/test_true_false.vim b/src/nvim/testdir/test_true_false.vim
index 4a5d47471d..ad865bb113 100644
--- a/src/nvim/testdir/test_true_false.vim
+++ b/src/nvim/testdir/test_true_false.vim
@@ -134,6 +134,8 @@ func Test_non_zero_arg()
" call test_settime(93784)
" call Try_arg_non_zero("mode(%v%)", 'x', 'x!')
" call test_settime(0)
+ let shellslash = &shellslash
+ set shellslash
call Try_arg_non_zero("shellescape('foo%', %v%)", "'foo%'", "'foo\\%'")
@@ -152,4 +154,6 @@ func Test_non_zero_arg()
let r = visualmode(v)
call assert_equal('', r, 'result for ' . v . ' is not "" but ' . r)
endfor
+
+ let &shellslash = shellslash
endfunc
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 9729ca9f57..0cb5dc4033 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -242,6 +242,7 @@ func Test_undojoin()
endfunc
func Test_undo_write()
+ call delete('Xtest')
split Xtest
call feedkeys("ione one one\<Esc>", 'xt')
w!
@@ -388,6 +389,15 @@ funct Test_undofile()
" Test undofile() with 'undodir' set to a non-existing directory.
" call assert_equal('', undofile('Xundofoo'))
+ if isdirectory('/tmp')
+ set undodir=/tmp
+ if has('osx')
+ call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file'))
+ else
+ call assert_equal('/tmp/%tmp%file', undofile('///tmp/file'))
+ endif
+ endif
+
set undodir&
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 74afc72f03..f69273635c 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -411,3 +411,27 @@ func Test_Visual_paragraph_textobject()
bwipe!
endfunc
+
+" Tests for "vaBiB", end could be wrong.
+func Test_Visual_Block()
+ new
+ a
+- Bug in "vPPPP" on this text:
+ {
+ cmd;
+ {
+ cmd;\t/* <-- Start cursor here */
+ {
+ }
+ }
+ }
+.
+ normal gg
+ call search('Start cursor here')
+ normal vaBiBD
+ call assert_equal(['- Bug in "vPPPP" on this text:',
+ \ "\t{",
+ \ "\t}"], getline(1, '$'))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index e4618610cd..ee43540fdd 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -158,3 +158,29 @@ func Test_winfixwidth_on_close()
%bwipeout!
setlocal nowinfixwidth splitbelow& splitright&
endfunction
+
+" Test that 'winfixheight' will be respected even there is non-leaf frame
+fun! Test_winfixheight_non_leaf_frame()
+ vsplit
+ botright 11new
+ let l:wid = win_getid()
+ setlocal winfixheight
+ call assert_equal(11, winheight(l:wid))
+ botright new
+ bwipe!
+ call assert_equal(11, winheight(l:wid))
+ %bwipe!
+endf
+
+" Test that 'winfixwidth' will be respected even there is non-leaf frame
+fun! Test_winfixwidth_non_leaf_frame()
+ split
+ topleft 11vnew
+ let l:wid = win_getid()
+ setlocal winfixwidth
+ call assert_equal(11, winwidth(l:wid))
+ topleft new
+ bwipe!
+ call assert_equal(11, winwidth(l:wid))
+ %bwipe!
+endf
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 57fb36abb8..003a23ea7b 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -646,4 +646,54 @@ func Test_relative_cursor_second_line_after_resize()
let &so = so_save
endfunc
+" Tests for the winnr() function
+func Test_winnr()
+ only | tabonly
+ call assert_equal(1, winnr('j'))
+ call assert_equal(1, winnr('k'))
+ call assert_equal(1, winnr('h'))
+ call assert_equal(1, winnr('l'))
+
+ " create a set of horizontally and vertically split windows
+ leftabove new | wincmd p
+ leftabove new | wincmd p
+ rightbelow new | wincmd p
+ rightbelow new | wincmd p
+ leftabove vnew | wincmd p
+ leftabove vnew | wincmd p
+ rightbelow vnew | wincmd p
+ rightbelow vnew | wincmd p
+
+ call assert_equal(8, winnr('j'))
+ call assert_equal(2, winnr('k'))
+ call assert_equal(4, winnr('h'))
+ call assert_equal(6, winnr('l'))
+ call assert_equal(9, winnr('2j'))
+ call assert_equal(1, winnr('2k'))
+ call assert_equal(3, winnr('2h'))
+ call assert_equal(7, winnr('2l'))
+
+ " Error cases
+ call assert_fails("echo winnr('0.2k')", 'E15:')
+ call assert_equal(2, winnr('-2k'))
+ call assert_fails("echo winnr('-2xj')", 'E15:')
+ call assert_fails("echo winnr('j2j')", 'E15:')
+ call assert_fails("echo winnr('ll')", 'E15:')
+ call assert_fails("echo winnr('5')", 'E15:')
+ call assert_equal(4, winnr('0h'))
+
+ tabnew
+ call assert_equal(8, tabpagewinnr(1, 'j'))
+ call assert_equal(2, tabpagewinnr(1, 'k'))
+ call assert_equal(4, tabpagewinnr(1, 'h'))
+ call assert_equal(6, tabpagewinnr(1, 'l'))
+
+ only | tabonly
+endfunc
+
+func Test_window_colon_command()
+ " This was reading invalid memory.
+ exe "norm! v\<C-W>:\<C-U>echo v:version"
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 1cd5fb306a..b4585a72ef 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -36,13 +36,15 @@ func Test_writefile_fails_conversion()
if !has('multi_byte') || !has('iconv')
return
endif
+ " Without a backup file the write won't happen if there is a conversion
+ " error.
set nobackup nowritebackup
new
let contents = ["line one", "line two"]
call writefile(contents, 'Xfile')
edit Xfile
call setline(1, ["first line", "cannot convert \u010b", "third line"])
- call assert_fails('write ++enc=cp932')
+ call assert_fails('write ++enc=cp932', 'E513:')
call assert_equal(contents, readfile('Xfile'))
call delete('Xfile')
@@ -50,6 +52,27 @@ func Test_writefile_fails_conversion()
set backup& writebackup&
endfunc
+func Test_writefile_fails_conversion2()
+ if !has('iconv') || has('sun')
+ return
+ endif
+ " With a backup file the write happens even if there is a conversion error,
+ " but then the backup file must remain
+ set nobackup writebackup
+ let contents = ["line one", "line two"]
+ call writefile(contents, 'Xfile_conversion_err')
+ edit Xfile_conversion_err
+ call setline(1, ["first line", "cannot convert \u010b", "third line"])
+ set fileencoding=latin1
+ let output = execute('write')
+ call assert_match('CONVERSION ERROR', output)
+ call assert_equal(contents, readfile('Xfile_conversion_err~'))
+
+ call delete('Xfile_conversion_err')
+ call delete('Xfile_conversion_err~')
+ bwipe!
+endfunc
+
func SetFlag(timer)
let g:flag = 1
endfunc
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 3eb88366d6..6d9023bd79 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -10,6 +10,7 @@
#include "nvim/charset.h"
#include "nvim/main.h"
#include "nvim/aucmd.h"
+#include "nvim/ex_docmd.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -22,7 +23,7 @@
# include "tui/input.c.generated.h"
#endif
-void term_input_init(TermInput *input, Loop *loop)
+void tinput_init(TermInput *input, Loop *loop)
{
input->loop = loop;
input->paste_enabled = false;
@@ -48,20 +49,12 @@ void term_input_init(TermInput *input, Loop *loop)
int curflags = termkey_get_canonflags(input->tk);
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
// setup input handle
-#ifdef WIN32
- uv_tty_init(&loop->uv, &input->tty_in, 0, 1);
- uv_tty_set_mode(&input->tty_in, UV_TTY_MODE_RAW);
- rstream_init_stream(&input->read_stream,
- (uv_stream_t *)&input->tty_in,
- 0xfff);
-#else
rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff);
-#endif
// initialize a timer handle for handling ESC with libtermkey
time_watcher_init(loop, &input->timer_handle, input);
}
-void term_input_destroy(TermInput *input)
+void tinput_destroy(TermInput *input)
{
rbuffer_free(input->key_buffer);
uv_mutex_destroy(&input->key_buffer_mutex);
@@ -71,23 +64,23 @@ void term_input_destroy(TermInput *input)
termkey_destroy(input->tk);
}
-void term_input_start(TermInput *input)
+void tinput_start(TermInput *input)
{
- rstream_start(&input->read_stream, read_cb, input);
+ rstream_start(&input->read_stream, tinput_read_cb, input);
}
-void term_input_stop(TermInput *input)
+void tinput_stop(TermInput *input)
{
rstream_stop(&input->read_stream);
time_watcher_stop(&input->timer_handle);
}
-static void input_done_event(void **argv)
+static void tinput_done_event(void **argv)
{
input_done();
}
-static void wait_input_enqueue(void **argv)
+static void tinput_wait_enqueue(void **argv)
{
TermInput *input = argv[0];
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
@@ -106,12 +99,12 @@ static void wait_input_enqueue(void **argv)
uv_mutex_unlock(&input->key_buffer_mutex);
}
-static void flush_input(TermInput *input, bool wait_until_empty)
+static void tinput_flush(TermInput *input, bool wait_until_empty)
{
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
do {
uv_mutex_lock(&input->key_buffer_mutex);
- loop_schedule(&main_loop, event_create(wait_input_enqueue, 1, input));
+ loop_schedule(&main_loop, event_create(tinput_wait_enqueue, 1, input));
input->waiting = true;
while (input->waiting) {
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
@@ -120,13 +113,13 @@ static void flush_input(TermInput *input, bool wait_until_empty)
} while (rbuffer_size(input->key_buffer) > drain_boundary);
}
-static void enqueue_input(TermInput *input, char *buf, size_t size)
+static void tinput_enqueue(TermInput *input, char *buf, size_t size)
{
if (rbuffer_size(input->key_buffer) >
rbuffer_capacity(input->key_buffer) - 0xff) {
// don't ever let the buffer get too full or we risk putting incomplete keys
// into it
- flush_input(input, false);
+ tinput_flush(input, false);
}
rbuffer_write(input->key_buffer, buf, size);
}
@@ -146,7 +139,7 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
ptr++;
}
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
@@ -164,7 +157,7 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
}
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static void forward_mouse_event(TermInput *input, TermKeyKey *key)
@@ -236,7 +229,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
@@ -244,7 +237,7 @@ static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key);
}
-static void timer_cb(TimeWatcher *watcher, void *data);
+static void tinput_timer_cb(TimeWatcher *watcher, void *data);
static int get_key_code_timeout(void)
{
@@ -287,16 +280,16 @@ static void tk_getkeys(TermInput *input, bool force)
if (ms > 0) {
// Stop the current timer if already running
time_watcher_stop(&input->timer_handle);
- time_watcher_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0);
+ time_watcher_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0);
} else {
tk_getkeys(input, true);
}
}
-static void timer_cb(TimeWatcher *watcher, void *data)
+static void tinput_timer_cb(TimeWatcher *watcher, void *data)
{
tk_getkeys(data, true);
- flush_input(data, true);
+ tinput_flush(data, true);
}
/// Handle focus events.
@@ -332,7 +325,7 @@ static bool handle_bracketed_paste(TermInput *input)
if (input->paste_enabled == enable) {
return true;
}
- enqueue_input(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
+ tinput_enqueue(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
input->paste_enabled = enable;
return true;
}
@@ -357,15 +350,17 @@ static bool handle_forced_escape(TermInput *input)
static void set_bg_deferred(void **argv)
{
char *bgvalue = argv[0];
- if (starting) {
- // Wait until after startup, so OptionSet is triggered.
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
- return;
- }
if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) {
// Value differs, apply it.
- set_option_value("bg", 0L, bgvalue, 0);
- reset_option_was_set("bg");
+ if (starting) {
+ // Wait until after startup, so OptionSet is triggered.
+ do_cmdline_cmd((bgvalue[0] == 'l')
+ ? "autocmd VimEnter * ++once ++nested set bg=light"
+ : "autocmd VimEnter * ++once ++nested set bg=dark");
+ } else {
+ set_option_value("bg", 0L, bgvalue, 0);
+ reset_option_was_set("bg");
+ }
}
}
@@ -424,7 +419,8 @@ static bool handle_background_color(TermInput *input)
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
char *bgvalue = luminance < 0.5 ? "dark" : "light";
DLOG("bg response: %s", bgvalue);
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
+ loop_schedule_deferred(&main_loop,
+ event_create(set_bg_deferred, 1, bgvalue));
} else {
DLOG("failed to parse bg response");
}
@@ -433,8 +429,8 @@ static bool handle_background_color(TermInput *input)
return false;
}
-static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
- bool eof)
+static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_,
+ void *data, bool eof)
{
TermInput *input = data;
@@ -452,9 +448,10 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
// ls *.md | xargs nvim
input->in_fd = 2;
stream_close(&input->read_stream, NULL, NULL);
- multiqueue_put(input->loop->fast_events, restart_reading, 1, input);
+ multiqueue_put(input->loop->fast_events, tinput_restart_reading, 1,
+ input);
} else {
- loop_schedule(&main_loop, event_create(input_done_event, 0));
+ loop_schedule(&main_loop, event_create(tinput_done_event, 0));
}
return;
}
@@ -494,15 +491,15 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
}
}
} while (rbuffer_size(input->read_stream.buffer));
- flush_input(input, true);
+ tinput_flush(input, true);
// Make sure the next input escape sequence fits into the ring buffer
// without wrap around, otherwise it could be misinterpreted.
rbuffer_reset(input->read_stream.buffer);
}
-static void restart_reading(void **argv)
+static void tinput_restart_reading(void **argv)
{
TermInput *input = argv[0];
rstream_init_fd(input->loop, &input->read_stream, input->in_fd, 0xfff);
- rstream_start(&input->read_stream, read_cb, input);
+ rstream_start(&input->read_stream, tinput_read_cb, input);
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 573cc9d683..7d59cf5c6a 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -17,9 +17,6 @@ typedef struct term_input {
#endif
TimeWatcher timer_handle;
Loop *loop;
-#ifdef WIN32
- uv_tty_t tty_in;
-#endif
Stream read_stream;
RBuffer *key_buffer;
uv_mutex_t key_buffer_mutex;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 3e7a3b1ba1..42e5b9b270 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -342,7 +342,7 @@ static void tui_terminal_start(UI *ui)
terminfo_start(ui);
tui_guess_size(ui);
signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
- term_input_start(&data->input);
+ tinput_start(&data->input);
}
static void tui_terminal_after_startup(UI *ui)
@@ -364,7 +364,7 @@ static void tui_terminal_stop(UI *ui)
ui->data = NULL; // Flag UI as "stopped".
return;
}
- term_input_stop(&data->input);
+ tinput_stop(&data->input);
signal_watcher_stop(&data->winch_handle);
terminfo_stop(ui);
ugrid_free(&data->grid);
@@ -404,7 +404,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
#endif
- term_input_init(&data->input, &tui_loop);
+ tinput_init(&data->input, &tui_loop);
tui_terminal_start(ui);
// Allow main thread to continue, we are ready to handle UI callbacks.
@@ -429,7 +429,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
}
ui_bridge_stopped(bridge);
- term_input_destroy(&data->input);
+ tinput_destroy(&data->input);
signal_watcher_stop(&data->cont_handle);
signal_watcher_close(&data->cont_handle, NULL);
signal_watcher_close(&data->winch_handle, NULL);
@@ -704,14 +704,6 @@ static void cursor_goto(UI *ui, int row, int col)
// even less expensive than using BSes or CUB.
unibi_out(ui, unibi_carriage_return);
ugrid_goto(grid, grid->row, 0);
- } else if (col > grid->col) {
- int n = col - grid->col;
- if (n <= (row == grid->row ? 4 : 2)
- && cheap_to_print(ui, grid->row, grid->col, n)) {
- UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, {
- print_cell(ui, cell);
- });
- }
}
if (row == grid->row) {
if (col < grid->col
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index d07ea3179e..7dbb8ec790 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -141,6 +141,17 @@ bool ui_rgb_attached(void)
return false;
}
+/// Returns true if any UI requested `override=true`.
+bool ui_override(void)
+{
+ for (size_t i = 1; i < ui_count; i++) {
+ if (uis[i]->override) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ui_active(void)
{
return ui_count > 1;
@@ -173,12 +184,13 @@ void ui_refresh(void)
ext_widgets[i] = true;
}
+ bool inclusive = ui_override();
for (size_t i = 0; i < ui_count; i++) {
UI *ui = uis[i];
width = MIN(ui->width, width);
height = MIN(ui->height, height);
for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
- ext_widgets[j] &= ui->ui_ext[j];
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive);
}
}
@@ -431,6 +443,7 @@ Array ui_array(void)
PUT(info, "width", INTEGER_OBJ(ui->width));
PUT(info, "height", INTEGER_OBJ(ui->height));
PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb));
+ PUT(info, "override", BOOLEAN_OBJ(ui->override));
for (UIExtension j = 0; j < kUIExtCount; j++) {
if (ui_ext_names[j][0] != '_' || ui->ui_ext[j]) {
PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j]));
@@ -460,7 +473,9 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
if (wp->w_floating) {
if (width != wp->w_width && height != wp->w_height) {
- win_config_float(wp, (int)width, (int)height, wp->w_float_config);
+ wp->w_float_config.width = width;
+ wp->w_float_config.height = height;
+ win_config_float(wp, wp->w_float_config);
}
} else {
// non-positive indicates no request
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 3f6b3babad..e1dd18a289 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -48,9 +48,11 @@ typedef int LineFlags;
struct ui_t {
bool rgb;
+ bool override; ///< Force highest-requested UI capabilities.
bool composed;
- bool ui_ext[kUIExtCount]; ///< Externalized widgets
- int width, height;
+ bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities.
+ int width;
+ int height;
void *data;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 9ad3f851ad..e24ab11a3a 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -3,6 +3,8 @@
// Compositor: merge floating grids with the main grid for display in
// TUI and non-multigrid UIs.
+//
+// Layer-based compositing: https://en.wikipedia.org/wiki/Digital_compositing
#include <assert.h>
#include <stdbool.h>
@@ -104,6 +106,9 @@ bool ui_comp_should_draw(void)
return composed_uis != 0 && valid_screen;
}
+/// Places `grid` at (col,row) position with (width * height) size.
+/// Adds `grid` as the top layer if it is a new layer.
+///
/// TODO(bfredl): later on the compositor should just use win_float_pos events,
/// though that will require slight event order adjustment: emit the win_pos
/// events in the beginning of update_screen(0), rather than in ui_flush()
@@ -158,6 +163,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
if (insert_at < kv_size(layers)-1) {
for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
kv_A(layers, i) = kv_A(layers, i-1);
+ kv_A(layers, i)->comp_index = i;
}
kv_A(layers, insert_at) = grid;
}
diff --git a/src/nvim/ui_compositor.h b/src/nvim/ui_compositor.h
index b3780db532..70e01c3a21 100644
--- a/src/nvim/ui_compositor.h
+++ b/src/nvim/ui_compositor.h
@@ -1,5 +1,3 @@
-// Bridge for communication between a UI thread and nvim core.
-// Used by the built-in TUI and libnvim-based UIs.
#ifndef NVIM_UI_COMPOSITOR_H
#define NVIM_UI_COMPOSITOR_H
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index df0507ed41..10996d99d5 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -83,13 +83,11 @@
#include <string.h>
#include <fcntl.h>
-#include "nvim/vim.h"
+#include "nvim/buffer.h"
#include "nvim/ascii.h"
#include "nvim/undo.h"
-#include "nvim/macros.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/buffer_updates.h"
@@ -102,8 +100,6 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -1135,8 +1131,9 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
/* If there is no undo information at all, quit here after deleting any
* existing undo file. */
if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) {
- if (p_verbose > 0)
- verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
+ if (p_verbose > 0) {
+ verb_msg(_("Skipping undo file write, nothing to undo"));
+ }
goto theend;
}
@@ -1822,7 +1819,7 @@ void undo_time(long step, bool sec, bool file, bool absolute)
u_header_T *uhp = NULL;
u_header_T *last;
int mark;
- int nomark;
+ int nomark = 0; // shut up compiler
int round;
bool dosec = sec;
bool dofile = file;
@@ -2154,7 +2151,7 @@ static void u_undoredo(int undo, bool do_buf_event)
int new_flags;
fmark_T namedm[NMARKS];
visualinfo_T visualinfo;
- int empty_buffer; /* buffer became empty */
+ bool empty_buffer; // buffer became empty
u_header_T *curhead = curbuf->b_u_curhead;
/* Don't want autocommands using the undo structures here, they are
@@ -2221,7 +2218,7 @@ static void u_undoredo(int undo, bool do_buf_event)
}
}
- empty_buffer = FALSE;
+ empty_buffer = false;
/* delete the lines between top and bot and save them in newarray */
if (oldsize > 0) {
@@ -2232,9 +2229,10 @@ static void u_undoredo(int undo, bool do_buf_event)
newarray[i] = u_save_line(lnum);
/* remember we deleted the last line in the buffer, and a
* dummy empty line will be inserted */
- if (curbuf->b_ml.ml_line_count == 1)
- empty_buffer = TRUE;
- ml_delete(lnum, FALSE);
+ if (curbuf->b_ml.ml_line_count == 1) {
+ empty_buffer = true;
+ }
+ ml_delete(lnum, false);
}
} else
newarray = NULL;
@@ -2249,7 +2247,7 @@ static void u_undoredo(int undo, bool do_buf_event)
if (empty_buffer && lnum == 0) {
ml_replace((linenr_T)1, uep->ue_array[i], true);
} else {
- ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
+ ml_append(lnum, uep->ue_array[i], (colnr_T)0, false);
}
xfree(uep->ue_array[i]);
}
@@ -2452,7 +2450,9 @@ static void u_undo_end(
}
}
- smsg(_("%" PRId64 " %s; %s #%" PRId64 " %s"),
+ smsg_attr_keep(
+ 0,
+ _("%" PRId64 " %s; %s #%" PRId64 " %s"),
u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
_(msgstr),
did_undo ? _("before") : _("after"),
@@ -2585,9 +2585,13 @@ static void u_add_time(char_u *buf, size_t buflen, time_t tt)
else
/* longer ago */
(void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
- } else
- vim_snprintf((char *)buf, buflen, _("%" PRId64 " seconds ago"),
- (int64_t)(time(NULL) - tt));
+ } else {
+ int64_t seconds = time(NULL) - tt;
+ vim_snprintf((char *)buf, buflen,
+ NGETTEXT("%" PRId64 " second ago",
+ "%" PRId64 " seconds ago", (uint32_t)seconds),
+ seconds);
+ }
}
/*
diff --git a/src/nvim/version.c b/src/nvim/version.c
index b7c9140b7f..b18cfc6dde 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -158,7 +158,7 @@ static const int included_patches[] = {
1766,
1765,
1764,
- // 1763,
+ 1763,
// 1762,
// 1761,
1760,
@@ -393,7 +393,7 @@ static const int included_patches[] = {
// 1531,
1530,
// 1529,
- // 1528,
+ 1528,
// 1527,
// 1526,
// 1525,
@@ -474,7 +474,7 @@ static const int included_patches[] = {
1450,
// 1449,
// 1448,
- // 1447,
+ 1447,
1446,
1445,
1444,
@@ -549,7 +549,7 @@ static const int included_patches[] = {
// 1375,
1374,
1373,
- // 1372,
+ 1372,
// 1371,
1370,
1369,
@@ -585,7 +585,7 @@ static const int included_patches[] = {
// 1339,
1338,
1337,
- // 1336,
+ 1336,
// 1335,
// 1334,
1333,
@@ -768,7 +768,7 @@ static const int included_patches[] = {
1156,
1155,
1154,
- // 1153,
+ 1153,
1152,
1151,
1150,
@@ -807,7 +807,7 @@ static const int included_patches[] = {
// 1117,
// 1116,
1115,
- // 1114,
+ 1114,
// 1113,
// 1112,
1111,
@@ -844,11 +844,11 @@ static const int included_patches[] = {
// 1080,
// 1079,
1078,
- // 1077,
+ 1077,
// 1076,
// 1075,
// 1074,
- // 1073,
+ 1073,
1072,
1071,
// 1070,
@@ -876,7 +876,7 @@ static const int included_patches[] = {
1048,
1047,
1046,
- // 1045,
+ 1045,
1044,
1043,
1042,
@@ -1193,7 +1193,7 @@ static const int included_patches[] = {
731,
// 730,
729,
- // 728,
+ 728,
727,
726,
// 725,
@@ -1212,11 +1212,11 @@ static const int included_patches[] = {
// 712,
711,
710,
- // 709,
+ 709,
708,
707,
706,
- // 705,
+ 705,
704,
703,
// 702,
@@ -1240,7 +1240,7 @@ static const int included_patches[] = {
684,
// 683,
682,
- // 681,
+ 681,
680,
679,
678,
@@ -1275,10 +1275,10 @@ static const int included_patches[] = {
649,
648,
// 647,
- // 646,
- // 645,
- // 644,
- // 643,
+ 646,
+ 645,
+ 644,
+ 643,
642,
641,
640,
@@ -1292,7 +1292,7 @@ static const int included_patches[] = {
632,
631,
630,
- // 629,
+ 629,
628,
627,
626,
@@ -1329,7 +1329,7 @@ static const int included_patches[] = {
595,
594,
593,
- // 592,
+ 592,
591,
590,
589,
@@ -1349,7 +1349,7 @@ static const int included_patches[] = {
575,
574,
573,
- // 572,
+ 572,
571,
570,
569,
@@ -1415,9 +1415,9 @@ static const int included_patches[] = {
509,
508,
507,
- // 506,
+ 506,
505,
- // 504,
+ 504,
503,
502,
501,
@@ -1508,9 +1508,9 @@ static const int included_patches[] = {
416,
415,
414,
- // 413,
- // 412,
- // 411,
+ 413,
+ 412,
+ 411,
410,
409,
408,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 767936ecee..3e0a5907be 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -277,7 +277,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Enums need a typecast to be used as array index (for Ultrix).
#define HL_ATTR(n) highlight_attr[(int)(n)]
-#define TERM_STR(n) term_strings[(int)(n)]
/// Maximum number of bytes in a multi-byte character. It can be one 32-bit
/// character of up to 6 bytes, or one 16-bit character of up to three bytes
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 83ddf534cf..6bc082ffb2 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -502,10 +502,11 @@ wingotofile:
break;
}
FloatConfig config = FLOAT_CONFIG_INIT;
+ config.width = curwin->w_width;
+ config.height = curwin->w_height;
config.external = true;
Error err = ERROR_INIT;
- if (!win_new_float(curwin, curwin->w_width, curwin->w_height, config,
- &err)) {
+ if (!win_new_float(curwin, config, &err)) {
EMSG(err.msg);
api_clear_error(&err);
beep_flush();
@@ -538,12 +539,9 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
/// float. It must then already belong to the current tabpage!
///
/// config must already have been validated!
-win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
- Error *err)
+win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
{
- bool new = false;
if (wp == NULL) {
- new = true;
wp = win_alloc(lastwin_nofloating(), false);
win_init(wp, curwin, 0);
} else {
@@ -569,29 +567,30 @@ win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
wp->w_floating = 1;
wp->w_status_height = 0;
wp->w_vsep_width = 0;
- win_config_float(wp, width, height, config);
+
+ // TODO(bfredl): use set_option_to() after merging #9110 ?
+ wp->w_p_nu = false;
+ wp->w_allbuf_opt.wo_nu = false;
+ win_config_float(wp, fconfig);
wp->w_pos_changed = true;
redraw_win_later(wp, VALID);
- if (new) {
- win_enter(wp, false);
- }
return wp;
}
-void win_config_float(win_T *wp, int width, int height,
- FloatConfig config)
+void win_config_float(win_T *wp, FloatConfig fconfig)
{
- wp->w_height = MAX(height, 1);
- wp->w_width = MAX(width, 2);
+ wp->w_width = MAX(fconfig.width, 1);
+ wp->w_height = MAX(fconfig.height, 1);
- if (config.relative == kFloatRelativeCursor) {
- config.relative = kFloatRelativeWindow;
- config.row += curwin->w_wrow;
- config.col += curwin->w_wcol;
- config.window = curwin->handle;
+ if (fconfig.relative == kFloatRelativeCursor) {
+ fconfig.relative = kFloatRelativeWindow;
+ fconfig.row += curwin->w_wrow;
+ fconfig.col += curwin->w_wcol;
+ fconfig.window = curwin->handle;
}
- wp->w_float_config = config;
+ bool change_external = fconfig.external != wp->w_float_config.external;
+ wp->w_float_config = fconfig;
if (!ui_has(kUIMultigrid)) {
wp->w_height = MIN(wp->w_height, Rows-1);
@@ -601,6 +600,10 @@ void win_config_float(win_T *wp, int width, int height,
win_set_inner_size(wp);
must_redraw = MAX(must_redraw, VALID);
wp->w_pos_changed = true;
+ if (change_external) {
+ wp->w_hl_needs_update = true;
+ redraw_win_later(wp, NOT_VALID);
+ }
}
static void ui_ext_win_position(win_T *wp)
@@ -610,28 +613,25 @@ static void ui_ext_win_position(win_T *wp)
wp->w_wincol, wp->w_width, wp->w_height);
return;
}
- const char *const anchor_str[] = {
- "NW",
- "NE",
- "SW",
- "SE"
- };
FloatConfig c = wp->w_float_config;
if (!c.external) {
ScreenGrid *grid = &default_grid;
- int row = c.row, col = c.col;
+ float row = c.row, col = c.col;
if (c.relative == kFloatRelativeWindow) {
Error dummy = ERROR_INIT;
win_T *win = find_window_by_handle(c.window, &dummy);
if (win) {
grid = &win->w_grid;
- screen_adjust_grid(&grid, &row, &col);
+ int row_off = 0, col_off = 0;
+ screen_adjust_grid(&grid, &row_off, &col_off);
+ row += row_off;
+ col += col_off;
}
api_clear_error(&dummy);
}
if (ui_has(kUIMultigrid)) {
- String anchor = cstr_to_string(anchor_str[c.anchor]);
+ String anchor = cstr_to_string(float_anchor_str[c.anchor]);
ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle,
row, col, c.focusable);
} else {
@@ -640,16 +640,16 @@ static void ui_ext_win_position(win_T *wp)
bool east = c.anchor & kFloatAnchorEast;
bool south = c.anchor & kFloatAnchorSouth;
- row -= (south ? wp->w_height : 0);
- col -= (east ? wp->w_width : 0);
- row = MAX(MIN(row, Rows-wp->w_height-1), 0);
- col = MAX(MIN(col, Columns-wp->w_width), 0);
- wp->w_winrow = row;
- wp->w_wincol = col;
+ int comp_row = (int)row - (south ? wp->w_height : 0);
+ int comp_col = (int)col - (east ? wp->w_width : 0);
+ comp_row = MAX(MIN(comp_row, Rows-wp->w_height-1), 0);
+ comp_col = MAX(MIN(comp_col, Columns-wp->w_width), 0);
+ wp->w_winrow = comp_row;
+ wp->w_wincol = comp_col;
bool valid = (wp->w_redr_type == 0);
bool on_top = (curwin == wp) || !curwin->w_floating;
- ui_comp_put_grid(&wp->w_grid, row, col, wp->w_height, wp->w_width,
- valid, on_top);
+ ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height,
+ wp->w_width, valid, on_top);
if (!valid) {
wp->w_grid.valid = false;
redraw_win_later(wp, NOT_VALID);
@@ -668,14 +668,14 @@ static bool parse_float_anchor(String anchor, FloatAnchor *out)
*out = (FloatAnchor)0;
}
char *str = anchor.data;
- if (!STRICMP(str, "NW")) {
- *out = kFloatAnchorNW;
- } else if (!STRICMP(str, "NE")) {
- *out = kFloatAnchorNE;
- } else if (!STRICMP(str, "SW")) {
- *out = kFloatAnchorSW;
- } else if (!STRICMP(str, "SE")) {
- *out = kFloatAnchorSE;
+ if (striequal(str, "NW")) {
+ *out = 0; // NW is the default
+ } else if (striequal(str, "NE")) {
+ *out = kFloatAnchorEast;
+ } else if (striequal(str, "SW")) {
+ *out = kFloatAnchorSouth;
+ } else if (striequal(str, "SE")) {
+ *out = kFloatAnchorSouth | kFloatAnchorEast;
} else {
return false;
}
@@ -684,15 +684,12 @@ static bool parse_float_anchor(String anchor, FloatAnchor *out)
static bool parse_float_relative(String relative, FloatRelative *out)
{
- if (relative.size == 0) {
- *out = (FloatRelative)0;
- }
char *str = relative.data;
- if (!STRICMP(str, "editor")) {
+ if (striequal(str, "editor")) {
*out = kFloatRelativeEditor;
- } else if (!STRICMP(str, "win")) {
+ } else if (striequal(str, "win")) {
*out = kFloatRelativeWindow;
- } else if (!STRICMP(str, "cursor")) {
+ } else if (striequal(str, "cursor")) {
*out = kFloatRelativeCursor;
} else {
return false;
@@ -700,11 +697,14 @@ static bool parse_float_relative(String relative, FloatRelative *out)
return true;
}
-bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
+bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
Error *err)
{
+ // TODO(bfredl): use a get/has_key interface instead and get rid of extra
+ // flags
bool has_row = false, has_col = false, has_relative = false;
bool has_external = false, has_window = false;
+ bool has_width = false, has_height = false;
for (size_t i = 0; i < config.size; i++) {
char *key = config.items[i].key.data;
@@ -712,109 +712,137 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
if (!strcmp(key, "row")) {
has_row = true;
if (val.type == kObjectTypeInteger) {
- out->row = val.data.integer;
+ fconfig->row = val.data.integer;
} else if (val.type == kObjectTypeFloat) {
- out->row = val.data.floating;
+ fconfig->row = val.data.floating;
} else {
api_set_error(err, kErrorTypeValidation,
- "'row' option has to be Integer or Float");
+ "'row' key must be Integer or Float");
return false;
}
} else if (!strcmp(key, "col")) {
has_col = true;
if (val.type == kObjectTypeInteger) {
- out->col = val.data.integer;
+ fconfig->col = val.data.integer;
} else if (val.type == kObjectTypeFloat) {
- out->col = val.data.floating;
+ fconfig->col = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ } else if (strequal(key, "width")) {
+ has_width = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->width = val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'width' key must be a positive Integer");
+ return false;
+ }
+ } else if (strequal(key, "height")) {
+ has_height = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->height= val.data.integer;
} else {
api_set_error(err, kErrorTypeValidation,
- "'col' option has to be Integer or Float");
+ "'height' key must be a positive Integer");
return false;
}
} else if (!strcmp(key, "anchor")) {
if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
- "'anchor' option has to be String");
+ "'anchor' key must be String");
return false;
}
- if (!parse_float_anchor(val.data.string, &out->anchor)) {
+ if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'anchor' option");
+ "Invalid value of 'anchor' key");
return false;
}
} else if (!strcmp(key, "relative")) {
- has_relative = true;
if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
- "'relative' option has to be String");
+ "'relative' key must be String");
return false;
}
- if (!parse_float_relative(val.data.string, &out->relative)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'relative' option");
- return false;
+ // ignore empty string, to match nvim_win_get_config
+ if (val.data.string.size > 0) {
+ has_relative = true;
+ if (!parse_float_relative(val.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'relative' key");
+ return false;
+ }
}
} else if (!strcmp(key, "win")) {
has_window = true;
if (val.type != kObjectTypeInteger
&& val.type != kObjectTypeWindow) {
api_set_error(err, kErrorTypeValidation,
- "'win' option has to be Integer or Window");
+ "'win' key must be Integer or Window");
return false;
}
- out->window = val.data.integer;
+ fconfig->window = val.data.integer;
} else if (!strcmp(key, "external")) {
if (val.type == kObjectTypeInteger) {
- out->external = val.data.integer;
+ fconfig->external = val.data.integer;
} else if (val.type == kObjectTypeBoolean) {
- out->external = val.data.boolean;
+ fconfig->external = val.data.boolean;
} else {
api_set_error(err, kErrorTypeValidation,
- "'external' option has to be Boolean");
+ "'external' key must be Boolean");
return false;
}
- has_external = out->external;
+ has_external = fconfig->external;
} else if (!strcmp(key, "focusable")) {
if (val.type == kObjectTypeInteger) {
- out->focusable = val.data.integer;
+ fconfig->focusable = val.data.integer;
} else if (val.type == kObjectTypeBoolean) {
- out->focusable = val.data.boolean;
+ fconfig->focusable = val.data.boolean;
} else {
api_set_error(err, kErrorTypeValidation,
- "'focusable' option has to be Boolean");
+ "'focusable' key must be Boolean");
return false;
}
} else {
api_set_error(err, kErrorTypeValidation,
- "Invalid options key '%s'", key);
+ "Invalid key '%s'", key);
return false;
}
}
- if (has_window && !(has_relative && out->relative == kFloatRelativeWindow)) {
+ if (has_window && !(has_relative
+ && fconfig->relative == kFloatRelativeWindow)) {
api_set_error(err, kErrorTypeValidation,
- "'win' option is only valid with relative='win'");
+ "'win' key is only valid with relative='win'");
return false;
}
- if ((has_relative && out->relative == kFloatRelativeWindow)
- && (!has_window || out->window == 0)) {
- out->window = curwin->handle;
+ if ((has_relative && fconfig->relative == kFloatRelativeWindow)
+ && (!has_window || fconfig->window == 0)) {
+ fconfig->window = curwin->handle;
}
if (has_relative && has_external) {
api_set_error(err, kErrorTypeValidation,
- "Only one of 'relative' and 'external' should be used");
+ "Only one of 'relative' and 'external' must be used");
return false;
} else if (!reconf && !has_relative && !has_external) {
api_set_error(err, kErrorTypeValidation,
"One of 'relative' and 'external' must be used");
return false;
} else if (has_relative) {
- out->external = false;
+ fconfig->external = false;
}
- if (out->external && !ui_has(kUIMultigrid)) {
+ if (!reconf && !(has_height && has_width)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'width' and 'height'");
+ return false;
+ }
+
+ if (fconfig->external && !ui_has(kUIMultigrid)) {
api_set_error(err, kErrorTypeValidation,
"UI doesn't support external windows");
return false;
@@ -1121,6 +1149,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (wp->w_floating) {
new_frame(wp);
wp->w_floating = false;
+ // non-floating window doesn't store float config.
+ wp->w_float_config = FLOAT_CONFIG_INIT;
}
/*
@@ -2265,8 +2295,7 @@ int win_close(win_T *win, bool free_buf)
EMSG(_("E813: Cannot close autocmd window"));
return FAIL;
}
- if ((firstwin == aucmd_win || lastwin_nofloating() == aucmd_win)
- && one_window()) {
+ if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) {
EMSG(_("E814: Cannot close window, only autocmd window would remain"));
return FAIL;
}
@@ -2299,10 +2328,10 @@ int win_close(win_T *win, bool free_buf)
if (!win->w_floating) {
wp = frame2win(win_altframe(win, NULL));
} else {
- if (win_valid(prevwin)) {
+ if (win_valid(prevwin) && prevwin != win) {
wp = prevwin;
} else {
- wp = curtab->tp_firstwin;
+ wp = firstwin;
}
}
@@ -2448,7 +2477,7 @@ int win_close(win_T *win, bool free_buf)
}
if (!was_floating) {
- if (p_ea && (*p_ead == 'b' || *p_ead == dir)) {
+ if (!curwin->w_floating && p_ea && (*p_ead == 'b' || *p_ead == dir)) {
// If the frame of the closed window contains the new current window,
// only resize that frame. Otherwise resize all windows.
win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
@@ -2561,15 +2590,12 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
free_tabpage(tp);
}
-/*
- * Free the memory used for a window.
- * Returns a pointer to the window that got the freed up space.
- */
-static win_T *
-win_free_mem (
+// Free the memory used for a window.
+// Returns a pointer to the window that got the freed up space.
+static win_T *win_free_mem(
win_T *win,
- int *dirp, /* set to 'v' or 'h' for direction if 'ea' */
- tabpage_T *tp /* tab page "win" is in, NULL for current */
+ int *dirp, // set to 'v' or 'h' for direction if 'ea'
+ tabpage_T *tp // tab page "win" is in, NULL for current
)
{
frame_T *frp;
@@ -2581,18 +2607,20 @@ win_free_mem (
wp = winframe_remove(win, dirp, tp);
xfree(frp);
} else {
- if (win_valid(prevwin)) {
+ *dirp = 'h'; // Dummy value.
+ if (win_valid(prevwin) && prevwin != win) {
wp = prevwin;
} else {
- wp = curtab->tp_firstwin;
+ wp = firstwin;
}
}
win_free(win, tp);
- /* When deleting the current window of another tab page select a new
- * current window. */
- if (tp != NULL && win == tp->tp_curwin)
+ // When deleting the current window of another tab page select a new
+ // current window.
+ if (tp != NULL && win == tp->tp_curwin) {
tp->tp_curwin = wp;
+ }
return wp;
}
@@ -2665,9 +2693,9 @@ winframe_remove (
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
- if (frp->fr_win != NULL && !frp->fr_win->w_p_wfh) {
+ if (!frame_fixed_height(frp)) {
frp2 = frp;
- wp = frp->fr_win;
+ wp = frame2win(frp2);
break;
}
frp = frp->fr_prev;
@@ -2694,9 +2722,9 @@ winframe_remove (
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
- if (frp->fr_win != NULL && !frp->fr_win->w_p_wfw) {
+ if (!frame_fixed_width(frp)) {
frp2 = frp;
- wp = frp->fr_win;
+ wp = frame2win(frp2);
break;
}
frp = frp->fr_prev;
@@ -3384,16 +3412,18 @@ int win_alloc_first(void)
return OK;
}
-/*
- * Init "aucmd_win". This can only be done after the first
- * window is fully initialized, thus it can't be in win_alloc_first().
- */
+// Init `aucmd_win`. This can only be done after the first window
+// is fully initialized, thus it can't be in win_alloc_first().
void win_alloc_aucmd_win(void)
{
- aucmd_win = win_alloc(NULL, TRUE);
- win_init_some(aucmd_win, curwin);
+ Error err = ERROR_INIT;
+ FloatConfig fconfig = FLOAT_CONFIG_INIT;
+ fconfig.width = Columns;
+ fconfig.height = 5;
+ fconfig.focusable = false;
+ aucmd_win = win_new_float(NULL, fconfig, &err);
+ aucmd_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win);
- new_frame(aucmd_win);
}
/*
@@ -3764,6 +3794,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
* the frames for that. When the Vim window was resized need to update
* frame sizes too. Use the stored value of p_ch, so that it can be
* different for each tab page. */
+ if (p_ch != curtab->tp_ch_used) {
+ clear_cmdline = true;
+ }
p_ch = curtab->tp_ch_used;
if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
))
@@ -3806,7 +3839,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
for (win_T *wp = firstwin; wp; wp = wp->w_next) {
if (wp->w_floating && !wp->w_float_config.external) {
- win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config);
+ win_config_float(wp, wp->w_float_config);
}
wp->w_pos_changed = true;
}
@@ -3818,7 +3851,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
*/
void goto_tabpage(int n)
{
- tabpage_T *tp;
+ tabpage_T *tp = NULL; // shut up compiler
tabpage_T *ttp;
int i;
@@ -4006,24 +4039,25 @@ tabpage_T *win_find_tabpage(win_T *win)
return NULL;
}
-/*
- * Move to window above or below "count" times.
- */
-static void
-win_goto_ver (
- int up, /* TRUE to go to win above */
- long count
-)
+/// Get the above or below neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param up true for the above neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
@@ -4033,14 +4067,17 @@ win_goto_ver (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (up)
+ }
+ if (up) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4054,11 +4091,12 @@ win_goto_ver (
}
fr = nfr->fr_child;
if (nfr->fr_layout == FR_ROW) {
- /* Find the frame at the cursor row. */
+ // Find the frame at the cursor row.
while (fr->fr_next != NULL
&& frame2win(fr)->w_wincol + fr->fr_width
- <= curwin->w_wincol + curwin->w_wcol)
+ <= wp->w_wincol + wp->w_wcol) {
fr = fr->fr_next;
+ }
}
if (nfr->fr_layout == FR_COL && up)
while (fr->fr_next != NULL)
@@ -4067,28 +4105,40 @@ win_goto_ver (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
}
-/*
- * Move to left or right window.
- */
-static void
-win_goto_hor (
- int left, /* TRUE to go to left win */
- long count
-)
+/// Move to window above or below "count" times.
+///
+/// @param up true to go to win above
+/// @param count go count times into direction
+static void win_goto_ver(bool up, long count)
+{
+ win_T *win = win_vert_neighbor(curtab, curwin, up, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
+}
+
+/// Get the left or right neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param left true for the left neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
@@ -4098,14 +4148,17 @@ win_goto_hor (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (left)
+ }
+ if (left) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4122,7 +4175,7 @@ win_goto_hor (
/* Find the frame at the cursor row. */
while (fr->fr_next != NULL
&& frame2win(fr)->w_winrow + fr->fr_height
- <= curwin->w_winrow + curwin->w_wrow)
+ <= wp->w_winrow + wp->w_wrow)
fr = fr->fr_next;
}
if (nfr->fr_layout == FR_ROW && left)
@@ -4132,8 +4185,19 @@ win_goto_hor (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
+}
+
+/// Move to left or right window.
+///
+/// @param left true to go to left window
+/// @param count go count times into direction
+static void win_goto_hor(bool left, long count)
+{
+ win_T *win = win_horz_neighbor(curtab, curwin, left, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
}
/*
@@ -4242,9 +4306,12 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
}
if (trigger_enter_autocmds) {
- apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
- if (other_buffer)
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
+ apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ if (other_buffer) {
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ }
+ apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
maketitle();
@@ -4374,6 +4441,7 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->w_cursor.lnum = 1;
new_wp->w_scbind_pos = 1;
new_wp->w_floating = 0;
+ new_wp->w_float_config = FLOAT_CONFIG_INIT;
/* We won't calculate w_fraction until resizing the window */
new_wp->w_fraction = 0;
@@ -4642,8 +4710,12 @@ void win_size_restore(garray_T *gap)
{
int i = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
- win_setheight_win(((int *)gap->ga_data)[i++], wp);
+ int width = ((int *)gap->ga_data)[i++];
+ int height = ((int *)gap->ga_data)[i++];
+ if (!wp->w_floating) {
+ frame_setwidth(wp->w_frame, width);
+ win_setheight_win(height, wp);
+ }
}
}
/* recompute the window positions */
@@ -4666,7 +4738,7 @@ int win_comp_pos(void)
// Too often, but when we support anchoring floats to split windows,
// this will be needed
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
- win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config);
+ win_config_float(wp, wp->w_float_config);
}
return row;
@@ -4739,7 +4811,8 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) {
if (win->w_float_config.external) {
- win_config_float(win, win->w_width, height, win->w_float_config);
+ win->w_float_config.height = height;
+ win_config_float(win, win->w_float_config);
} else {
beep_flush();
return;
@@ -4944,7 +5017,8 @@ void win_setwidth_win(int width, win_T *wp)
}
if (wp->w_floating) {
if (wp->w_float_config.external) {
- win_config_float(wp, width, wp->w_height, wp->w_float_config);
+ wp->w_float_config.width = width;
+ win_config_float(wp, wp->w_float_config);
} else {
beep_flush();
return;
@@ -5770,7 +5844,7 @@ last_status (
{
/* Don't make a difference between horizontal or vertical split. */
last_status_rec(topframe, (p_ls == 2
- || (p_ls == 1 && (morewin || !ONE_WINDOW))));
+ || (p_ls == 1 && (morewin || !one_window()))));
}
static void last_status_rec(frame_T *fr, int statusline)
@@ -5889,16 +5963,40 @@ void check_lnums(int do_curwin)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) {
+ // save the original cursor position and topline
+ wp->w_save_cursor.w_cursor_save = wp->w_cursor;
+ wp->w_save_cursor.w_topline_save = wp->w_topline;
+
if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
if (wp->w_topline > curbuf->b_ml.ml_line_count) {
wp->w_topline = curbuf->b_ml.ml_line_count;
}
+
+ // save the corrected cursor position and topline
+ wp->w_save_cursor.w_cursor_corr = wp->w_cursor;
+ wp->w_save_cursor.w_topline_corr = wp->w_topline;
}
}
}
+/// Reset cursor and topline to its stored values from check_lnums().
+/// check_lnums() must have been called first!
+void reset_lnums(void)
+{
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == curbuf) {
+ // Restore the value if the autocommand didn't change it.
+ if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)) {
+ wp->w_cursor = wp->w_save_cursor.w_cursor_save;
+ }
+ if (wp->w_save_cursor.w_topline_corr == wp->w_topline) {
+ wp->w_topline = wp->w_save_cursor.w_topline_save;
+ }
+ }
+ }
+}
/*
* A snapshot of the window sizes, to restore them after closing the help
@@ -6044,7 +6142,7 @@ static win_T *get_snapshot_focus(int idx)
}
}
- return sn->fr_win;
+ return win_valid(sn->fr_win) ? sn->fr_win : NULL;
}
/*