aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt20
-rw-r--r--src/nvim/api/deprecated.c40
-rw-r--r--src/nvim/api/extmark.c24
-rw-r--r--src/nvim/api/keysets_defs.h1
-rw-r--r--src/nvim/api/options.c55
-rw-r--r--src/nvim/api/private/helpers.c27
-rw-r--r--src/nvim/api/private/helpers.h6
-rw-r--r--src/nvim/api/ui_events.in.h4
-rw-r--r--src/nvim/api/vim.c64
-rw-r--r--src/nvim/api/win_config.c13
-rw-r--r--src/nvim/auevents.lua6
-rw-r--r--src/nvim/autocmd.c16
-rw-r--r--src/nvim/buffer.c12
-rw-r--r--src/nvim/buffer_defs.h12
-rw-r--r--src/nvim/bufwrite.c14
-rw-r--r--src/nvim/change.c4
-rw-r--r--src/nvim/charset.c196
-rw-r--r--src/nvim/cmdexpand.c69
-rw-r--r--src/nvim/cmdexpand_defs.h2
-rw-r--r--src/nvim/cmdhist.c49
-rw-r--r--src/nvim/cmdhist.h1
-rw-r--r--src/nvim/decoration_defs.h2
-rw-r--r--src/nvim/diff.c6
-rw-r--r--src/nvim/digraph.c8
-rw-r--r--src/nvim/drawline.c50
-rw-r--r--src/nvim/drawscreen.c57
-rw-r--r--src/nvim/errors.h8
-rw-r--r--src/nvim/eval.c81
-rw-r--r--src/nvim/eval.lua124
-rw-r--r--src/nvim/eval/funcs.c14
-rw-r--r--src/nvim/eval/vars.c21
-rw-r--r--src/nvim/ex_cmds.c18
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_docmd.c232
-rw-r--r--src/nvim/ex_getln.c204
-rw-r--r--src/nvim/ex_getln_defs.h6
-rw-r--r--src/nvim/file_search.c10
-rw-r--r--src/nvim/fileio.c28
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua2
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua4
-rw-r--r--src/nvim/generators/gen_options.lua347
-rw-r--r--src/nvim/generators/gen_options_enum.lua129
-rw-r--r--src/nvim/getchar.c82
-rw-r--r--src/nvim/getchar_defs.h2
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/grid_defs.h8
-rw-r--r--src/nvim/highlight.c12
-rw-r--r--src/nvim/highlight.h2
-rw-r--r--src/nvim/highlight_defs.h3
-rw-r--r--src/nvim/highlight_group.c35
-rw-r--r--src/nvim/indent.c22
-rw-r--r--src/nvim/input.c2
-rw-r--r--src/nvim/insexpand.c46
-rw-r--r--src/nvim/linematch.c4
-rw-r--r--src/nvim/lua/executor.c103
-rw-r--r--src/nvim/lua/stdlib.c14
-rw-r--r--src/nvim/lua/treesitter.c49
-rw-r--r--src/nvim/main.c12
-rw-r--r--src/nvim/mapping.c30
-rw-r--r--src/nvim/mapping.h7
-rw-r--r--src/nvim/mark.c13
-rw-r--r--src/nvim/mbyte.c13
-rw-r--r--src/nvim/memline.c51
-rw-r--r--src/nvim/menu.c4
-rw-r--r--src/nvim/message.c282
-rw-r--r--src/nvim/message.h2
-rw-r--r--src/nvim/message_defs.h6
-rw-r--r--src/nvim/mouse.c2
-rw-r--r--src/nvim/normal.c68
-rw-r--r--src/nvim/normal_defs.h5
-rw-r--r--src/nvim/ops.c290
-rw-r--r--src/nvim/ops.h2
-rw-r--r--src/nvim/option.c2078
-rw-r--r--src/nvim/option.h64
-rw-r--r--src/nvim/option_defs.h77
-rw-r--r--src/nvim/option_vars.h74
-rw-r--r--src/nvim/options.lua531
-rw-r--r--src/nvim/optionstr.c101
-rw-r--r--src/nvim/os/input.c62
-rw-r--r--src/nvim/os/shell.c8
-rw-r--r--src/nvim/path.c9
-rw-r--r--src/nvim/popupmenu.c48
-rw-r--r--src/nvim/popupmenu.h2
-rw-r--r--src/nvim/profile.c5
-rw-r--r--src/nvim/quickfix.c179
-rw-r--r--src/nvim/runtime.c2
-rw-r--r--src/nvim/search.c34
-rw-r--r--src/nvim/shada.c13
-rw-r--r--src/nvim/sign.c6
-rw-r--r--src/nvim/spell.c28
-rw-r--r--src/nvim/statusline.c14
-rw-r--r--src/nvim/strings.c4
-rw-r--r--src/nvim/syntax.c90
-rw-r--r--src/nvim/tag.c26
-rw-r--r--src/nvim/terminal.c11
-rw-r--r--src/nvim/textformat.c2
-rw-r--r--src/nvim/tui/tui.c36
-rw-r--r--src/nvim/types_defs.h4
-rw-r--r--src/nvim/ui.c60
-rw-r--r--src/nvim/ui.h4
-rw-r--r--src/nvim/ui_compositor.c2
-rw-r--r--src/nvim/undo.c16
-rw-r--r--src/nvim/usercmd.c16
-rw-r--r--src/nvim/vim_defs.h1
-rw-r--r--src/nvim/vvars.lua261
-rw-r--r--src/nvim/window.c62
-rw-r--r--src/nvim/winfloat.c7
-rw-r--r--src/nvim/yankmap.c2
-rw-r--r--src/uncrustify.cfg13
-rw-r--r--src/vterm/vterm.c509
-rw-r--r--src/vterm/vterm.h37
112 files changed, 4375 insertions, 3292 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index c2a358327a..344b4bef00 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -31,7 +31,7 @@ target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY})
find_package(Iconv REQUIRED)
find_package(Libuv 1.28.0 REQUIRED)
find_package(Lpeg REQUIRED)
-find_package(Treesitter 0.23.0 REQUIRED)
+find_package(Treesitter 0.24.0 REQUIRED)
find_package(Unibilium 2.0 REQUIRED)
find_package(UTF8proc REQUIRED)
@@ -49,7 +49,7 @@ if(ENABLE_LIBINTL)
endif()
if(ENABLE_WASMTIME)
- find_package(Wasmtime 25.0.1 EXACT REQUIRED)
+ find_package(Wasmtime 25.0.2 EXACT REQUIRED)
target_link_libraries(main_lib INTERFACE wasmtime)
target_compile_definitions(nvim_bin PRIVATE HAVE_WASMTIME)
endif()
@@ -308,7 +308,6 @@ set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua)
set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload.lua)
set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua)
-set(OPTIONS_ENUM_GENERATOR ${GENERATOR_DIR}/gen_options_enum.lua)
set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua)
# GENERATED_DIR and GENERATED_INCLUDES_DIR
@@ -687,16 +686,11 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
-add_custom_command(OUTPUT ${GENERATED_OPTIONS}
- COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS}
+add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
+ COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
)
-add_custom_command(OUTPUT ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
- COMMAND ${LUA_GEN} ${OPTIONS_ENUM_GENERATOR} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
- DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_ENUM_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
-)
-
# NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive.
foreach(hfile ${NVIM_GENERATED_FOR_HEADERS})
list(FIND NVIM_GENERATED_FOR_SOURCES ${hfile} hfile_idx)
@@ -750,6 +744,7 @@ add_custom_target(nvim_runtime_deps)
if(WIN32)
# Copy DLLs and third-party tools to bin/ and install them along with nvim
add_custom_command(TARGET nvim_runtime_deps
+ POST_BUILD
COMMAND ${CMAKE_COMMAND} -E ${COPY_DIRECTORY} ${PROJECT_BINARY_DIR}/windows_runtime_deps/
${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
install(DIRECTORY ${PROJECT_BINARY_DIR}/windows_runtime_deps/
@@ -791,7 +786,10 @@ file(MAKE_DIRECTORY ${BINARY_LIB_DIR})
# install treesitter parser if bundled
if(EXISTS ${DEPS_PREFIX}/lib/nvim/parser)
- add_custom_command(TARGET nvim_runtime_deps COMMAND ${CMAKE_COMMAND} -E ${COPY_DIRECTORY} ${DEPS_PREFIX}/lib/nvim/parser ${BINARY_LIB_DIR}/parser)
+ add_custom_command(
+ TARGET nvim_runtime_deps
+ POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E ${COPY_DIRECTORY} ${DEPS_PREFIX}/lib/nvim/parser ${BINARY_LIB_DIR}/parser)
endif()
install(DIRECTORY ${BINARY_LIB_DIR}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 6376011106..b38a7d4173 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -533,7 +533,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
- set_option_to(channel_id, NULL, kOptReqGlobal, name, value, err);
+ set_option_to(channel_id, NULL, kOptScopeGlobal, name, value, err);
}
/// Gets the global value of an option.
@@ -546,7 +546,7 @@ Object nvim_get_option(String name, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
- return get_option_from(NULL, kOptReqGlobal, name, err);
+ return get_option_from(NULL, kOptScopeGlobal, name, err);
}
/// Gets a buffer option value
@@ -566,7 +566,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
return (Object)OBJECT_INIT;
}
- return get_option_from(buf, kOptReqBuf, name, err);
+ return get_option_from(buf, kOptScopeBuf, name, err);
}
/// Sets a buffer option value. Passing `nil` as value deletes the option (only
@@ -588,7 +588,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
return;
}
- set_option_to(channel_id, buf, kOptReqBuf, name, value, err);
+ set_option_to(channel_id, buf, kOptScopeBuf, name, value, err);
}
/// Gets a window option value
@@ -608,7 +608,7 @@ Object nvim_win_get_option(Window window, String name, Error *err)
return (Object)OBJECT_INIT;
}
- return get_option_from(win, kOptReqWin, name, err);
+ return get_option_from(win, kOptScopeWin, name, err);
}
/// Sets a window option value. Passing `nil` as value deletes the option (only
@@ -630,26 +630,32 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
return;
}
- set_option_to(channel_id, win, kOptReqWin, name, value, err);
+ set_option_to(channel_id, win, kOptScopeWin, name, value, err);
}
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param name The option name.
/// @param[out] err Details of an error that may have occurred.
///
/// @return the option value.
-static Object get_option_from(void *from, OptReqScope req_scope, String name, Error *err)
+static Object get_option_from(void *from, OptScope req_scope, String name, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return (Object)OBJECT_INIT;
});
- OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err);
- if (ERROR_SET(err)) {
- return (Object)OBJECT_INIT;
+ OptIndex opt_idx = find_option(name.data);
+ OptVal value = NIL_OPTVAL;
+
+ if (option_has_scope(opt_idx, req_scope)) {
+ value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
+ req_scope, from, err);
+ if (ERROR_SET(err)) {
+ return (Object)OBJECT_INIT;
+ }
}
VALIDATE_S(value.type != kOptValTypeNil, "option name", name.data, {
@@ -662,11 +668,11 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er
/// Sets the value of a global or local (buffer, window) option.
///
/// @param[in] to Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param name The option name.
/// @param value New option value.
/// @param[out] err Details of an error that may have occurred.
-static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, String name,
+static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name,
Object value, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
@@ -689,12 +695,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
return;
});
- int attrs = get_option_attrs(opt_idx);
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
- const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL))
- ? 0
- : (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL;
+ const int opt_flags
+ = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
+ ? 0
+ : ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
WITH_SCRIPT_CONTEXT(channel_id, {
set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 7786c30624..c94b8df9ea 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -381,8 +381,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - id : id of the extmark to edit.
/// - end_row : ending line of the mark, 0-based inclusive.
/// - end_col : ending col of the mark, 0-based exclusive.
-/// - hl_group : name of the highlight group used to highlight
-/// this mark.
+/// - hl_group : highlight group used for the text range. This and below
+/// highlight groups can be supplied either as a string or as an integer,
+/// the latter of which can be obtained using |nvim_get_hl_id_by_name()|.
/// - hl_eol : when true, for a multiline highlight covering the
/// EOL of a line, continue the highlight for the rest
/// of the screen line (just like for diff and
@@ -392,9 +393,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// text chunk with specified highlight. `highlight` element
/// can either be a single highlight group, or an array of
/// multiple highlight groups that will be stacked
-/// (highest priority last). A highlight group can be supplied
-/// either as a string or as an integer, the latter which
-/// can be obtained using |nvim_get_hl_id_by_name()|.
+/// (highest priority last).
/// - virt_text_pos : position of virtual text. Possible values:
/// - "eol": right after eol character (default).
/// - "overlay": display over the specified column, without
@@ -465,15 +464,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// buffer or end of the line respectively. Defaults to true.
/// - sign_text: string of length 1-2 used to display in the
/// sign column.
-/// - sign_hl_group: name of the highlight group used to
-/// highlight the sign column text.
-/// - number_hl_group: name of the highlight group used to
-/// highlight the number column.
-/// - line_hl_group: name of the highlight group used to
-/// highlight the whole line.
-/// - cursorline_hl_group: name of the highlight group used to
-/// highlight the sign column text when the cursor is on
-/// the same line as the mark and 'cursorline' is enabled.
+/// - sign_hl_group: highlight group used for the sign column text.
+/// - number_hl_group: highlight group used for the number column.
+/// - line_hl_group: highlight group used for the whole line.
+/// - cursorline_hl_group: highlight group used for the sign
+/// column text when the cursor is on the same line as the
+/// mark and 'cursorline' is enabled.
/// - conceal: string which should be either empty or a single
/// character. Enable concealing similar to |:syn-conceal|.
/// When a character is supplied it is used as |:syn-cchar|.
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 552612dd13..96aabb851f 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -119,6 +119,7 @@ typedef struct {
Array bufpos;
Boolean external;
Boolean focusable;
+ Boolean mouse;
Boolean vertical;
Integer zindex;
Object border;
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 1a0edd551e..3289daeb6f 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -23,8 +23,8 @@
#endif
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
- int *scope, OptReqScope *req_scope, void **from,
- char **filetype, Error *err)
+ int *scope, OptScope *req_scope, void **from, char **filetype,
+ Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
@@ -39,14 +39,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- *req_scope = kOptReqGlobal;
+ *req_scope = kOptScopeGlobal;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY_X(opts, win)) {
- *req_scope = kOptReqWin;
+ *req_scope = kOptScopeWin;
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -59,7 +59,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
return FAIL;
});
*scope = OPT_LOCAL;
- *req_scope = kOptReqBuf;
+ *req_scope = kOptScopeBuf;
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -78,18 +78,17 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
});
*opt_idxp = find_option(name);
- int flags = get_option_attrs(*opt_idxp);
- if (flags == 0) {
- // hidden or unknown option
+ if (*opt_idxp == kOptInvalid) {
+ // unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
- } else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) {
+ } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
- int req_flags = *req_scope == kOptReqBuf ? SOPT_BUF : SOPT_WIN;
- if (!(flags & req_flags)) {
- char *tgt = *req_scope & kOptReqBuf ? "buf" : "win";
- char *global = flags & SOPT_GLOBAL ? "global " : "";
- char *req = flags & SOPT_BUF ? "buffer-local "
- : flags & SOPT_WIN ? "window-local " : "";
+ if (!option_has_scope(*opt_idxp, *req_scope)) {
+ char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win";
+ char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
+ char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
+ ? "buffer-local "
+ : (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : "");
api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'",
tgt, global, req, name);
@@ -153,7 +152,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
{
OptIndex opt_idx = 0;
int scope = 0;
- OptReqScope req_scope = kOptReqGlobal;
+ OptScope req_scope = kOptScopeGlobal;
void *from = NULL;
char *filetype = NULL;
@@ -166,6 +165,14 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
buf_T *ftbuf = do_ft_buf(filetype, &aco, err);
if (ERROR_SET(err)) {
+ if (ftbuf != NULL) {
+ // restore curwin/curbuf and a few other things
+ aucmd_restbuf(&aco);
+
+ assert(curbuf != ftbuf); // safety check
+ wipe_buffer(ftbuf, false);
+ }
+
return (Object)OBJECT_INIT;
}
@@ -175,7 +182,6 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
}
OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
- bool hidden = is_option_hidden(opt_idx);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -189,7 +195,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
goto err;
}
- VALIDATE_S(!hidden && value.type != kOptValTypeNil, "option", name.data, {
+ VALIDATE_S(value.type != kOptValTypeNil, "option", name.data, {
goto err;
});
@@ -219,7 +225,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
{
OptIndex opt_idx = 0;
int scope = 0;
- OptReqScope req_scope = kOptReqGlobal;
+ OptScope req_scope = kOptScopeGlobal;
void *to = NULL;
if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
return;
@@ -231,9 +237,8 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
// - option is global or local to window (global-local)
//
// Then force scope to local since we don't want to change the global option
- if (req_scope == kOptReqWin && scope == 0) {
- int flags = get_option_attrs(opt_idx);
- if (flags & SOPT_GLOBAL) {
+ if (req_scope == kOptScopeWin && scope == 0) {
+ if (option_has_scope(opt_idx, kOptScopeGlobal)) {
scope = OPT_LOCAL;
}
}
@@ -306,15 +311,15 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error
{
OptIndex opt_idx = 0;
int scope = 0;
- OptReqScope req_scope = kOptReqGlobal;
+ OptScope req_scope = kOptScopeGlobal;
void *from = NULL;
if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
err)) {
return (Dict)ARRAY_DICT_INIT;
}
- buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf;
- win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin;
+ buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
+ win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin;
return get_vimoption(name, scope, buf, win, arena, err);
}
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index e1fb4ed732..8ddaecc58e 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -771,7 +771,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err)
int id = (int)obj.data.integer;
return (1 <= id && id <= highlight_num_groups()) ? id : 0;
} else {
- api_set_error(err, kErrorTypeValidation, "Invalid highlight: %s", what);
+ api_set_error(err, kErrorTypeValidation, "Invalid hl_group: %s", what);
return 0;
}
}
@@ -809,31 +809,22 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
- if (chunks.items[i].type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
+ VALIDATE_T("chunk", kObjectTypeArray, chunks.items[i].type, {
goto free_exit;
- }
+ });
Array chunk = chunks.items[i].data.array;
- if (chunk.size == 0 || chunk.size > 2
- || chunk.items[0].type != kObjectTypeString
- || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) {
- api_set_error(err, kErrorTypeValidation,
- "Chunk is not an array with one or two strings");
+ VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString),
+ "%s", "Invalid chunk: expected Array with 1 or 2 Strings", {
goto free_exit;
- }
+ });
String str = copy_string(chunk.items[0].data.string, NULL);
- int attr = 0;
+ int hl_id = 0;
if (chunk.size == 2) {
- String hl = chunk.items[1].data.string;
- if (hl.size > 0) {
- // TODO(bfredl): use object_to_hl_id and allow integer
- int hl_id = syn_check_group(hl.data, hl.size);
- attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
- }
+ hl_id = object_to_hl_id(chunk.items[1], "text highlight", err);
}
- kv_push(hl_msg, ((HlMessageChunk){ .text = str, .attr = attr }));
+ kv_push(hl_msg, ((HlMessageChunk){ .text = str, .hl_id = hl_id }));
}
return hl_msg;
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 57932e067e..d06f5c9c65 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -111,6 +111,12 @@ typedef kvec_withinit_t(Object, 16) ArrayBuilder;
#define STATIC_CSTR_AS_OBJ(s) STRING_OBJ(STATIC_CSTR_AS_STRING(s))
#define STATIC_CSTR_TO_OBJ(s) STRING_OBJ(STATIC_CSTR_TO_STRING(s))
+#define API_CLEAR_STRING(s) \
+ do { \
+ XFREE_CLEAR(s.data); \
+ s.size = 0; \
+ } while (0)
+
// Helpers used by the generated msgpack-rpc api wrappers
#define api_init_boolean
#define api_init_integer
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 2bd8792d71..0ed208fc1a 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -102,7 +102,7 @@ void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integ
Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row,
- Float anchor_col, Boolean focusable, Integer zindex)
+ Float anchor_col, Boolean mouse_enabled, Integer zindex)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_external_pos(Integer grid, Window win)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
@@ -159,7 +159,7 @@ void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void msg_show(String kind, Array content, Boolean replace_last)
- FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_showcmd(Array content)
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 8c88a19147..83f9aa573d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -594,10 +594,12 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Er
kvi_init(cookie.rv);
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
+ TryState tstate;
+
+ try_enter(&tstate);
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
+ vim_ignored = try_leave(&tstate, err);
- TRY_WRAP(err, {
- do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
- });
return arena_take_arraybuilder(arena, &cookie.rv);
}
@@ -777,12 +779,12 @@ void nvim_set_vvar(String name, Object value, Error *err)
/// Echo a message.
///
/// @param chunks A list of `[text, hl_group]` arrays, each representing a
-/// text chunk with specified highlight. `hl_group` element
-/// can be omitted for no highlight.
+/// text chunk with specified highlight group name or ID.
+/// `hl_group` element can be omitted for no highlight.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters.
-/// - verbose: Message was printed as a result of 'verbose' option
-/// if Nvim was invoked with -V3log_file, the message will be
+/// - verbose: Message is printed as a result of 'verbose' option.
+/// If Nvim was invoked with -V3log_file, the message will be
/// redirected to the log_file and suppressed from direct output.
void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
FUNC_API_SINCE(7)
@@ -796,7 +798,7 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
verbose_enter();
}
- msg_multiattr(hl_msg, history ? "echomsg" : "echo", history);
+ msg_multihl(hl_msg, history ? "echomsg" : "echo", history);
if (opts->verbose) {
verbose_leave();
@@ -1002,10 +1004,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
if (scratch) {
- set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf,
- buf);
- set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf,
- buf);
+ set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
buf->b_p_swf = false;
buf->b_p_ml = false;
@@ -1317,15 +1319,15 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow,
return; // Nothing to do.
}
- reg->y_array = arena_alloc(arena, lines.size * sizeof(uint8_t *), true);
+ reg->y_array = arena_alloc(arena, lines.size * sizeof(String), true);
reg->y_size = lines.size;
for (size_t i = 0; i < lines.size; i++) {
VALIDATE_T("line", kObjectTypeString, lines.items[i].type, {
return;
});
String line = lines.items[i].data.string;
- reg->y_array[i] = arena_memdupz(arena, line.data, line.size);
- memchrsub(reg->y_array[i], NUL, NL, line.size);
+ reg->y_array[i] = copy_string(line, arena);
+ memchrsub(reg->y_array[i].data, NUL, NL, line.size);
}
finish_yankreg_from_object(reg, false);
@@ -2161,11 +2163,11 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
if (num_id) {
stc_hl_id = num_id;
} else if (statuscol.use_cul) {
- stc_hl_id = HLF_CLN + 1;
+ stc_hl_id = HLF_CLN;
} else if (wp->w_p_rnu) {
- stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1;
+ stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB);
} else {
- stc_hl_id = HLF_N + 1;
+ stc_hl_id = HLF_N;
}
set_vim_var_nr(VV_LNUM, lnum);
@@ -2396,14 +2398,22 @@ void nvim__redraw(Dict(redraw) *opts, Error *err)
redraw_buf_range_later(rbuf, first, last);
}
- bool flush = opts->flush;
+ // Redraw later types require update_screen() so call implicitly unless set to false.
+ if (HAS_KEY(opts, redraw, valid) || HAS_KEY(opts, redraw, range)) {
+ opts->flush = HAS_KEY(opts, redraw, flush) ? opts->flush : true;
+ }
+
+ // When explicitly set to false and only "redraw later" types are present,
+ // don't call ui_flush() either.
+ bool flush_ui = opts->flush;
if (opts->tabline) {
// Flush later in case tabline was just hidden or shown for the first time.
if (redraw_tabline && firstwin->w_lines_valid == 0) {
- flush = true;
+ opts->flush = true;
} else {
draw_tabline();
}
+ flush_ui = true;
}
bool save_lz = p_lz;
@@ -2414,31 +2424,35 @@ void nvim__redraw(Dict(redraw) *opts, Error *err)
if (win == NULL) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (buf == NULL || wp->w_buffer == buf) {
- redraw_status(wp, opts, &flush);
+ redraw_status(wp, opts, &opts->flush);
}
}
} else {
- redraw_status(win, opts, &flush);
+ redraw_status(win, opts, &opts->flush);
}
+ flush_ui = true;
}
win_T *cwin = win ? win : curwin;
// Allow moving cursor to recently opened window and make sure it is drawn #28868.
if (opts->cursor && (!cwin->w_grid.target || !cwin->w_grid.target->valid)) {
- flush = true;
+ opts->flush = true;
}
// Redraw pending screen updates when explicitly requested or when determined
// that it is necessary to properly draw other requested components.
- if (flush && !cmdpreview) {
+ if (opts->flush && !cmdpreview) {
update_screen();
}
if (opts->cursor) {
setcursor_mayforce(cwin, true);
+ flush_ui = true;
}
- ui_flush();
+ if (flush_ui) {
+ ui_flush();
+ }
RedrawingDisabled = save_rd;
p_lz = save_lz;
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index f63fdc5381..6f5a9a90c0 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -129,7 +129,12 @@
/// fractional.
/// - focusable: Enable focus by user actions (wincmds, mouse events).
/// Defaults to true. Non-focusable windows can be entered by
-/// |nvim_set_current_win()|.
+/// |nvim_set_current_win()|, or, when the `mouse` field is set to true,
+/// by mouse events. See |focusable|.
+/// - mouse: Specify how this window interacts with mouse events.
+/// Defaults to `focusable` value.
+/// - If false, mouse events pass through this window.
+/// - If true, mouse events interact with this window normally.
/// - external: GUI should display the window as an external
/// top-level window. Currently accepts no other positioning
/// configuration together with this.
@@ -714,6 +719,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
PUT_KEY_X(rv, focusable, config->focusable);
PUT_KEY_X(rv, external, config->external);
PUT_KEY_X(rv, hide, config->hide);
+ PUT_KEY_X(rv, mouse, config->mouse);
if (wp->w_floating) {
PUT_KEY_X(rv, width, config->width);
@@ -1202,6 +1208,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
if (HAS_KEY_X(config, focusable)) {
fconfig->focusable = config->focusable;
+ fconfig->mouse = config->focusable;
+ }
+
+ if (HAS_KEY_X(config, mouse)) {
+ fconfig->mouse = config->mouse;
}
if (HAS_KEY_X(config, zindex)) {
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index d509837de7..3243822c94 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -73,10 +73,10 @@ return {
'InsertLeavePre', -- just before leaving Insert mode
'LspAttach', -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer
- 'LspRequest', -- after an LSP request is started, canceled, or completed
'LspNotify', -- after an LSP notice has been sent to the server
- 'LspTokenUpdate', -- after a visible LSP token is updated
'LspProgress', -- after a LSP progress update
+ 'LspRequest', -- after an LSP request is started, canceled, or completed
+ 'LspTokenUpdate', -- after a visible LSP token is updated
'MenuPopup', -- just before popup menu is displayed
'ModeChanged', -- after changing the mode
'OptionSet', -- after setting any option
@@ -162,8 +162,8 @@ return {
LspAttach = true,
LspDetach = true,
LspNotify = true,
- LspRequest = true,
LspProgress = true,
+ LspRequest = true,
LspTokenUpdate = true,
RecordingEnter = true,
RecordingLeave = true,
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 5a4ade913d..c08ef7a4c1 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -220,14 +220,14 @@ static void au_show_for_event(int group, event_T event, const char *pat)
// show the group name, if it's not the default group
if (ac->pat->group != AUGROUP_DEFAULT) {
if (last_group_name == NULL) {
- msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
+ msg_puts_hl(get_deleted_augroup(), HLF_E, false);
} else {
- msg_puts_attr(last_group_name, HL_ATTR(HLF_T));
+ msg_puts_hl(last_group_name, HLF_T, false);
}
msg_puts(" ");
}
// show the event name
- msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
+ msg_puts_hl(event_nr2name(event), HLF_T, false);
}
// Show pattern only if it changed.
@@ -240,7 +240,7 @@ static void au_show_for_event(int group, event_T event, const char *pat)
}
msg_col = 4;
- msg_outtrans(ac->pat->pat, 0);
+ msg_outtrans(ac->pat->pat, 0, false);
}
if (got_int) {
@@ -260,17 +260,17 @@ static void au_show_for_event(int group, event_T event, const char *pat)
size_t msglen = 100;
char *msg = xmallocz(msglen);
if (ac->exec.type == CALLABLE_CB) {
- msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
+ msg_puts_hl(exec_to_string, HLF_8, false);
snprintf(msg, msglen, " [%s]", ac->desc);
} else {
snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
}
- msg_outtrans(msg, 0);
+ msg_outtrans(msg, 0, false);
XFREE_CLEAR(msg);
} else if (ac->exec.type == CALLABLE_CB) {
- msg_puts_attr(exec_to_string, HL_ATTR(HLF_8));
+ msg_puts_hl(exec_to_string, HLF_8, false);
} else {
- msg_outtrans(exec_to_string, 0);
+ msg_outtrans(exec_to_string, 0, false);
}
XFREE_CLEAR(exec_to_string);
if (p_verbose > 0) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4a87cebfa7..abcce0dfe8 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -255,7 +255,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg)
emsg(_("E83: Cannot allocate buffer, using other one..."));
enter_buffer(curbuf);
if (old_tw != curbuf->b_p_tw) {
- check_colorcolumn(curwin);
+ check_colorcolumn(NULL, curwin);
}
return FAIL;
}
@@ -1029,7 +1029,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
enter_buffer(buf);
if (old_tw != curbuf->b_p_tw) {
- check_colorcolumn(curwin);
+ check_colorcolumn(NULL, curwin);
}
}
// If "old_curbuf" is NULL we are in big trouble here...
@@ -1669,7 +1669,7 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist)
// enter some buffer. Using the last one is hopefully OK.
enter_buffer(valid ? buf : lastbuf);
if (old_tw != curbuf->b_p_tw) {
- check_colorcolumn(curwin);
+ check_colorcolumn(NULL, curwin);
}
}
@@ -2097,6 +2097,8 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_tfu);
callback_free(&buf->b_tfu_cb);
+ clear_string_option(&buf->b_p_ffu);
+ callback_free(&buf->b_ffu_cb);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
@@ -2900,7 +2902,7 @@ void buflist_list(exarg_T *eap)
buf == curbuf ? (int64_t)curwin->w_cursor.lnum : (int64_t)buflist_findlnum(buf));
}
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
line_breakcheck();
}
@@ -2985,7 +2987,7 @@ int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
close_buffer(NULL, obuf, DOBUF_WIPE, false, false);
}
sfname = xstrdup(sfname);
-#ifdef USE_FNAME_CASE
+#ifdef CASE_INSENSITIVE_FILENAME
path_fix_case(sfname); // set correct case for short file name
#endif
if (buf->b_sfname != buf->b_ffname) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index a68e841f3e..8cd1ffc979 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -206,7 +206,7 @@ typedef struct {
OptInt wo_winbl;
#define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend'
- LastSet wo_script_ctx[WV_COUNT]; // SCTXs for window-local options
+ LastSet wo_script_ctx[kWinOptCount]; // SCTXs for window-local options
#define w_p_script_ctx w_onebuf_opt.wo_script_ctx
} winopt_T;
@@ -512,7 +512,7 @@ struct file_buffer {
// or contents of the file being edited.
bool b_p_initialized; // set when options initialized
- LastSet b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options
+ LastSet b_p_script_ctx[kBufOptCount]; // SCTXs for buffer-local options
int b_p_ai; ///< 'autoindent'
int b_p_ai_nopaste; ///< b_p_ai saved for paste mode
@@ -543,9 +543,11 @@ struct file_buffer {
Callback b_cfu_cb; ///< 'completefunc' callback
char *b_p_ofu; ///< 'omnifunc'
Callback b_ofu_cb; ///< 'omnifunc' callback
- char *b_p_tfu; ///< 'tagfunc'
+ char *b_p_tfu; ///< 'tagfunc' option value
Callback b_tfu_cb; ///< 'tagfunc' callback
char *b_p_urf; ///< 'userregfunc'
+ char *b_p_ffu; ///< 'findfunc' option value
+ Callback b_ffu_cb; ///< 'findfunc' callback
int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
@@ -939,6 +941,7 @@ typedef struct {
FloatRelative relative;
bool external;
bool focusable;
+ bool mouse;
WinSplit split;
int zindex;
WinStyle style;
@@ -965,6 +968,7 @@ typedef struct {
.row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \
.focusable = true, \
+ .mouse = true, \
.split = 0, \
.zindex = kZIndexFloatDefault, \
.style = kWinStyleUnused, \
@@ -1248,7 +1252,7 @@ struct window_S {
// transform a pointer to a "onebuf" option into a "allbuf" option
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
- // A few options have local flags for P_INSECURE.
+ // A few options have local flags for kOptFlagInsecure.
uint32_t w_p_stl_flags; // flags for 'statusline'
uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index fa0a55e7b6..5f830b4219 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -350,7 +350,7 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
msg_scroll = true; // Don't overwrite messages here.
msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers.
- msg(_("WARNING: The file has been changed since reading it!!!"), HL_ATTR(HLF_E));
+ msg(_("WARNING: The file has been changed since reading it!!!"), HLF_E);
if (ask_yesno(_("Do you really want to write to it"), true) == 'n') {
return FAIL;
}
@@ -1150,9 +1150,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (!filtering) {
// show that we are busy
#ifndef UNIX
- filemess(buf, sfname, "", 0);
+ filemess(buf, sfname, "");
#else
- filemess(buf, fname, "", 0);
+ filemess(buf, fname, "");
#endif
}
msg_scroll = false; // always overwrite the file message now
@@ -1881,11 +1881,9 @@ nofail:
retval = FAIL;
if (end == 0) {
- const int attr = HL_ATTR(HLF_E); // Set highlight for error messages.
- msg_puts_attr(_("\nWARNING: Original file may be lost or damaged\n"),
- attr | MSG_HIST);
- msg_puts_attr(_("don't quit the editor until the file is successfully written!"),
- attr | MSG_HIST);
+ const int hl_id = HLF_E; // Set highlight for error messages.
+ msg_puts_hl(_("\nWARNING: Original file may be lost or damaged\n"), hl_id, true);
+ msg_puts_hl(_("don't quit the editor until the file is successfully written!"), hl_id, true);
// Update the timestamp to avoid an "overwrite changed file"
// prompt when writing again.
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 51a13b80e7..f3a8e0b208 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -89,9 +89,9 @@ void change_warning(buf_T *buf, int col)
if (msg_row == Rows - 1) {
msg_col = col;
}
- msg_source(HL_ATTR(HLF_W));
+ msg_source(HLF_W);
msg_ext_set_kind("wmsg");
- msg_puts_attr(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST);
+ msg_puts_hl(_(w_readonly), HLF_W, true);
set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
msg_end();
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 430f6b15fe..1afd590b0e 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -88,13 +88,11 @@ int init_chartab(void)
/// an error, OK otherwise.
int buf_init_chartab(buf_T *buf, bool global)
{
- int c;
-
if (global) {
// Set the default size for printable characters:
// From <Space> to '~' is 1 (printable), others are 2 (not printable).
// This also inits all 'isident' and 'isfname' flags to false.
- c = 0;
+ int c = 0;
while (c < ' ') {
g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
@@ -124,9 +122,7 @@ int buf_init_chartab(buf_T *buf, bool global)
SET_CHARTAB(buf, '-');
}
- // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint'
- // options Each option is a list of characters, character numbers or
- // ranges, separated by commas, e.g.: "200-210,x,#-178,-"
+ // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' options.
for (int i = global ? 0 : 3; i <= 3; i++) {
const char *p;
if (i == 0) {
@@ -142,110 +138,130 @@ int buf_init_chartab(buf_T *buf, bool global)
// fourth round: 'iskeyword'
p = buf->b_p_isk;
}
+ if (parse_isopt(p, buf, false) == FAIL) {
+ return FAIL;
+ }
+ }
- while (*p) {
- bool tilde = false;
- bool do_isalpha = false;
+ chartab_initialized = true;
+ return OK;
+}
- if ((*p == '^') && (p[1] != NUL)) {
- tilde = true;
- p++;
- }
+/// Checks the format for the option settings 'iskeyword', 'isident', 'isfname'
+/// or 'isprint'.
+/// Returns FAIL if has an error, OK otherwise.
+int check_isopt(char *var)
+{
+ return parse_isopt(var, NULL, true);
+}
+
+/// @param only_check if false: refill g_chartab[]
+static int parse_isopt(const char *var, buf_T *buf, bool only_check)
+{
+ const char *p = var;
+
+ // Parses the 'isident', 'iskeyword', 'isfname' and 'isprint' options.
+ // Each option is a list of characters, character numbers or ranges,
+ // separated by commas, e.g.: "200-210,x,#-178,-"
+ while (*p) {
+ bool tilde = false;
+ bool do_isalpha = false;
+
+ if (*p == '^' && p[1] != NUL) {
+ tilde = true;
+ p++;
+ }
+
+ int c;
+ if (ascii_isdigit(*p)) {
+ c = getdigits_int((char **)&p, true, 0);
+ } else {
+ c = mb_ptr2char_adv(&p);
+ }
+ int c2 = -1;
+
+ if (*p == '-' && p[1] != NUL) {
+ p++;
if (ascii_isdigit(*p)) {
- c = getdigits_int((char **)&p, true, 0);
+ c2 = getdigits_int((char **)&p, true, 0);
} else {
- c = mb_ptr2char_adv(&p);
+ c2 = mb_ptr2char_adv(&p);
}
- int c2 = -1;
+ }
- if ((*p == '-') && (p[1] != NUL)) {
- p++;
+ if (c <= 0 || c >= 256 || (c2 < c && c2 != -1) || c2 >= 256
+ || !(*p == NUL || *p == ',')) {
+ return FAIL;
+ }
- if (ascii_isdigit(*p)) {
- c2 = getdigits_int((char **)&p, true, 0);
- } else {
- c2 = mb_ptr2char_adv(&p);
- }
- }
+ bool trail_comma = *p == ',';
+ p = skip_to_option_part(p);
+ if (trail_comma && *p == NUL) {
+ // Trailing comma is not allowed.
+ return FAIL;
+ }
- if ((c <= 0)
- || (c >= 256)
- || ((c2 < c) && (c2 != -1))
- || (c2 >= 256)
- || !((*p == NUL) || (*p == ','))) {
- return FAIL;
- }
+ if (only_check) {
+ continue;
+ }
- if (c2 == -1) { // not a range
- // A single '@' (not "@-@"):
- // Decide on letters being ID/printable/keyword chars with
- // standard function isalpha(). This takes care of locale for
- // single-byte characters).
- if (c == '@') {
- do_isalpha = true;
- c = 1;
- c2 = 255;
- } else {
- c2 = c;
- }
+ if (c2 == -1) { // not a range
+ // A single '@' (not "@-@"):
+ // Decide on letters being ID/printable/keyword chars with
+ // standard function isalpha(). This takes care of locale for
+ // single-byte characters).
+ if (c == '@') {
+ do_isalpha = true;
+ c = 1;
+ c2 = 255;
+ } else {
+ c2 = c;
}
+ }
- while (c <= c2) {
- // Use the MB_ functions here, because isalpha() doesn't
- // work properly when 'encoding' is "latin1" and the locale is
- // "C".
- if (!do_isalpha
- || mb_islower(c)
- || mb_isupper(c)) {
- if (i == 0) {
- // (re)set ID flag
- if (tilde) {
- g_chartab[c] &= (uint8_t) ~CT_ID_CHAR;
- } else {
- g_chartab[c] |= CT_ID_CHAR;
- }
- } else if (i == 1) {
- // (re)set printable
- if (c < ' ' || c > '~') {
- if (tilde) {
- g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
- + ((dy_flags & DY_UHEX) ? 4 : 2));
- g_chartab[c] &= (uint8_t) ~CT_PRINT_CHAR;
- } else {
- g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1);
- g_chartab[c] |= CT_PRINT_CHAR;
- }
- }
- } else if (i == 2) {
- // (re)set fname flag
- if (tilde) {
- g_chartab[c] &= (uint8_t) ~CT_FNAME_CHAR;
- } else {
- g_chartab[c] |= CT_FNAME_CHAR;
- }
- } else { // i == 3
- // (re)set keyword flag
+ while (c <= c2) {
+ // Use the MB_ functions here, because isalpha() doesn't
+ // work properly when 'encoding' is "latin1" and the locale is
+ // "C".
+ if (!do_isalpha
+ || mb_islower(c)
+ || mb_isupper(c)) {
+ if (var == p_isi) { // (re)set ID flag
+ if (tilde) {
+ g_chartab[c] &= (uint8_t) ~CT_ID_CHAR;
+ } else {
+ g_chartab[c] |= CT_ID_CHAR;
+ }
+ } else if (var == p_isp) { // (re)set printable
+ if (c < ' ' || c > '~') {
if (tilde) {
- RESET_CHARTAB(buf, c);
+ g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
+ + ((dy_flags & DY_UHEX) ? 4 : 2));
+ g_chartab[c] &= (uint8_t) ~CT_PRINT_CHAR;
} else {
- SET_CHARTAB(buf, c);
+ g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1);
+ g_chartab[c] |= CT_PRINT_CHAR;
}
}
+ } else if (var == p_isf) { // (re)set fname flag
+ if (tilde) {
+ g_chartab[c] &= (uint8_t) ~CT_FNAME_CHAR;
+ } else {
+ g_chartab[c] |= CT_FNAME_CHAR;
+ }
+ } else { // (var == p_isk || var == buf->b_p_isk) (re)set keyword flag
+ if (tilde) {
+ RESET_CHARTAB(buf, c);
+ } else {
+ SET_CHARTAB(buf, c);
+ }
}
- c++;
- }
-
- c = (uint8_t)(*p);
- p = skip_to_option_part(p);
-
- if ((c == ',') && (*p == NUL)) {
- // Trailing comma is not allowed.
- return FAIL;
}
+ c++;
}
}
- chartab_initialized = true;
+
return OK;
}
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 402a891099..700d554821 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -109,6 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_FILES
&& xp->xp_context != EXPAND_FILES_IN_PATH
&& xp->xp_context != EXPAND_FILETYPE
+ && xp->xp_context != EXPAND_FINDFUNC
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_KEYMAP
&& xp->xp_context != EXPAND_LUA
@@ -119,6 +120,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_PACKADD
&& xp->xp_context != EXPAND_RUNTIME
&& xp->xp_context != EXPAND_SHELLCMD
+ && xp->xp_context != EXPAND_SHELLCMDLINE
&& xp->xp_context != EXPAND_TAGS
&& xp->xp_context != EXPAND_TAGS_LISTFILES
&& xp->xp_context != EXPAND_USER_LIST
@@ -356,7 +358,8 @@ static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches,
.pum_info = NULL,
.pum_extra = NULL,
.pum_kind = NULL,
- .pum_user_hlattr = -1,
+ .pum_user_abbr_hlattr = -1,
+ .pum_user_kind_hlattr = -1,
};
}
@@ -976,20 +979,19 @@ void ExpandCleanup(expand_T *xp)
/// @param linenr line number of matches to display
/// @param maxlen maximum number of characters in each line
/// @param showtail display only the tail of the full path of a file name
-/// @param dir_attr highlight attribute to use for directory names
static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, int lines, int linenr,
- int maxlen, bool showtail, int dir_attr)
+ int maxlen, bool showtail)
{
char *p;
int lastlen = 999;
for (int j = linenr; j < numMatches; j += lines) {
if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- msg_outtrans(matches[j], HL_ATTR(HLF_D));
+ msg_outtrans(matches[j], HLF_D, false);
p = matches[j] + strlen(matches[j]) + 1;
msg_advance(maxlen + 1);
msg_puts(p);
msg_advance(maxlen + 3);
- msg_outtrans_long(p + 2, HL_ATTR(HLF_D));
+ msg_outtrans_long(p + 2, HLF_D);
break;
}
for (int i = maxlen - lastlen; --i >= 0;) {
@@ -1026,7 +1028,7 @@ static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, in
isdir = false;
p = SHOW_MATCH(j);
}
- lastlen = msg_outtrans(p, isdir ? dir_attr : 0);
+ lastlen = msg_outtrans(p, isdir ? HLF_D : 0, false);
}
if (msg_col > 0) { // when not wrapped around
msg_clr_eos();
@@ -1116,18 +1118,16 @@ int showmatches(expand_T *xp, bool wildmenu)
lines = (numMatches + columns - 1) / columns;
}
- int attr = HL_ATTR(HLF_D); // find out highlighting for directories
-
if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
+ msg_puts_hl(_("tagname"), HLF_T, false);
msg_clr_eos();
msg_advance(maxlen - 3);
- msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
+ msg_puts_hl(_(" kind file\n"), HLF_T, false);
}
// list the files line by line
for (int i = 0; i < lines; i++) {
- showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail, attr);
+ showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail);
if (got_int) {
got_int = false;
break;
@@ -1226,7 +1226,8 @@ char *addstar(char *fname, size_t len, int context)
// For help tags the translation is done in find_help_tags().
// For a tag pattern starting with "/" no translation is needed.
- if (context == EXPAND_HELP
+ if (context == EXPAND_FINDFUNC
+ || context == EXPAND_HELP
|| context == EXPAND_COLORS
|| context == EXPAND_COMPILER
|| context == EXPAND_OWNSYNTAX
@@ -1348,7 +1349,7 @@ char *addstar(char *fname, size_t len, int context)
/// it.
/// EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
/// EXPAND_FILES After command with EX_XFILE set, or after setting
-/// with P_EXPAND set. eg :e ^I, :w>>^I
+/// with kOptFlagExpand set. eg :e ^I, :w>>^I
/// EXPAND_DIRECTORIES In some cases this is used instead of the latter
/// when we know only directories are of interest.
/// E.g. :set dir=^I and :cd ^I
@@ -1527,7 +1528,9 @@ static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool use
xp->xp_context = EXPAND_FILES;
// For a shell command more chars need to be escaped.
- if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) {
+ if (usefilter
+ || (eap != NULL && (eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal))
+ || *complp == EXPAND_SHELLCMDLINE) {
#ifndef BACKSLASH_IN_FILENAME
xp->xp_shell = true;
#endif
@@ -1823,7 +1826,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) {
- xp->xp_context = EXPAND_FILES_IN_PATH;
+ xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC : EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
@@ -2493,21 +2496,25 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
}
- if (xp->xp_context == EXPAND_FILES) {
- flags |= EW_FILE;
- } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
- flags |= (EW_FILE | EW_PATH);
- } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
- flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE;
+ int ret = FAIL;
+ if (xp->xp_context == EXPAND_FINDFUNC) {
+ ret = expand_findfunc(pat, matches, numMatches);
} else {
- flags = (flags | EW_DIR) & ~EW_FILE;
- }
- if (options & WILD_ICASE) {
- flags |= EW_ICASE;
+ if (xp->xp_context == EXPAND_FILES) {
+ flags |= EW_FILE;
+ } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
+ flags |= (EW_FILE | EW_PATH);
+ } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
+ flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE;
+ } else {
+ flags = (flags | EW_DIR) & ~EW_FILE;
+ }
+ if (options & WILD_ICASE) {
+ flags |= EW_ICASE;
+ }
+ // Expand wildcards, supporting %:h and the like.
+ ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
}
-
- // Expand wildcards, supporting %:h and the like.
- int ret = expand_wildcards_eval(&pat, numMatches, matches, flags);
if (free_pat) {
xfree(pat);
}
@@ -2712,6 +2719,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH
+ || xp->xp_context == EXPAND_FINDFUNC
|| xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
}
@@ -3601,6 +3609,11 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
set_context_in_runtime_cmd(&xpc, xpc.xp_pattern);
xpc.xp_pattern_len = strlen(xpc.xp_pattern);
}
+ if (xpc.xp_context == EXPAND_SHELLCMDLINE) {
+ int context = EXPAND_SHELLCMDLINE;
+ set_context_for_wildcard_arg(NULL, xpc.xp_pattern, false, &xpc, &context);
+ xpc.xp_pattern_len = strlen(xpc.xp_pattern);
+ }
theend:
if (xpc.xp_context == EXPAND_LUA) {
diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h
index 3369790151..dfda9fdaed 100644
--- a/src/nvim/cmdexpand_defs.h
+++ b/src/nvim/cmdexpand_defs.h
@@ -106,6 +106,8 @@ enum {
EXPAND_ARGOPT,
EXPAND_KEYMAP,
EXPAND_DIRS_IN_CDPATH,
+ EXPAND_SHELLCMDLINE,
+ EXPAND_FINDFUNC,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 47a4ffba9e..5993eefd67 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -221,7 +221,7 @@ static int in_history(int type, const char *str, int move_to_front, int sep)
// well.
char *p = history[type][i].hisstr;
if (strcmp(str, p) == 0
- && (type != HIST_SEARCH || sep == p[strlen(p) + 1])) {
+ && (type != HIST_SEARCH || sep == p[history[type][i].hisstrlen + 1])) {
if (!move_to_front) {
return true;
}
@@ -239,6 +239,7 @@ static int in_history(int type, const char *str, int move_to_front, int sep)
AdditionalData *ad = history[type][i].additional_data;
char *const save_hisstr = history[type][i].hisstr;
+ const size_t save_hisstrlen = history[type][i].hisstrlen;
while (i != hisidx[type]) {
if (++i >= hislen) {
i = 0;
@@ -249,6 +250,7 @@ static int in_history(int type, const char *str, int move_to_front, int sep)
xfree(ad);
history[type][i].hisnum = ++hisnum[type];
history[type][i].hisstr = save_hisstr;
+ history[type][i].hisstrlen = save_hisstrlen;
history[type][i].timestamp = os_time();
history[type][i].additional_data = NULL;
return true;
@@ -339,6 +341,7 @@ void add_to_history(int histype, const char *new_entry, size_t new_entrylen, boo
hisptr->timestamp = os_time();
hisptr->additional_data = NULL;
hisptr->hisstr[new_entrylen + 1] = (char)sep;
+ hisptr->hisstrlen = new_entrylen;
hisptr->hisnum = ++hisnum[histype];
if (histype == HIST_SEARCH && in_map) {
@@ -400,19 +403,6 @@ static int calc_hist_idx(int histype, int num)
return -1;
}
-/// Get a history entry by its index.
-///
-/// @param histype may be one of the HIST_ values.
-static char *get_history_entry(int histype, int idx)
-{
- idx = calc_hist_idx(histype, idx);
- if (idx >= 0) {
- return history[histype][idx].hisstr;
- } else {
- return "";
- }
-}
-
/// Clear all entries in a history
///
/// @param[in] histype One of the HIST_ values.
@@ -575,10 +565,15 @@ void f_histget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (argvars[1].v_type == VAR_UNKNOWN) {
idx = get_history_idx(type);
} else {
- idx = (int)tv_get_number_chk(&argvars[1], NULL);
+ idx = (int)tv_get_number_chk(&argvars[1], NULL); // -1 on type error
+ }
+ idx = calc_hist_idx(type, idx);
+ if (idx < 0) {
+ rettv->vval.v_string = xstrnsave("", 0);
+ } else {
+ rettv->vval.v_string = xstrnsave(history[type][idx].hisstr,
+ history[type][idx].hisstrlen);
}
- // -1 on type error
- rettv->vval.v_string = xstrdup(get_history_entry(type, idx));
}
rettv->v_type = VAR_STRING;
}
@@ -591,9 +586,10 @@ void f_histnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
? HIST_INVALID
: get_histtype(histname, strlen(histname), false);
if (i != HIST_INVALID) {
- i = get_history_idx(i);
+ rettv->vval.v_number = get_history_idx(i);
+ } else {
+ rettv->vval.v_number = HIST_INVALID;
}
- rettv->vval.v_number = i;
}
/// :history command - print a history
@@ -642,10 +638,8 @@ void ex_history(exarg_T *eap)
}
for (; !got_int && histype1 <= histype2; histype1++) {
- xstrlcpy(IObuff, "\n # ", IOSIZE);
assert(history_names[histype1] != NULL);
- xstrlcat(IObuff, history_names[histype1], IOSIZE);
- xstrlcat(IObuff, " history", IOSIZE);
+ vim_snprintf(IObuff, IOSIZE, "\n # %s history", history_names[histype1]);
msg_puts_title(IObuff);
int idx = hisidx[histype1];
histentry_T *hist = history[histype1];
@@ -666,15 +660,14 @@ void ex_history(exarg_T *eap)
&& hist[i].hisnum >= j && hist[i].hisnum <= k
&& !message_filtered(hist[i].hisstr)) {
msg_putchar('\n');
- snprintf(IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
- hist[i].hisnum);
+ int len = snprintf(IObuff, IOSIZE,
+ "%c%6d ", i == idx ? '>' : ' ', hist[i].hisnum);
if (vim_strsize(hist[i].hisstr) > Columns - 10) {
- trunc_string(hist[i].hisstr, IObuff + strlen(IObuff),
- Columns - 10, IOSIZE - (int)strlen(IObuff));
+ trunc_string(hist[i].hisstr, IObuff + len, Columns - 10, IOSIZE - len);
} else {
- xstrlcat(IObuff, hist[i].hisstr, IOSIZE);
+ xstrlcpy(IObuff + len, hist[i].hisstr, (size_t)(IOSIZE - len));
}
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
}
if (i == idx) {
break;
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index 4df4b09e68..c933982593 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -23,6 +23,7 @@ enum { HIST_COUNT = HIST_DEBUG + 1, }; ///< Number of history tables
typedef struct {
int hisnum; ///< Entry identifier number.
char *hisstr; ///< Actual entry, separator char after the NUL.
+ size_t hisstrlen; ///< Length of hisstr (excluding the NUL).
Timestamp timestamp; ///< Time when entry was added.
AdditionalData *additional_data; ///< Additional entries from ShaDa file.
} histentry_T;
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h
index 49dc4f9168..58ba93a7ba 100644
--- a/src/nvim/decoration_defs.h
+++ b/src/nvim/decoration_defs.h
@@ -55,7 +55,7 @@ typedef struct {
schar_T conceal_char;
} DecorHighlightInline;
-#define DECOR_HIGHLIGHT_INLINE_INIT { 0, DECOR_PRIORITY_BASE, 0, 0 }
+#define DECOR_HIGHLIGHT_INLINE_INIT { 0, DECOR_PRIORITY_BASE, 0, 0 }
typedef struct {
uint16_t flags;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index d22fb65827..f1dd08f0e6 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -801,7 +801,7 @@ static int diff_write(buf_T *buf, diffin_T *din)
// Always use 'fileformat' set to "unix".
char *save_ff = buf->b_p_ff;
- buf->b_p_ff = xstrdup(FF_UNIX);
+ buf->b_p_ff = xstrdup("unix");
const bool save_cmod_flags = cmdmod.cmod_flags;
// Writing the buffer is an implementation detail of performing the diff,
// so it shouldn't update the '[ and '] marks.
@@ -1390,8 +1390,8 @@ void diff_win_options(win_T *wp, bool addbuf)
}
wp->w_p_fdm_save = xstrdup(wp->w_p_fdm);
}
- set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, kOptReqWin,
- wp);
+ set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0,
+ kOptScopeWin, wp);
if (!wp->w_p_diff) {
wp->w_p_fen_save = wp->w_p_fen;
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 7413d33fe4..ea0d1ba708 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -1707,7 +1707,7 @@ static void digraph_header(const char *msg)
if (msg_col > 0) {
msg_putchar('\n');
}
- msg_outtrans(msg, HL_ATTR(HLF_CM));
+ msg_outtrans(msg, HLF_CM, false);
msg_putchar('\n');
}
@@ -1861,7 +1861,7 @@ static void printdigraph(const digr_T *dp, result_T *previous)
*p++ = (char)dp->char2;
*p++ = ' ';
*p = NUL;
- msg_outtrans(buf, 0);
+ msg_outtrans(buf, 0, false);
p = buf;
// add a space to draw a composing char on
@@ -1871,14 +1871,14 @@ static void printdigraph(const digr_T *dp, result_T *previous)
p += utf_char2bytes(dp->result, p);
*p = NUL;
- msg_outtrans(buf, HL_ATTR(HLF_8));
+ msg_outtrans(buf, HLF_8, false);
p = buf;
if (char2cells(dp->result) == 1) {
*p++ = ' ';
}
assert(p >= buf);
vim_snprintf(p, sizeof(buf) - (size_t)(p - buf), " %3d", dp->result);
- msg_outtrans(buf, 0);
+ msg_outtrans(buf, 0, false);
}
/// Get the two digraph characters from a typval.
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 3b88dd2e90..79f3298eb4 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -927,7 +927,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
bool noinvcur = false; // don't invert the cursor
bool lnum_in_visual_area = false;
- bool attr_pri = false; // char_attr has priority
+ int char_attr_pri = 0; // attributes with high priority
+ int char_attr_base = 0; // attributes with low priority
bool area_highlighting = false; // Visual or incsearch highlighting in this line
int vi_attr = 0; // attributes for Visual and incsearch highlighting
int area_attr = 0; // attributes desired by highlighting
@@ -1741,16 +1742,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// Decide which of the highlight attributes to use.
- attr_pri = true;
-
if (area_attr != 0) {
- wlv.char_attr = hl_combine_attr(wlv.line_attr, area_attr);
+ char_attr_pri = hl_combine_attr(wlv.line_attr, area_attr);
if (!highlight_match) {
// let search highlight show in Visual area if possible
- wlv.char_attr = hl_combine_attr(search_attr, wlv.char_attr);
+ char_attr_pri = hl_combine_attr(search_attr, char_attr_pri);
}
} else if (search_attr != 0) {
- wlv.char_attr = hl_combine_attr(wlv.line_attr, search_attr);
+ char_attr_pri = hl_combine_attr(wlv.line_attr, search_attr);
} else if (wlv.line_attr != 0
&& ((wlv.fromcol == -10 && wlv.tocol == MAXCOL)
|| wlv.vcol < wlv.fromcol
@@ -1758,15 +1757,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|| wlv.vcol >= wlv.tocol)) {
// Use wlv.line_attr when not in the Visual or 'incsearch' area
// (area_attr may be 0 when "noinvcur" is set).
- wlv.char_attr = wlv.line_attr;
+ char_attr_pri = wlv.line_attr;
} else {
- attr_pri = false;
- wlv.char_attr = decor_attr;
- }
-
- if (folded_attr != 0) {
- wlv.char_attr = hl_combine_attr(folded_attr, wlv.char_attr);
+ char_attr_pri = 0;
}
+ char_attr_base = hl_combine_attr(folded_attr, decor_attr);
+ wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
}
if (draw_folded && has_foldtext && wlv.n_extra == 0 && wlv.col == win_col_offset) {
@@ -1997,25 +1993,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell);
}
- if (folded_attr) {
- decor_attr = hl_combine_attr(folded_attr, decor_attr);
- }
-
- if (decor_attr) {
- if (!attr_pri) {
- if (wlv.cul_attr) {
- wlv.char_attr = 0 != wlv.line_attr_lowprio
- ? hl_combine_attr(wlv.cul_attr, decor_attr)
- : hl_combine_attr(decor_attr, wlv.cul_attr);
- } else {
- wlv.char_attr = decor_attr;
- }
- } else {
- wlv.char_attr = hl_combine_attr(decor_attr, wlv.char_attr);
- }
- } else if (!attr_pri) {
- wlv.char_attr = 0;
- }
+ char_attr_base = hl_combine_attr(folded_attr, decor_attr);
+ wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
// Check spelling (unless at the end of the line).
// Only do this when there is no syntax highlighting, the
@@ -2083,11 +2062,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
if (spell_attr != 0) {
- if (!attr_pri) {
- wlv.char_attr = hl_combine_attr(wlv.char_attr, spell_attr);
- } else {
- wlv.char_attr = hl_combine_attr(spell_attr, wlv.char_attr);
- }
+ char_attr_base = hl_combine_attr(char_attr_base, spell_attr);
+ wlv.char_attr = hl_combine_attr(char_attr_base, char_attr_pri);
}
if (wp->w_buffer->terminal) {
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index aa5c66465b..e90a0d945f 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -952,7 +952,7 @@ int showmode(void)
// Position on the last line in the window, column 0
msg_pos_mode();
- int attr = HL_ATTR(HLF_CM); // Highlight mode
+ int hl_id = HLF_CM; // Highlight mode
// When the screen is too narrow to show the entire mode message,
// avoid scrolling and truncate instead.
@@ -961,7 +961,7 @@ int showmode(void)
lines_left = 0;
if (do_mode) {
- msg_puts_attr("--", attr);
+ msg_puts_hl("--", hl_id, false);
// CTRL-X in Insert mode
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
// These messages can get long, avoid a wrap in a narrow window.
@@ -981,52 +981,49 @@ int showmode(void)
}
if (length - vim_strsize(edit_submode) > 0) {
if (edit_submode_pre != NULL) {
- msg_puts_attr(edit_submode_pre, attr);
+ msg_puts_hl(edit_submode_pre, hl_id, false);
}
- msg_puts_attr(edit_submode, attr);
+ msg_puts_hl(edit_submode, hl_id, false);
}
if (edit_submode_extra != NULL) {
- msg_puts_attr(" ", attr); // Add a space in between.
- int sub_attr = edit_submode_highl < HLF_COUNT
- ? win_hl_attr(curwin, (int)edit_submode_highl)
- : attr;
- msg_puts_attr(edit_submode_extra, sub_attr);
+ msg_puts_hl(" ", hl_id, false); // Add a space in between.
+ int sub_id = edit_submode_highl < HLF_COUNT ? (int)edit_submode_highl : hl_id;
+ msg_puts_hl(edit_submode_extra, sub_id, false);
}
}
} else {
if (State & MODE_TERMINAL) {
- msg_puts_attr(_(" TERMINAL"), attr);
+ msg_puts_hl(_(" TERMINAL"), hl_id, false);
} else if (State & VREPLACE_FLAG) {
- msg_puts_attr(_(" VREPLACE"), attr);
+ msg_puts_hl(_(" VREPLACE"), hl_id, false);
} else if (State & REPLACE_FLAG) {
- msg_puts_attr(_(" REPLACE"), attr);
+ msg_puts_hl(_(" REPLACE"), hl_id, false);
} else if (State & MODE_INSERT) {
if (p_ri) {
- msg_puts_attr(_(" REVERSE"), attr);
+ msg_puts_hl(_(" REVERSE"), hl_id, false);
}
- msg_puts_attr(_(" INSERT"), attr);
+ msg_puts_hl(_(" INSERT"), hl_id, false);
} else if (restart_edit == 'I' || restart_edit == 'i'
|| restart_edit == 'a' || restart_edit == 'A') {
if (curbuf->terminal) {
- msg_puts_attr(_(" (terminal)"), attr);
+ msg_puts_hl(_(" (terminal)"), hl_id, false);
} else {
- msg_puts_attr(_(" (insert)"), attr);
+ msg_puts_hl(_(" (insert)"), hl_id, false);
}
} else if (restart_edit == 'R') {
- msg_puts_attr(_(" (replace)"), attr);
+ msg_puts_hl(_(" (replace)"), hl_id, false);
} else if (restart_edit == 'V') {
- msg_puts_attr(_(" (vreplace)"), attr);
+ msg_puts_hl(_(" (vreplace)"), hl_id, false);
}
if (State & MODE_LANGMAP) {
if (curwin->w_p_arab) {
- msg_puts_attr(_(" Arabic"), attr);
- } else if (get_keymap_str(curwin, " (%s)",
- NameBuff, MAXPATHL)) {
- msg_puts_attr(NameBuff, attr);
+ msg_puts_hl(_(" Arabic"), hl_id, false);
+ } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) {
+ msg_puts_hl(NameBuff, hl_id, false);
}
}
if ((State & MODE_INSERT) && p_paste) {
- msg_puts_attr(_(" (paste)"), attr);
+ msg_puts_hl(_(" (paste)"), hl_id, false);
}
if (VIsual_active) {
@@ -1050,9 +1047,9 @@ int showmode(void)
default:
p = N_(" SELECT BLOCK"); break;
}
- msg_puts_attr(_(p), attr);
+ msg_puts_hl(_(p), hl_id, false);
}
- msg_puts_attr(" --", attr);
+ msg_puts_hl(" --", hl_id, false);
}
need_clear = true;
@@ -1060,7 +1057,7 @@ int showmode(void)
if (reg_recording != 0
&& edit_submode == NULL // otherwise it gets too long
) {
- recording_mode(attr);
+ recording_mode(hl_id);
need_clear = true;
}
@@ -1136,7 +1133,7 @@ void clearmode(void)
msg_ext_ui_flush();
msg_pos_mode();
if (reg_recording != 0) {
- recording_mode(HL_ATTR(HLF_CM));
+ recording_mode(HLF_CM);
}
msg_clr_eos();
msg_ext_flush_showmode();
@@ -1145,16 +1142,16 @@ void clearmode(void)
msg_row = save_msg_row;
}
-static void recording_mode(int attr)
+static void recording_mode(int hl_id)
{
if (shortmess(SHM_RECORDING)) {
return;
}
- msg_puts_attr(_("recording"), attr);
+ msg_puts_hl(_("recording"), hl_id, false);
char s[4];
snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording);
- msg_puts_attr(s, attr);
+ msg_puts_hl(s, hl_id, false);
}
#define COL_RULER 17 // columns needed by standard ruler
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
index 39095db952..df94945a3d 100644
--- a/src/nvim/errors.h
+++ b/src/nvim/errors.h
@@ -151,11 +151,16 @@ EXTERN const char e_auabort[] INIT(= N_("E855: Autocommands caused command to ab
EXTERN const char e_api_error[] INIT(= N_("E5555: API call: %s"));
-EXTERN const char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called in a lua loop callback"));
+EXTERN const char e_fast_api_disabled[] INIT(= N_("E5560: %s must not be called in a fast event context"));
EXTERN const char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain"));
EXTERN const char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float"));
+EXTERN const char e_cant_find_directory_str_in_cdpath[] INIT(= N_("E344: Can't find directory \"%s\" in cdpath"));
+EXTERN const char e_cant_find_file_str_in_path[] INIT(= N_("E345: Can't find file \"%s\" in path"));
+EXTERN const char e_no_more_directory_str_found_in_cdpath[] INIT(= N_("E346: No more directory \"%s\" found in cdpath"));
+EXTERN const char e_no_more_file_str_found_in_path[] INIT(= N_("E347: No more file \"%s\" found in path"));
+
EXTERN const char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
EXTERN const char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long"));
@@ -181,6 +186,7 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")
EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
+EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type"));
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 8682139b32..faacf3c65a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -126,7 +126,7 @@ bool *eval_lavars_used = NULL;
#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
-static int echo_attr = 0; // attributes used for ":echo"
+static int echo_hl_id = 0; // highlight id used for ":echo"
/// Info used by a ":for" loop.
typedef struct {
@@ -460,6 +460,8 @@ void eval_init(void)
set_vim_var_nr(VV_SEARCHFORWARD, 1);
set_vim_var_nr(VV_HLSEARCH, 1);
set_vim_var_nr(VV_COUNT1, 1);
+ set_vim_var_special(VV_EXITING, kSpecialVarNull);
+
set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
@@ -475,7 +477,6 @@ void eval_init(void)
set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX);
set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN);
set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
- set_vim_var_special(VV_EXITING, kSpecialVarNull);
set_vim_var_nr(VV_MAXCOL, MAXCOL);
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
@@ -921,13 +922,12 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a
{
if (expr->v_type == VAR_PARTIAL) {
return eval_expr_partial(expr, argv, argc, rettv);
- } else if (expr->v_type == VAR_FUNC || want_func) {
+ }
+ if (expr->v_type == VAR_FUNC || want_func) {
return eval_expr_func(expr, argv, argc, rettv);
- } else {
- return eval_expr_string(expr, rettv);
}
- return OK;
+ return eval_expr_string(expr, rettv);
}
/// Like eval_to_bool() but using a typval_T instead of a string.
@@ -1047,7 +1047,7 @@ char *eval_to_string_eap(char *arg, const bool join_list, exarg_T *eap,
retval = typval2string(&tv, join_list);
tv_clear(&tv);
}
- clear_evalarg(&EVALARG_EVALUATE, NULL);
+ clear_evalarg(&evalarg, NULL);
return retval;
}
@@ -1367,7 +1367,7 @@ int eval_foldexpr(win_T *wp, int *cp)
const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL);
char *arg = skipwhite(wp->w_p_fde);
- current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
+ current_sctx = wp->w_p_script_ctx[kWinOptFoldexpr].script_ctx;
emsg_off++;
if (use_sandbox) {
@@ -1981,7 +1981,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, bool copy, const bool
// handle +=, -=, *=, /=, %= and .=
di = NULL;
- if (eval_variable(lp->ll_name, (int)strlen(lp->ll_name),
+ if (eval_variable(lp->ll_name, (int)lp->ll_name_len,
&tv, &di, true, false) == OK) {
if ((di == NULL
|| (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING)
@@ -2631,7 +2631,7 @@ static int may_call_simple_func(const char *arg, typval_T *rettv)
/// Handle zero level expression with optimization for a simple function call.
/// Same arguments and return value as eval0().
-static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
+int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int r = may_call_simple_func(arg, rettv);
@@ -4788,6 +4788,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL);
+ ABORTING(set_ref_in_callback)(&buf->b_ffu_cb, copyID, NULL, NULL);
}
// 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
@@ -4799,6 +4800,9 @@ bool garbage_collect(bool testing)
// 'tagfunc' callback
ABORTING(set_ref_in_tagfunc)(copyID);
+ // 'findfunc' callback
+ ABORTING(set_ref_in_findfunc)(copyID);
+
FOR_ALL_TAB_WINDOWS(tp, wp) {
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);
@@ -6850,11 +6854,11 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
char *temp_result = eval_to_string(expr_start + 1, false, false);
if (temp_result != NULL) {
- retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
- + (size_t)(in_end - expr_end) + 1);
- STRCPY(retval, in_start);
- strcat(retval, temp_result);
- strcat(retval, expr_end + 1);
+ size_t retvalsize = (size_t)(expr_start - in_start)
+ + strlen(temp_result)
+ + (size_t)(in_end - expr_end) + 1;
+ retval = xmalloc(retvalsize);
+ vim_snprintf(retval, retvalsize, "%s%s%s", in_start, temp_result, expr_end + 1);
}
xfree(temp_result);
@@ -7869,12 +7873,12 @@ void ex_echo(exarg_T *eap)
msg_start();
}
} else if (eap->cmdidx == CMD_echo) {
- msg_puts_attr(" ", echo_attr);
+ msg_puts_hl(" ", echo_hl_id, false);
}
char *tofree = encode_tv2echo(&rettv, NULL);
if (*tofree != NUL) {
msg_ext_set_kind("echo");
- msg_multiline(tofree, echo_attr, true, &need_clear);
+ msg_multiline(cstr_as_string(tofree), echo_hl_id, true, false, &need_clear);
}
xfree(tofree);
}
@@ -7900,13 +7904,13 @@ void ex_echo(exarg_T *eap)
/// ":echohl {name}".
void ex_echohl(exarg_T *eap)
{
- echo_attr = syn_name2attr(eap->arg);
+ echo_hl_id = syn_name2id(eap->arg);
}
-/// Returns the :echo attribute
-int get_echo_attr(void)
+/// Returns the :echo highlight id
+int get_echo_hl_id(void)
{
- return echo_attr;
+ return echo_hl_id;
}
/// ":execute expr1 ..." execute the result of an expression.
@@ -7957,7 +7961,7 @@ void ex_execute(exarg_T *eap)
if (ret != FAIL && ga.ga_data != NULL) {
if (eap->cmdidx == CMD_echomsg) {
msg_ext_set_kind("echomsg");
- msg(ga.ga_data, echo_attr);
+ msg(ga.ga_data, echo_hl_id);
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
int save_did_emsg = did_emsg;
@@ -8343,9 +8347,10 @@ repeat:
char *const sub = xmemdupz(s, (size_t)(p - s));
char *const str = xmemdupz(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
- s = do_string_sub(str, pat, sub, NULL, flags);
+ size_t slen;
+ s = do_string_sub(str, *fnamelen, pat, sub, NULL, flags, &slen);
*fnamep = s;
- *fnamelen = strlen(s);
+ *fnamelen = slen;
xfree(*bufp);
*bufp = s;
didit = true;
@@ -8384,12 +8389,14 @@ repeat:
/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL.
/// "flags" can be "g" to do a global substitute.
///
+/// @param ret_len length of returned buffer
+///
/// @return an allocated string, NULL for error.
-char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags)
+char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr, const char *flags,
+ size_t *ret_len)
{
regmatch_T regmatch;
garray_T ga;
- char *zero_width = NULL;
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
char *save_cpo = p_cpo;
@@ -8397,14 +8404,15 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
ga_init(&ga, 1, 200);
- int do_all = (flags[0] == 'g');
-
regmatch.rm_ic = p_ic;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
- int sublen;
char *tail = str;
- char *end = str + strlen(str);
+ char *end = str + len;
+ bool do_all = (flags[0] == 'g');
+ int sublen;
+ char *zero_width = NULL;
+
while (vim_regexec_nl(&regmatch, str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
@@ -8451,12 +8459,17 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
if (ga.ga_data != NULL) {
STRCPY((char *)ga.ga_data + ga.ga_len, tail);
+ ga.ga_len += (int)(end - tail);
}
vim_regfree(regmatch.regprog);
}
- char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
+ if (ga.ga_data != NULL) {
+ str = ga.ga_data;
+ len = (size_t)ga.ga_len;
+ }
+ char *ret = xstrnsave(str, len);
ga_clear(&ga);
if (p_cpo == empty_string_option) {
p_cpo = save_cpo;
@@ -8470,6 +8483,10 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
free_string_option(save_cpo);
}
+ if (ret_len != NULL) {
+ *ret_len = len;
+ }
+
return ret;
}
@@ -8609,7 +8626,7 @@ bool eval_has_provider(const char *feat, bool throw_if_fast)
}
if (throw_if_fast && !nlua_is_deferred_safe()) {
- semsg(e_luv_api_disabled, "Vimscript function");
+ semsg(e_fast_api_disabled, "Vimscript function");
return false;
}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 50aaf9e03b..a418b34909 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -326,17 +326,17 @@ M.funcs = {
args = { 2, 3 },
base = 1,
desc = [=[
- When the files {fname-one} and {fname-two} do not contain
+ When the files {fname_one} and {fname_two} do not contain
exactly the same text an error message is added to |v:errors|.
Also see |assert-return|.
- When {fname-one} or {fname-two} does not exist the error will
+ When {fname_one} or {fname_two} does not exist the error will
mention that.
]=],
name = 'assert_equalfile',
- params = { { 'fname-one', 'string' }, { 'fname-two', 'string' } },
+ params = { { 'fname_one', 'string' }, { 'fname_two', 'string' } },
returns = '0|1',
- signature = 'assert_equalfile({fname-one}, {fname-two})',
+ signature = 'assert_equalfile({fname_one}, {fname_two})',
},
assert_exception = {
args = { 1, 2 },
@@ -1111,7 +1111,7 @@ M.funcs = {
The character class is one of:
0 blank
1 punctuation
- 2 word character
+ 2 word character (depends on 'iskeyword')
3 emoji
other specific Unicode class
The class is used in patterns and word motions.
@@ -1137,7 +1137,7 @@ M.funcs = {
]=],
name = 'charcol',
- params = { { 'expr', 'string|integer[]' }, { 'winid', 'integer' } },
+ params = { { 'expr', 'string|any[]' }, { 'winid', 'integer' } },
returns = 'integer',
signature = 'charcol({expr} [, {winid}])',
},
@@ -1296,7 +1296,7 @@ M.funcs = {
]=],
name = 'col',
- params = { { 'expr', 'string|integer[]' }, { 'winid', 'integer' } },
+ params = { { 'expr', 'string|any[]' }, { 'winid', 'integer' } },
returns = 'integer',
signature = 'col({expr} [, {winid}])',
},
@@ -2165,6 +2165,7 @@ M.funcs = {
If {expr} starts with "./" the |current-directory| is used.
]=],
+ fast = true,
name = 'exepath',
params = { { 'expr', 'string' } },
signature = 'exepath({expr})',
@@ -3611,6 +3612,20 @@ M.funcs = {
returns = 'string',
signature = 'getcharstr([{expr}])',
},
+ getcmdcomplpat = {
+ desc = [=[
+ Return completion pattern of the current command-line.
+ Only works when the command line is being edited, thus
+ requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+ Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
+ |getcmdprompt()|, |getcmdcompltype()| and |setcmdline()|.
+ Returns an empty string when completion is not defined.
+ ]=],
+ name = 'getcmdcomplpat',
+ params = {},
+ returns = 'string',
+ signature = 'getcmdcomplpat()',
+ },
getcmdcompltype = {
desc = [=[
Return the type of the current command-line completion.
@@ -3618,7 +3633,7 @@ M.funcs = {
requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
See |:command-completion| for the return string.
Also see |getcmdtype()|, |setcmdpos()|, |getcmdline()|,
- |getcmdprompt()| and |setcmdline()|.
+ |getcmdprompt()|, |getcmdcomplpat()| and |setcmdline()|.
Returns an empty string when completion is not defined.
]=],
name = 'getcmdcompltype',
@@ -3761,6 +3776,7 @@ M.funcs = {
runtime |:runtime| completion
scriptnames sourced script names |:scriptnames|
shellcmd Shell command
+ shellcmdline Shell command line with filename arguments
sign |:sign| suboptions
syntax syntax file names |'syntax'|
syntime |:syntime| suboptions
@@ -6391,7 +6407,7 @@ M.funcs = {
base = 1,
desc = [=[
{expr1} must be a |List|, |String|, |Blob| or |Dictionary|.
- When {expr1} is a |List|| or |Dictionary|, replace each
+ When {expr1} is a |List| or |Dictionary|, replace each
item in {expr1} with the result of evaluating {expr2}.
For a |Blob| each byte is replaced.
For a |String|, each character, including composing
@@ -9525,7 +9541,7 @@ M.funcs = {
To clear the overrides pass an empty {list}: >vim
call setcellwidths([])
- <You can use the script $VIMRUNTIME/tools/emoji_list.lua to see
+ <You can use the script $VIMRUNTIME/scripts/emoji_list.lua to see
the effect for known emoji characters. Move the cursor
through the text to check if the cell widths of your terminal
match with what Vim knows about each emoji. If it doesn't
@@ -9881,6 +9897,8 @@ M.funcs = {
clear the list: >vim
call setqflist([], 'r')
<
+ 'u' Like 'r', but tries to preserve the current selection
+ in the quickfix list.
'f' All the quickfix lists in the quickfix stack are
freed.
@@ -9938,7 +9956,11 @@ M.funcs = {
]=],
name = 'setqflist',
- params = { { 'list', 'any[]' }, { 'action', 'string' }, { 'what', 'table' } },
+ params = {
+ { 'list', 'vim.quickfix.entry[]' },
+ { 'action', 'string' },
+ { 'what', 'vim.fn.setqflist.what' },
+ },
signature = 'setqflist({list} [, {action} [, {what}]])',
},
setreg = {
@@ -10994,6 +11016,44 @@ M.funcs = {
params = { { 'expr', 'number' } },
signature = 'srand([{expr}])',
},
+ state = {
+ args = { 0, 1 },
+ base = 1,
+ desc = [=[
+ Return a string which contains characters indicating the
+ current state. Mostly useful in callbacks that want to do
+ work that may not always be safe. Roughly this works like:
+ - callback uses state() to check if work is safe to do.
+ Yes: then do it right away.
+ No: add to work queue and add a |SafeState| autocommand.
+ - When SafeState is triggered and executes your autocommand,
+ check with `state()` if the work can be done now, and if yes
+ remove it from the queue and execute.
+ Remove the autocommand if the queue is now empty.
+ Also see |mode()|.
+
+ When {what} is given only characters in this string will be
+ added. E.g, this checks if the screen has scrolled: >vim
+ if state('s') == ''
+ " screen has not scrolled
+ <
+ These characters indicate the state, generally indicating that
+ something is busy:
+ m halfway a mapping, :normal command, feedkeys() or
+ stuffed command
+ o operator pending, e.g. after |d|
+ a Insert mode autocomplete active
+ x executing an autocommand
+ S not triggering SafeState, e.g. after |f| or a count
+ c callback invoked, including timer (repeats for
+ recursiveness up to "ccc")
+ s screen has scrolled for messages
+ ]=],
+ fast = true,
+ name = 'state',
+ params = { { 'what', 'string' } },
+ signature = 'state([{what}])',
+ },
stdioopen = {
args = 1,
desc = [=[
@@ -11052,44 +11112,6 @@ M.funcs = {
returns = 'string|string[]',
signature = 'stdpath({what})',
},
- state = {
- args = { 0, 1 },
- base = 1,
- desc = [=[
- Return a string which contains characters indicating the
- current state. Mostly useful in callbacks that want to do
- work that may not always be safe. Roughly this works like:
- - callback uses state() to check if work is safe to do.
- Yes: then do it right away.
- No: add to work queue and add a |SafeState| autocommand.
- - When SafeState is triggered and executes your autocommand,
- check with `state()` if the work can be done now, and if yes
- remove it from the queue and execute.
- Remove the autocommand if the queue is now empty.
- Also see |mode()|.
-
- When {what} is given only characters in this string will be
- added. E.g, this checks if the screen has scrolled: >vim
- if state('s') == ''
- " screen has not scrolled
- <
- These characters indicate the state, generally indicating that
- something is busy:
- m halfway a mapping, :normal command, feedkeys() or
- stuffed command
- o operator pending, e.g. after |d|
- a Insert mode autocomplete active
- x executing an autocommand
- S not triggering SafeState, e.g. after |f| or a count
- c callback invoked, including timer (repeats for
- recursiveness up to "ccc")
- s screen has scrolled for messages
- ]=],
- fast = true,
- name = 'state',
- params = { { 'what', 'string' } },
- signature = 'state([{what}])',
- },
str2float = {
args = 1,
base = 1,
@@ -12662,7 +12684,7 @@ M.funcs = {
]=],
name = 'virtcol',
- params = { { 'expr', 'string|integer[]' }, { 'list', 'boolean' }, { 'winid', 'integer' } },
+ params = { { 'expr', 'string|any[]' }, { 'list', 'boolean' }, { 'winid', 'integer' } },
signature = 'virtcol({expr} [, {list} [, {winid}]])',
},
virtcol2col = {
@@ -12751,7 +12773,7 @@ M.funcs = {
For example to make <c-j> work like <down> in wildmode, use: >vim
cnoremap <expr> <C-j> wildmenumode() ? "\<Down>\<Tab>" : "\<c-j>"
<
- (Note, this needs the 'wildcharm' option set appropriately).
+ (Note: this needs the 'wildcharm' option set appropriately).
]=],
name = 'wildmenumode',
params = {},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 6d1cb4b2c3..9e9e36d3c5 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1342,7 +1342,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
hlID = HLF_CHD; // Changed line.
}
}
- rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (hlID + 1);
+ rettv->vval.v_number = hlID;
}
/// "empty({expr})" function
@@ -4352,9 +4352,17 @@ static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (wp != NULL && tp != NULL) {
switchwin_T switchwin;
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
+ // in diff mode, prevent that the window scrolls
+ // and keep the topline
+ if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff) {
+ skip_update_topline = true;
+ }
check_cursor(curwin);
fp = var2fpos(&argvars[0], true, &fnum, false);
}
+ if (curwin->w_p_diff && switchwin.sw_curwin->w_p_diff) {
+ skip_update_topline = false;
+ }
restore_win_noblock(&switchwin, true);
}
} else {
@@ -7841,8 +7849,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|| flg == NULL) {
rettv->vval.v_string = NULL;
} else {
- rettv->vval.v_string = do_string_sub((char *)str, (char *)pat,
- (char *)sub, expr, (char *)flg);
+ rettv->vval.v_string = do_string_sub((char *)str, strlen(str), (char *)pat,
+ (char *)sub, expr, (char *)flg, NULL);
}
}
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 0e0088bfad..a8358aab51 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -34,6 +34,7 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/macros_defs.h"
+#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/ops.h"
@@ -1412,7 +1413,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
msg_start();
msg_puts(prefix);
if (name != NULL) { // "a:" vars don't have a name stored
- msg_puts_len(name, name_len, 0);
+ msg_puts_len(name, name_len, 0, false);
}
msg_putchar(' ');
msg_advance(22);
@@ -1434,7 +1435,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
msg_putchar(' ');
}
- msg_outtrans(string, 0);
+ msg_outtrans(string, 0, false);
if (type == VAR_FUNC || type == VAR_PARTIAL) {
msg_puts("()");
@@ -1913,7 +1914,7 @@ static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, b
const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber);
const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString);
- if (!is_tty_opt && (get_option(opt_idx)->flags & P_FUNC) && tv_is_func(*tv)) {
+ if (!is_tty_opt && (get_option(opt_idx)->flags & kOptFlagFunc) && tv_is_func(*tv)) {
// If the option can be set to a function reference or a lambda
// and the passed value is a function reference, then convert it to
// the name (string) of the function reference.
@@ -1970,14 +1971,12 @@ typval_T optval_as_tv(OptVal value, bool numbool)
case kOptValTypeNil:
break;
case kOptValTypeBoolean:
- if (value.data.boolean != kNone) {
- if (numbool) {
- rettv.v_type = VAR_NUMBER;
- rettv.vval.v_number = value.data.boolean == kTrue;
- } else {
- rettv.v_type = VAR_BOOL;
- rettv.vval.v_bool = value.data.boolean == kTrue;
- }
+ if (numbool) {
+ rettv.v_type = VAR_NUMBER;
+ rettv.vval.v_number = value.data.boolean;
+ } else if (value.data.boolean != kNone) {
+ rettv.v_type = VAR_BOOL;
+ rettv.vval.v_bool = value.data.boolean == kTrue;
}
break; // return v:null for None boolean value.
case kOptValTypeNumber:
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index a98de05815..8cccf08e11 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -189,7 +189,7 @@ void do_ascii(exarg_T *eap)
transchar(c), buf1, buf2, cval, cval, cval);
}
- msg_multiline(IObuff, 0, true, &need_clear);
+ msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear);
off += (size_t)utf_ptr2len(data); // needed for overlong ascii?
}
@@ -224,7 +224,7 @@ void do_ascii(exarg_T *eap)
c, c, c);
}
- msg_multiline(IObuff, 0, true, &need_clear);
+ msg_multiline(cstr_as_string(IObuff), 0, true, false, &need_clear);
off += (size_t)utf_ptr2len(data + off); // needed for overlong ascii?
}
@@ -1028,7 +1028,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
msg_start();
msg_putchar(':');
msg_putchar('!');
- msg_outtrans(newcmd, 0);
+ msg_outtrans(newcmd, 0, false);
msg_clr_eos();
ui_cursor_goto(msg_row, msg_col);
@@ -1469,7 +1469,7 @@ void print_line_no_prefix(linenr_T lnum, int use_number, bool list)
if (curwin->w_p_nu || use_number) {
vim_snprintf(numbuf, sizeof(numbuf), "%*" PRIdLINENR " ",
number_width(curwin), lnum);
- msg_puts_attr(numbuf, HL_ATTR(HLF_N)); // Highlight line nrs.
+ msg_puts_hl(numbuf, HLF_N + 1, false); // Highlight line nrs.
}
msg_prt_line(ml_get(lnum), list);
}
@@ -2134,7 +2134,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
if (sfname == NULL) {
sfname = ffname;
}
-#ifdef USE_FNAME_CASE
+#ifdef CASE_INSENSITIVE_FILENAME
if (sfname != NULL) {
path_fix_case(sfname); // set correct case for sfname
}
@@ -2261,7 +2261,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
if (buf == NULL) {
goto theend;
}
- // autocommands try to edit a file that is goind to be removed, abort
+ // autocommands try to edit a file that is going to be removed, abort
if (buf_locked(buf)) {
// window was split, but not editing the new buffer, reset b_nwindows again
if (oldwin == NULL
@@ -3805,7 +3805,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
msg_no_more = true;
msg_ext_set_kind("confirm_sub");
// Same highlight as wait_return().
- smsg(HL_ATTR(HLF_R), _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
+ smsg(HLF_R, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
msg_no_more = false;
msg_scroll = i;
if (!ui_has(kUIMessages)) {
@@ -4434,11 +4434,11 @@ void ex_global(exarg_T *eap)
delim = *cmd; // get the delimiter
cmd++; // skip delimiter if there is one
pat = cmd; // remember start of pattern
- patlen = strlen(pat);
cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL, NULL);
if (cmd[0] == delim) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
+ patlen = strlen(pat);
}
char *used_pat;
@@ -4796,7 +4796,7 @@ void ex_oldfiles(exarg_T *eap)
if (!message_filtered(fname)) {
msg_outnum(nr);
msg_puts(": ");
- msg_outtrans(tv_get_string(TV_LIST_ITEM_TV(li)), 0);
+ msg_outtrans(tv_get_string(TV_LIST_ITEM_TV(li)), 0, false);
msg_clr_eos();
msg_putchar('\n');
os_breakcheck();
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index aff9dce7c1..e37c37e8e6 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -450,7 +450,7 @@ int buf_write_all(buf_T *buf, bool forceit)
1, buf->b_ml.ml_line_count, NULL,
false, forceit, true, false));
if (curbuf != old_curbuf) {
- msg_source(HL_ATTR(HLF_W));
+ msg_source(HLF_W);
msg(_("Warning: Entered other buffer unexpectedly (check autocommands)"), 0);
}
return retval;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 293aaac036..f5ecedf827 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5165,6 +5165,177 @@ static void ex_wrongmodifier(exarg_T *eap)
eap->errmsg = _(e_invcmd);
}
+/// callback function for 'findfunc'
+static Callback ffu_cb;
+
+static Callback *get_findfunc_callback(void)
+{
+ return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
+}
+
+/// Call 'findfunc' to obtain a list of file names.
+static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete)
+{
+ const sctx_T saved_sctx = current_sctx;
+
+ typval_T args[3];
+ args[0].v_type = VAR_STRING;
+ args[0].vval.v_string = pat;
+ args[1].v_type = VAR_BOOL;
+ args[1].vval.v_bool = cmdcomplete;
+ args[2].v_type = VAR_UNKNOWN;
+
+ // Lock the text to prevent weird things from happening. Also disallow
+ // switching to another window, it should not be needed and may end up in
+ // Insert mode in another buffer.
+ textlock++;
+
+ sctx_T *ctx = get_option_sctx(kOptFindfunc);
+ if (ctx != NULL) {
+ current_sctx = *ctx;
+ }
+
+ Callback *cb = get_findfunc_callback();
+ typval_T rettv;
+ int retval = callback_call(cb, 2, args, &rettv);
+
+ current_sctx = saved_sctx;
+
+ textlock--;
+
+ list_T *retlist = NULL;
+
+ if (retval == OK) {
+ if (rettv.v_type == VAR_LIST) {
+ retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID());
+ } else {
+ emsg(_(e_invalid_return_type_from_findfunc));
+ }
+
+ tv_clear(&rettv);
+ }
+
+ return retlist;
+}
+
+/// Find file names matching "pat" using 'findfunc' and return it in "files".
+/// Used for expanding the :find, :sfind and :tabfind command argument.
+/// Returns OK on success and FAIL otherwise.
+int expand_findfunc(char *pat, char ***files, int *numMatches)
+{
+ *numMatches = 0;
+ *files = NULL;
+
+ list_T *l = call_findfunc(pat, kBoolVarTrue);
+ if (l == NULL) {
+ return FAIL;
+ }
+
+ int len = tv_list_len(l);
+ if (len == 0) { // empty List
+ return FAIL;
+ }
+
+ *files = xmalloc(sizeof(char *) * (size_t)len);
+
+ // Copy all the List items
+ int idx = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
+ (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ idx++;
+ }
+ });
+
+ *numMatches = idx;
+ tv_list_free(l);
+
+ return OK;
+}
+
+/// Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find
+/// the n'th matching file.
+static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
+{
+ char *ret_fname = NULL;
+
+ const char cc = findarg[findarg_len];
+ findarg[findarg_len] = NUL;
+
+ list_T *fname_list = call_findfunc(findarg, kBoolVarFalse);
+ int fname_count = tv_list_len(fname_list);
+
+ if (fname_count == 0) {
+ semsg(_(e_cant_find_file_str_in_path), findarg);
+ } else {
+ if (count > fname_count) {
+ semsg(_(e_no_more_file_str_found_in_path), findarg);
+ } else {
+ listitem_T *li = tv_list_find(fname_list, count - 1);
+ if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
+ ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
+ }
+ }
+ }
+
+ if (fname_list != NULL) {
+ tv_list_free(fname_list);
+ }
+
+ findarg[findarg_len] = cc;
+
+ return ret_fname;
+}
+
+/// Process the 'findfunc' option value.
+/// Returns NULL on success and an error message on failure.
+const char *did_set_findfunc(optset_T *args)
+{
+ buf_T *buf = (buf_T *)args->os_buf;
+ int retval;
+
+ if (args->os_flags & OPT_LOCAL) {
+ // buffer-local option set
+ retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb);
+ } else {
+ // global option set
+ retval = option_set_callback_func(p_ffu, &ffu_cb);
+ // when using :set, free the local callback
+ if (!(args->os_flags & OPT_GLOBAL)) {
+ callback_free(&buf->b_ffu_cb);
+ }
+ }
+
+ if (retval == FAIL) {
+ return e_invarg;
+ }
+
+ // If the option value starts with <SID> or s:, then replace that with
+ // the script identifier.
+ char **varp = (char **)args->os_varp;
+ char *name = get_scriptlocal_funcname(*varp);
+ if (name != NULL) {
+ free_string_option(*varp);
+ *varp = name;
+ }
+
+ return NULL;
+}
+
+void free_findfunc_option(void)
+{
+ callback_free(&ffu_cb);
+}
+
+/// Mark the global 'findfunc' callback with "copyID" so that it is not
+/// garbage collected.
+bool set_ref_in_findfunc(int copyID)
+{
+ bool abort = false;
+ abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL);
+ return abort;
+}
+
/// :sview [+command] file split window with new file, read-only
/// :split [[+command] file] split window with current or new file
/// :vsplit [[+command] file] split window vertically with current or new file
@@ -5196,13 +5367,17 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
- char *file_to_find = NULL;
- char *search_ctx = NULL;
- fname = find_file_in_path(eap->arg, strlen(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname,
- &file_to_find, &search_ctx);
- xfree(file_to_find);
- vim_findfile_cleanup(search_ctx);
+ if (*get_findfunc() != NUL) {
+ fname = findfunc_find_file(eap->arg, strlen(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+ } else {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+ fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
+ }
if (fname == NULL) {
goto theend;
}
@@ -5326,12 +5501,14 @@ static void ex_tabs(exarg_T *eap)
msg_putchar('\n');
vim_snprintf(IObuff, IOSIZE, _("Tab page %d"), tabcount++);
- msg_outtrans(IObuff, HL_ATTR(HLF_T));
+ msg_outtrans(IObuff, HLF_T, false);
os_breakcheck();
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
if (got_int) {
break;
+ } else if (!wp->w_config.focusable) {
+ continue;
}
msg_putchar('\n');
@@ -5344,7 +5521,7 @@ static void ex_tabs(exarg_T *eap)
} else {
home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true);
}
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
os_breakcheck();
}
}
@@ -5398,23 +5575,28 @@ static void ex_find(exarg_T *eap)
return;
}
- char *file_to_find = NULL;
- char *search_ctx = NULL;
- char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
- FNAME_MESS, true, curbuf->b_ffname,
- &file_to_find, &search_ctx);
- if (eap->addr_count > 0) {
- // Repeat finding the file "count" times. This matters when it appears
- // several times in the path.
- linenr_T count = eap->line2;
- while (fname != NULL && --count > 0) {
- xfree(fname);
- fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname,
- &file_to_find, &search_ctx);
+ char *fname = NULL;
+ if (*get_findfunc() != NUL) {
+ fname = findfunc_find_file(eap->arg, strlen(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+ } else {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+ fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ if (eap->addr_count > 0) {
+ // Repeat finding the file "count" times. This matters when it appears
+ // several times in the path.
+ linenr_T count = eap->line2;
+ while (fname != NULL && --count > 0) {
+ xfree(fname);
+ fname = find_file_in_path(NULL, 0, FNAME_MESS, false,
+ curbuf->b_ffname, &file_to_find, &search_ctx);
+ }
}
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
}
- xfree(file_to_find);
- vim_findfile_cleanup(search_ctx);
if (fname == NULL) {
return;
@@ -7596,7 +7778,7 @@ void verify_command(char *cmd)
if (strcmp("smile", cmd) != 0) {
return; // acceptable non-existing command
}
- int a = HL_ATTR(HLF_E);
+ int a = HLF_E;
msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx"
"xxxxxxnz+, znnnnnnnnnnnnnnnn.", a);
msg(" n###z x####` :x##########W+` ,###"
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7d87e609ca..ace62ea729 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -125,6 +125,7 @@ typedef struct {
bool gotesc; // true when <ESC> just typed
int do_abbr; // when true check for abbr.
char *lookfor; // string to match
+ int lookforlen;
int hiscnt; // current history line in use
int save_hiscnt; // history line before attempting
// to jump to next match
@@ -385,7 +386,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
if (!use_last_pat) {
char c = *end;
*end = NUL;
- bool empty = empty_pattern_magic(p, strlen(p), magic);
+ bool empty = empty_pattern_magic(p, (size_t)(end - p), magic);
*end = c;
if (empty) {
goto theend;
@@ -538,7 +539,8 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state
if (!use_last_pat) {
next_char = ccline.cmdbuff[skiplen + patlen];
ccline.cmdbuff[skiplen + patlen] = NUL;
- if (empty_pattern(ccline.cmdbuff + skiplen, search_delim) && !no_hlsearch) {
+ if (empty_pattern(ccline.cmdbuff + skiplen, (size_t)patlen, search_delim)
+ && !no_hlsearch) {
redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(true);
}
@@ -809,7 +811,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
- msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
+ msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
@@ -900,12 +902,11 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
&& ccline.cmdlen
&& s->firstc != NUL
&& (s->some_key_typed || s->histype == HIST_SEARCH)) {
- size_t cmdbufflen = strlen(ccline.cmdbuff);
- add_to_history(s->histype, ccline.cmdbuff, cmdbufflen, true,
+ add_to_history(s->histype, ccline.cmdbuff, (size_t)ccline.cmdlen, true,
s->histype == HIST_SEARCH ? s->firstc : NUL);
if (s->firstc == ':') {
xfree(new_last_cmdline);
- new_last_cmdline = xstrnsave(ccline.cmdbuff, cmdbufflen);
+ new_last_cmdline = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
}
}
@@ -1262,6 +1263,7 @@ static int command_line_execute(VimState *state, int key)
&& s->c != K_LEFT && s->c != K_RIGHT
&& (s->xpc.xp_numfiles > 0 || (s->c != Ctrl_P && s->c != Ctrl_N))) {
XFREE_CLEAR(s->lookfor);
+ s->lookforlen = 0;
}
// When there are matching completions to select <S-Tab> works like
@@ -1440,7 +1442,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
return FAIL;
}
skiplen = 0;
- patlen = (int)strlen(pat);
+ patlen = (int)last_search_pattern_len();
} else {
pat = ccline.cmdbuff + skiplen;
}
@@ -1574,7 +1576,8 @@ static int command_line_erase_chars(CommandLineState *s)
return CMDLINE_NOT_CHANGED;
}
- XFREE_CLEAR(ccline.cmdbuff); // no commandline to return
+ dealloc_cmdbuff(); // no commandline to return
+
if (!cmd_silent && !ui_has(kUICmdline)) {
msg_col = 0;
msg_putchar(' '); // delete ':'
@@ -1702,7 +1705,6 @@ static void command_line_left_right_mouse(CommandLineState *s)
static void command_line_next_histidx(CommandLineState *s, bool next_match)
{
- int j = (int)strlen(s->lookfor);
while (true) {
// one step backwards
if (!next_match) {
@@ -1746,7 +1748,7 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->save_hiscnt
|| strncmp(get_histentry(s->histype)[s->hiscnt].hisstr,
- s->lookfor, (size_t)j) == 0) {
+ s->lookfor, (size_t)s->lookforlen) == 0) {
break;
}
}
@@ -1765,30 +1767,34 @@ static int command_line_browse_history(CommandLineState *s)
// save current command string so it can be restored later
if (s->lookfor == NULL) {
- s->lookfor = xstrdup(ccline.cmdbuff);
+ s->lookfor = xstrnsave(ccline.cmdbuff, (size_t)ccline.cmdlen);
s->lookfor[ccline.cmdpos] = NUL;
+ s->lookforlen = ccline.cmdpos;
}
bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
|| s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
command_line_next_histidx(s, next_match);
- if (s->hiscnt != s->save_hiscnt) {
- // jumped to other entry
+ if (s->hiscnt != s->save_hiscnt) { // jumped to other entry
char *p;
+ int plen;
int old_firstc;
- XFREE_CLEAR(ccline.cmdbuff);
+ dealloc_cmdbuff();
+
s->xpc.xp_context = EXPAND_NOTHING;
if (s->hiscnt == get_hislen()) {
p = s->lookfor; // back to the old one
+ plen = s->lookforlen;
} else {
p = get_histentry(s->histype)[s->hiscnt].hisstr;
+ plen = (int)get_histentry(s->histype)[s->hiscnt].hisstrlen;
}
if (s->histype == HIST_SEARCH
&& p != s->lookfor
- && (old_firstc = (uint8_t)p[strlen(p) + 1]) != s->firstc) {
+ && (old_firstc = (uint8_t)p[plen + 1]) != s->firstc) {
int len = 0;
// Correct for the separator character used when
// adding the history entry vs the one used now.
@@ -1827,12 +1833,13 @@ static int command_line_browse_history(CommandLineState *s)
}
}
ccline.cmdbuff[len] = NUL;
+ ccline.cmdpos = ccline.cmdlen = len;
} else {
- alloc_cmdbuff((int)strlen(p));
+ alloc_cmdbuff(plen);
STRCPY(ccline.cmdbuff, p);
+ ccline.cmdpos = ccline.cmdlen = plen;
}
- ccline.cmdpos = ccline.cmdlen = (int)strlen(ccline.cmdbuff);
redrawcmd();
return CMDLINE_CHANGED;
}
@@ -2201,18 +2208,17 @@ static int command_line_not_changed(CommandLineState *s)
/// Guess that the pattern matches everything. Only finds specific cases, such
/// as a trailing \|, which can happen while typing a pattern.
-static bool empty_pattern(char *p, int delim)
+static bool empty_pattern(char *p, size_t len, int delim)
{
- size_t n = strlen(p);
magic_T magic_val = MAGIC_ON;
- if (n > 0) {
+ if (len > 0) {
skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val);
} else {
return true;
}
- return empty_pattern_magic(p, n, magic_val);
+ return empty_pattern_magic(p, len, magic_val);
}
static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
@@ -2225,11 +2231,9 @@ static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val)
// true, if the pattern is empty, or the pattern ends with \| and magic is
// set (or it ends with '|' and very magic is set)
- return len == 0 || (len > 1
- && ((p[len - 2] == '\\'
- && p[len - 1] == '|' && magic_val == MAGIC_ON)
- || (p[len - 2] != '\\'
- && p[len - 1] == '|' && magic_val == MAGIC_ALL)));
+ return len == 0 || (len > 1 && p[len - 1] == '|'
+ && ((p[len - 2] == '\\' && magic_val == MAGIC_ON)
+ || (p[len - 2] != '\\' && magic_val == MAGIC_ALL)));
}
handle_T cmdpreview_get_bufnr(void)
@@ -2546,6 +2550,9 @@ static bool cmdpreview_may_show(CommandLineState *s)
goto end;
}
+ // Cursor may be at the end of the message grid rather than at cmdspos.
+ // Place it there in case preview callback flushes it. #30696
+ cursorcmd();
// Flush now: external cmdline may itself wish to update the screen which is
// currently disallowed during cmdpreview(no longer needed in case that changes).
cmdline_ui_flush();
@@ -2653,7 +2660,7 @@ static void do_autocmd_cmdlinechanged(int firstc)
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
- msg_puts_attr(err.msg, HL_ATTR(HLF_E)|MSG_HIST);
+ msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
@@ -2710,7 +2717,7 @@ static int command_line_changed(CommandLineState *s)
/// Abandon the command line.
static void abandon_cmdline(void)
{
- XFREE_CLEAR(ccline.cmdbuff);
+ dealloc_cmdbuff();
ccline.redraw_state = kCmdRedrawNone;
if (msg_scrolled == 0) {
compute_cmdrow();
@@ -2751,13 +2758,13 @@ char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNU
///
/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug.
/// @param[in] prompt Prompt string: what is displayed before the user text.
-/// @param[in] attr Prompt highlighting.
+/// @param[in] hl_id Prompt highlight id.
/// @param[in] xp_context Type of expansion.
/// @param[in] xp_arg User-defined expansion argument.
/// @param[in] highlight_callback Callback used for highlighting user input.
///
/// @return [allocated] Command line or NULL.
-char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr,
+char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
const int xp_context, const char *const xp_arg,
const Callback highlight_callback)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
@@ -2775,7 +2782,7 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at
}
ccline.prompt_id = last_prompt_id++;
ccline.cmdprompt = (char *)prompt;
- ccline.cmdattr = attr;
+ ccline.hl_id = hl_id;
ccline.xp_context = xp_context;
ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
@@ -3000,8 +3007,15 @@ bool cmdline_at_end(void)
return (ccline.cmdpos >= ccline.cmdlen);
}
-// Allocate a new command line buffer.
-// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
+/// Deallocate a command line buffer, updating the buffer size and length.
+static void dealloc_cmdbuff(void)
+{
+ XFREE_CLEAR(ccline.cmdbuff);
+ ccline.cmdlen = ccline.cmdbufflen = 0;
+}
+
+/// Allocate a new command line buffer.
+/// Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
static void alloc_cmdbuff(int len)
{
// give some extra space to avoid having to allocate all the time
@@ -3023,6 +3037,7 @@ void realloc_cmdbuff(int len)
}
char *p = ccline.cmdbuff;
+
alloc_cmdbuff(len); // will get some more
// There isn't always a NUL after the command, but it may need to be
// there, thus copy up to the NUL and add a NUL.
@@ -3083,15 +3098,13 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = (int)chunk.start.col,
- .attr = 0,
+ .hl_id = 0,
}));
}
- const int id = syn_name2id(chunk.group);
- const int attr = (id == 0 ? 0 : syn_id2attr(id));
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)chunk.start.col,
.end = (int)chunk.end_col,
- .attr = attr,
+ .hl_id = syn_name2id(chunk.group),
}));
prev_end = chunk.end_col;
}
@@ -3099,7 +3112,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = colored_ccline->cmdlen,
- .attr = 0,
+ .hl_id = 0,
}));
}
kvi_destroy(colors);
@@ -3128,7 +3141,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
#define PRINT_ERRMSG(...) \
do { \
msg_putchar('\n'); \
- msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, __VA_ARGS__); \
+ msg_printf_hl(HLF_E, __VA_ARGS__); \
printed_errmsg = true; \
} while (0)
bool ret = true;
@@ -3263,7 +3276,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = (int)start,
- .attr = 0,
+ .hl_id = 0,
}));
}
const varnumber_T end =
@@ -3287,12 +3300,10 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
if (group == NULL) {
goto color_cmdline_error;
}
- const int id = syn_name2id(group);
- const int attr = (id == 0 ? 0 : syn_id2attr(id));
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)start,
.end = (int)end,
- .attr = attr,
+ .hl_id = syn_name2id(group),
}));
i++;
});
@@ -3300,7 +3311,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
kv_push(ccline_colors->colors, ((CmdlineColorChunk) {
.start = (int)prev_end,
.end = colored_ccline->cmdlen,
- .attr = 0,
+ .hl_id = 0,
}));
}
prev_prompt_errors = 0;
@@ -3362,10 +3373,10 @@ static void draw_cmdline(int start, int len)
continue;
}
const int chunk_start = MAX(chunk.start, start);
- msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.attr);
+ msg_outtrans_len(ccline.cmdbuff + chunk_start, chunk.end - chunk_start, chunk.hl_id, false);
}
} else {
- msg_outtrans_len(ccline.cmdbuff + start, len, 0);
+ msg_outtrans_len(ccline.cmdbuff + start, len, 0, false);
}
}
}
@@ -3391,7 +3402,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
Array item = arena_array(&arena, 2);
- ADD_C(item, INTEGER_OBJ(chunk.attr));
+ ADD_C(item, INTEGER_OBJ(chunk.hl_id == 0 ? 0 : syn_id2attr(chunk.hl_id)));
assert(chunk.end >= chunk.start);
ADD_C(item, STRING_OBJ(cbuf_as_string(line->cmdbuff + chunk.start,
@@ -3581,38 +3592,14 @@ void put_on_cmdline(const char *str, int len, bool redraw)
memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len);
ccline.cmdbuff[ccline.cmdlen] = NUL;
- {
- // When the inserted text starts with a composing character,
- // backup to the character before it. There could be two of them.
- int i = 0;
- int c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
- // TODO(bfredl): this can be corrected/simplified as utf_head_off implements the
- // correct grapheme cluster breaks
- while (ccline.cmdpos > 0 && utf_iscomposing_legacy(c)) {
- i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
+ // When the inserted text starts with a composing character,
+ // backup to the character before it.
+ if (ccline.cmdpos > 0 && (uint8_t)ccline.cmdbuff[ccline.cmdpos] >= 0x80) {
+ int i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos);
+ if (i != 0) {
ccline.cmdpos -= i;
len += i;
- c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
- }
- if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) {
- // Check the previous character for Arabic combining pair.
- i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
- if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) {
- ccline.cmdpos -= i;
- len += i;
- } else {
- i = 0;
- }
- }
- if (i != 0) {
- // Also backup the cursor position.
- i = ptr2cells(ccline.cmdbuff + ccline.cmdpos);
- ccline.cmdspos -= i;
- msg_col -= i;
- if (msg_col < 0) {
- msg_col += Columns;
- msg_row--;
- }
+ ccline.cmdspos = cmd_screencol(ccline.cmdpos);
}
}
@@ -3801,7 +3788,7 @@ static void redrawcmdprompt(void)
msg_putchar(ccline.cmdfirstc);
}
if (ccline.cmdprompt != NULL) {
- msg_puts_attr(ccline.cmdprompt, ccline.cmdattr);
+ msg_puts_hl(ccline.cmdprompt, ccline.hl_id, false);
ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns;
// do the reverse of cmd_startcol()
if (ccline.cmdfirstc != NUL) {
@@ -4074,14 +4061,44 @@ static char *get_cmdline_str(void)
return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
}
+/// Get the current command-line completion pattern.
+static char *get_cmdline_completion_pattern(void)
+{
+ if (cmdline_star > 0) {
+ return NULL;
+ }
+
+ CmdlineInfo *p = get_ccline_ptr();
+ if (p == NULL || p->xpc == NULL) {
+ return NULL;
+ }
+
+ int xp_context = p->xpc->xp_context;
+ if (xp_context == EXPAND_NOTHING) {
+ set_expand_context(p->xpc);
+ xp_context = p->xpc->xp_context;
+ p->xpc->xp_context = EXPAND_NOTHING;
+ }
+ if (xp_context == EXPAND_UNSUCCESSFUL) {
+ return NULL;
+ }
+
+ char *compl_pat = p->xpc->xp_pattern;
+ if (compl_pat == NULL) {
+ return NULL;
+ }
+
+ return xstrdup(compl_pat);
+}
+
/// Get the current command-line completion type.
static char *get_cmdline_completion(void)
{
if (cmdline_star > 0) {
return NULL;
}
- CmdlineInfo *p = get_ccline_ptr();
+ CmdlineInfo *p = get_ccline_ptr();
if (p == NULL || p->xpc == NULL) {
return NULL;
}
@@ -4111,6 +4128,13 @@ static char *get_cmdline_completion(void)
return xstrdup(cmd_compl);
}
+/// "getcmdcomplpat()" function
+void f_getcmdcomplpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_cmdline_completion_pattern();
+}
+
/// "getcmdcompltype()" function
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -4509,17 +4533,20 @@ static int open_cmdwin(void)
cmdwin_result = Ctrl_C;
}
// Set the new command line from the cmdline buffer.
- xfree(ccline.cmdbuff);
+ dealloc_cmdbuff();
+
if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed
const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!";
+ size_t plen = (cmdwin_result == K_XF2) ? 2 : 3;
if (histtype == HIST_CMD) {
// Execute the command directly.
- ccline.cmdbuff = xstrdup(p);
+ ccline.cmdbuff = xmemdupz(p, plen);
+ ccline.cmdlen = (int)plen;
+ ccline.cmdbufflen = (int)plen + 1;
cmdwin_result = CAR;
} else {
// First need to cancel what we were doing.
- ccline.cmdbuff = NULL;
stuffcharReadbuff(':');
stuffReadbuff(p);
stuffcharReadbuff(CAR);
@@ -4529,17 +4556,18 @@ static int open_cmdwin(void)
// and don't modify the cmd window.
ccline.cmdbuff = NULL;
} else {
- ccline.cmdbuff = xstrdup(get_cursor_line_ptr());
+ ccline.cmdlen = get_cursor_line_len();
+ ccline.cmdbufflen = ccline.cmdlen + 1;
+ ccline.cmdbuff = xstrnsave(get_cursor_line_ptr(), (size_t)ccline.cmdlen);
}
+
if (ccline.cmdbuff == NULL) {
- ccline.cmdbuff = xstrdup("");
+ ccline.cmdbuff = xmemdupz("", 0);
ccline.cmdlen = 0;
ccline.cmdbufflen = 1;
ccline.cmdpos = 0;
cmdwin_result = Ctrl_C;
} else {
- ccline.cmdlen = (int)strlen(ccline.cmdbuff);
- ccline.cmdbufflen = ccline.cmdlen + 1;
ccline.cmdpos = curwin->w_cursor.col;
// If the cursor is on the last character, it probably should be after it.
if (ccline.cmdpos == ccline.cmdlen - 1 || ccline.cmdpos > ccline.cmdlen) {
@@ -4748,7 +4776,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
p = lastnl + 1;
msg_start();
msg_clr_eos();
- msg_puts_len(prompt, p - prompt, get_echo_attr());
+ msg_puts_len(prompt, p - prompt, get_echo_hl_id(), false);
msg_didout = false;
msg_starthere();
}
@@ -4759,7 +4787,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
- rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_attr(),
+ rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
xp_type, xp_arg, input_callback);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);
diff --git a/src/nvim/ex_getln_defs.h b/src/nvim/ex_getln_defs.h
index daba6cabb8..584c360450 100644
--- a/src/nvim/ex_getln_defs.h
+++ b/src/nvim/ex_getln_defs.h
@@ -10,8 +10,8 @@
/// Defines a region which has the same highlighting.
typedef struct {
int start; ///< Colored chunk start.
- int end; ///< Colored chunk end (exclusive, > start).
- int attr; ///< Highlight attr.
+ int end; ///< Colored chunk end (exclusive, > start).
+ int hl_id; ///< Highlight id.
} CmdlineColorChunk;
/// Command-line colors
@@ -49,7 +49,7 @@ struct cmdline_info {
int cmdfirstc; ///< ':', '/', '?', '=', '>' or NUL
int cmdindent; ///< number of spaces before cmdline
char *cmdprompt; ///< message in front of cmdline
- int cmdattr; ///< attributes for prompt
+ int hl_id; ///< highlight id for prompt
int overstrike; ///< Typing mode on the command line. Shared by
///< getcmdline() and put_on_cmdline().
expand_T *xpc; ///< struct being used for expansion, xp_pattern
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index cdfd281718..d183978d2d 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -1489,15 +1489,15 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch
if (file_name == NULL && (options & FNAME_MESS)) {
if (first == true) {
if (find_what == FINDFILE_DIR) {
- semsg(_("E344: Can't find directory \"%s\" in cdpath"), *file_to_find);
+ semsg(_(e_cant_find_directory_str_in_cdpath), *file_to_find);
} else {
- semsg(_("E345: Can't find file \"%s\" in path"), *file_to_find);
+ semsg(_(e_cant_find_file_str_in_path), *file_to_find);
}
} else {
if (find_what == FINDFILE_DIR) {
- semsg(_("E346: No more directory \"%s\" found in cdpath"), *file_to_find);
+ semsg(_(e_no_more_directory_str_found_in_cdpath), *file_to_find);
} else {
- semsg(_("E347: No more file \"%s\" found in path"), *file_to_find);
+ semsg(_(e_no_more_file_str_found_in_path), *file_to_find);
}
}
}
@@ -1648,7 +1648,7 @@ static char *eval_includeexpr(const char *const ptr, const size_t len)
{
const sctx_T save_sctx = current_sctx;
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- current_sctx = curbuf->b_p_script_ctx[BV_INEX].script_ctx;
+ current_sctx = curbuf->b_p_script_ctx[kBufOptIncludeexpr].script_ctx;
char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, kOptIncludeexpr, OPT_LOCAL),
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 724d754ca7..fc7fabc009 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -99,7 +99,7 @@
static const char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name");
-void filemess(buf_T *buf, char *name, char *s, int attr)
+void filemess(buf_T *buf, char *name, char *s)
{
int prev_msg_col = msg_col;
@@ -129,7 +129,7 @@ void filemess(buf_T *buf, char *name, char *s, int attr)
msg_scroll = msg_scroll_save;
msg_scrolled_ign = true;
// may truncate the message to avoid a hit-return prompt
- msg_outtrans(msg_may_trunc(false, IObuff), attr);
+ msg_outtrans(msg_may_trunc(false, IObuff), 0, false);
msg_clr_eos();
ui_flush();
msg_scrolled_ign = false;
@@ -335,7 +335,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// If the name is too long we might crash further on, quit here.
if (namelen >= MAXPATHL) {
- filemess(curbuf, fname, _("Illegal file name"), 0);
+ filemess(curbuf, fname, _("Illegal file name"));
msg_end();
msg_scroll = msg_save;
goto theend;
@@ -346,7 +346,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// swap file may destroy it! Reported on MS-DOS and Win 95.
if (after_pathsep(fname, fname + namelen)) {
if (!silent) {
- filemess(curbuf, fname, _(msg_is_a_directory), 0);
+ filemess(curbuf, fname, _(msg_is_a_directory));
}
msg_end();
msg_scroll = msg_save;
@@ -374,11 +374,11 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
) {
if (S_ISDIR(perm)) {
if (!silent) {
- filemess(curbuf, fname, _(msg_is_a_directory), 0);
+ filemess(curbuf, fname, _(msg_is_a_directory));
}
retval = NOTDONE;
} else {
- filemess(curbuf, fname, _("is not a file"), 0);
+ filemess(curbuf, fname, _("is not a file"));
}
msg_end();
msg_scroll = msg_save;
@@ -467,9 +467,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
if (!silent) {
if (dir_of_file_exists(fname)) {
- filemess(curbuf, sfname, _("[New]"), 0);
+ filemess(curbuf, sfname, _("[New]"));
} else {
- filemess(curbuf, sfname, _("[New DIRECTORY]"), 0);
+ filemess(curbuf, sfname, _("[New DIRECTORY]"));
}
}
// Even though this is a new file, it might have been
@@ -497,10 +497,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// open() does not set
// EOVERFLOW
(fd == -EOVERFLOW) ? _("[File too big]")
- : _("[Permission Denied]")), 0);
+ : _("[Permission Denied]")));
#else
filemess(curbuf, sfname, ((fd == UV_EFBIG) ? _("[File too big]")
- : _("[Permission Denied]")), 0);
+ : _("[Permission Denied]")));
#endif
curbuf->b_p_ro = true; // must use "w!" now
@@ -658,7 +658,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
if (!recoverymode && !filtering && !(flags & READ_DUMMY) && !silent) {
if (!read_stdin && !read_buffer) {
- filemess(curbuf, sfname, "", 0);
+ filemess(curbuf, sfname, "");
}
}
@@ -1685,7 +1685,7 @@ failed:
if (got_int) {
if (!(flags & READ_DUMMY)) {
- filemess(curbuf, sfname, _(e_interr), 0);
+ filemess(curbuf, sfname, _(e_interr));
if (newfile) {
curbuf->b_p_ro = true; // must use "w!" now
}
@@ -3035,9 +3035,9 @@ int buf_check_timestamp(buf_T *buf)
} else {
if (!autocmd_busy) {
msg_start();
- msg_puts_attr(tbuf, HL_ATTR(HLF_E) + MSG_HIST);
+ msg_puts_hl(tbuf, HLF_E, true);
if (*mesg2 != NUL) {
- msg_puts_attr(mesg2, HL_ATTR(HLF_W) + MSG_HIST);
+ msg_puts_hl(mesg2, HLF_W, true);
}
msg_clr_eos();
msg_end();
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index e7231c31ab..c9699cb161 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1731,7 +1731,7 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
curwin = wp;
curbuf = wp->w_buffer;
- current_sctx = wp->w_p_script_ctx[WV_FDT].script_ctx;
+ current_sctx = wp->w_p_script_ctx[kWinOptFoldtext].script_ctx;
emsg_off++; // handle exceptions, but don't display errors
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 9e0aa407a1..a78f746fee 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -750,7 +750,7 @@ local function process_function(fn)
write_shifted_output(
[[
if (!nlua_is_deferred_safe()) {
- return luaL_error(lstate, e_luv_api_disabled, "%s");
+ return luaL_error(lstate, e_fast_api_disabled, "%s");
}
]],
fn.name
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 3e8ae19c9a..30a83330eb 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -136,8 +136,8 @@ for i = 1, #events do
call_output:write(' }\n')
call_output:write(' entered = true;\n')
write_arglist(call_output, ev)
- call_output:write(' ui_call_event("' .. ev.name .. '", ' .. args .. ');\n')
- call_output:write(' entered = false;\n')
+ call_output:write((' ui_call_event("%s", %s, %s)'):format(ev.name, tostring(ev.fast), args))
+ call_output:write(';\n entered = false;\n')
elseif ev.compositor_impl then
call_output:write(' ui_comp_' .. ev.name)
write_signature(call_output, ev, '', true)
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 591a6b93df..02f3ac3257 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -1,6 +1,10 @@
local options_file = arg[1]
+local options_enum_file = arg[2]
+local options_map_file = arg[3]
local opt_fd = assert(io.open(options_file, 'w'))
+local opt_enum_fd = assert(io.open(options_enum_file, 'w'))
+local opt_map_fd = assert(io.open(options_map_file, 'w'))
local w = function(s)
if s:match('^ %.') then
@@ -10,30 +14,29 @@ local w = function(s)
end
end
+--- @param s string
+local function enum_w(s)
+ opt_enum_fd:write(s .. '\n')
+end
+
+--- @param s string
+local function map_w(s)
+ opt_map_fd:write(s .. '\n')
+end
+
--- @module 'nvim.options'
local options = require('options')
+local options_meta = options.options
local cstr = options.cstr
+local valid_scopes = options.valid_scopes
-local redraw_flags = {
- ui_option = 'P_UI_OPTION',
- tabline = 'P_RTABL',
- statuslines = 'P_RSTAT',
- current_window = 'P_RWIN',
- current_buffer = 'P_RBUF',
- all_windows = 'P_RALL',
- curswant = 'P_CURSWANT',
- highlight_only = 'P_HLONLY',
-}
-
-local list_flags = {
- comma = 'P_COMMA',
- onecomma = 'P_ONECOMMA',
- commacolon = 'P_COMMA|P_COLON',
- onecommacolon = 'P_ONECOMMA|P_COLON',
- flags = 'P_FLAGLIST',
- flagscomma = 'P_COMMA|P_FLAGLIST',
-}
+--- Options for each scope.
+--- @type table<string, vim.option_meta[]>
+local scope_options = {}
+for _, scope in ipairs(valid_scopes) do
+ scope_options[scope] = {}
+end
--- @param s string
--- @return string
@@ -41,6 +44,120 @@ local lowercase_to_titlecase = function(s)
return s:sub(1, 1):upper() .. s:sub(2)
end
+-- Generate options enum file
+enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
+enum_w('')
+
+--- Map of option name to option index
+--- @type table<string, string>
+local option_index = {}
+
+-- Generate option index enum and populate the `option_index` and `scope_option` dicts.
+enum_w('typedef enum {')
+enum_w(' kOptInvalid = -1,')
+
+for i, o in ipairs(options_meta) do
+ local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
+ enum_w((' %s = %u,'):format(enum_val_name, i - 1))
+
+ option_index[o.full_name] = enum_val_name
+
+ if o.abbreviation then
+ option_index[o.abbreviation] = enum_val_name
+ end
+
+ if o.alias then
+ o.alias = type(o.alias) == 'string' and { o.alias } or o.alias
+
+ for _, v in ipairs(o.alias) do
+ option_index[v] = enum_val_name
+ end
+ end
+
+ for _, scope in ipairs(o.scope) do
+ table.insert(scope_options[scope], o)
+ end
+end
+
+enum_w(' // Option count')
+enum_w('#define kOptCount ' .. tostring(#options_meta))
+enum_w('} OptIndex;')
+
+--- @param scope string
+--- @param option_name string
+--- @return string
+local get_scope_option = function(scope, option_name)
+ return ('k%sOpt%s'):format(lowercase_to_titlecase(scope), lowercase_to_titlecase(option_name))
+end
+
+-- Generate option index enum for each scope
+for _, scope in ipairs(valid_scopes) do
+ enum_w('')
+
+ local scope_name = lowercase_to_titlecase(scope)
+ enum_w('typedef enum {')
+ enum_w((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
+
+ for idx, option in ipairs(scope_options[scope]) do
+ enum_w((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
+ end
+
+ enum_w((' // %s option count'):format(scope_name))
+ enum_w(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
+ enum_w(('} %sOptIndex;'):format(scope_name))
+end
+
+-- Generate reverse lookup from option scope index to option index for each scope.
+for _, scope in ipairs(valid_scopes) do
+ enum_w('')
+ enum_w(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
+ for _, option in ipairs(scope_options[scope]) do
+ local idx = option_index[option.full_name]
+ enum_w((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
+ end
+ enum_w('});')
+end
+
+opt_enum_fd:close()
+
+-- Generate option index map.
+local hashy = require('generators.hashy')
+local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx)
+ return ('option_hash_elems[%s].name'):format(idx)
+end)
+
+map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
+
+for _, name in ipairs(neworder) do
+ assert(option_index[name] ~= nil)
+ map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
+end
+
+map_w('};\n')
+map_w('static ' .. hashfun)
+
+opt_map_fd:close()
+
+local redraw_flags = {
+ ui_option = 'kOptFlagUIOption',
+ tabline = 'kOptFlagRedrTabl',
+ statuslines = 'kOptFlagRedrStat',
+ current_window = 'kOptFlagRedrWin',
+ current_buffer = 'kOptFlagRedrBuf',
+ all_windows = 'kOptFlagRedrAll',
+ curswant = 'kOptFlagCurswant',
+ highlight_only = 'kOptFlagHLOnly',
+}
+
+local list_flags = {
+ comma = 'kOptFlagComma',
+ onecomma = 'kOptFlagOneComma',
+ commacolon = 'kOptFlagComma|kOptFlagColon',
+ onecommacolon = 'kOptFlagOneComma|kOptFlagColon',
+ flags = 'kOptFlagFlagList',
+ flagscomma = 'kOptFlagComma|kOptFlagFlagList',
+}
+
--- @param o vim.option_meta
--- @return string
local function get_flags(o)
@@ -61,28 +178,27 @@ local function get_flags(o)
end
end
if o.expand then
- add_flag('P_EXPAND')
+ add_flag('kOptFlagExpand')
if o.expand == 'nodefault' then
- add_flag('P_NO_DEF_EXP')
+ add_flag('kOptFlagNoDefExp')
end
end
for _, flag_desc in ipairs({
- { 'alloced' },
- { 'nodefault' },
- { 'no_mkrc' },
+ { 'nodefault', 'NoDefault' },
+ { 'no_mkrc', 'NoMkrc' },
{ 'secure' },
{ 'gettext' },
- { 'noglob' },
- { 'normal_fname_chars', 'P_NFNAME' },
- { 'normal_dname_chars', 'P_NDNAME' },
- { 'pri_mkrc' },
- { 'deny_in_modelines', 'P_NO_ML' },
- { 'deny_duplicates', 'P_NODUP' },
- { 'modelineexpr', 'P_MLE' },
+ { 'noglob', 'NoGlob' },
+ { 'normal_fname_chars', 'NFname' },
+ { 'normal_dname_chars', 'NDname' },
+ { 'pri_mkrc', 'PriMkrc' },
+ { 'deny_in_modelines', 'NoML' },
+ { 'deny_duplicates', 'NoDup' },
+ { 'modelineexpr', 'MLE' },
{ 'func' },
}) do
local key_name = flag_desc[1]
- local def_name = flag_desc[2] or ('P_' .. key_name:upper())
+ local def_name = 'kOptFlag' .. (flag_desc[2] or lowercase_to_titlecase(key_name))
if o[key_name] then
add_flag(def_name)
end
@@ -90,6 +206,18 @@ local function get_flags(o)
return flags
end
+--- @param opt_type vim.option_type
+--- @return string
+local function opt_type_enum(opt_type)
+ return ('kOptValType%s'):format(lowercase_to_titlecase(opt_type))
+end
+
+--- @param scope vim.option_scope
+--- @return string
+local function opt_scope_enum(scope)
+ return ('kOptScope%s'):format(lowercase_to_titlecase(scope))
+end
+
--- @param o vim.option_meta
--- @return string
local function get_type_flags(o)
@@ -99,12 +227,41 @@ local function get_type_flags(o)
for _, opt_type in ipairs(opt_types) do
assert(type(opt_type) == 'string')
- type_flags = ('%s | (1 << kOptValType%s)'):format(type_flags, lowercase_to_titlecase(opt_type))
+ type_flags = ('%s | (1 << %s)'):format(type_flags, opt_type_enum(opt_type))
end
return type_flags
end
+--- @param o vim.option_meta
+--- @return string
+local function get_scope_flags(o)
+ local scope_flags = '0'
+
+ for _, scope in ipairs(o.scope) do
+ scope_flags = ('%s | (1 << %s)'):format(scope_flags, opt_scope_enum(scope))
+ end
+
+ return scope_flags
+end
+
+--- @param o vim.option_meta
+--- @return string
+local function get_scope_idx(o)
+ --- @type string[]
+ local strs = {}
+
+ for _, scope in pairs(valid_scopes) do
+ local has_scope = vim.tbl_contains(o.scope, scope)
+ strs[#strs + 1] = (' [%s] = %s'):format(
+ opt_scope_enum(scope),
+ get_scope_option(scope, has_scope and o.full_name or 'Invalid')
+ )
+ end
+
+ return ('{\n%s\n }'):format(table.concat(strs, ',\n'))
+end
+
--- @param c string|string[]
--- @param base_string? string
--- @return string
@@ -125,32 +282,49 @@ local function get_cond(c, base_string)
return cond_string
end
+--- @param s string
+--- @return string
+local static_cstr_as_string = function(s)
+ return ('{ .data = %s, .size = sizeof(%s) - 1 }'):format(s, s)
+end
+
+--- @param v vim.option_value|function
+--- @return string
+local get_opt_val = function(v)
+ --- @type vim.option_type
+ local v_type
+
+ if type(v) == 'function' then
+ v, v_type = v() --[[ @as string, vim.option_type ]]
+
+ if v_type == 'string' then
+ v = static_cstr_as_string(v)
+ end
+ else
+ v_type = type(v) --[[ @as vim.option_type ]]
+
+ if v_type == 'boolean' then
+ v = v and 'true' or 'false'
+ elseif v_type == 'number' then
+ v = ('%iL'):format(v)
+ elseif v_type == 'string' then
+ v = static_cstr_as_string(cstr(v))
+ end
+ end
+
+ return ('{ .type = %s, .data.%s = %s }'):format(opt_type_enum(v_type), v_type, v)
+end
+
+--- @param d vim.option_value|function
+--- @param n string
+--- @return string
local get_defaults = function(d, n)
if d == nil then
error("option '" .. n .. "' should have a default value")
end
-
- local value_dumpers = {
- ['function'] = function(v)
- return v()
- end,
- string = function(v)
- return '.string=' .. cstr(v)
- end,
- boolean = function(v)
- return '.boolean=' .. (v and 'true' or 'false')
- end,
- number = function(v)
- return ('.number=%iL'):format(v)
- end,
- }
-
- return value_dumpers[type(d)](d)
+ return get_opt_val(d)
end
---- @type [string,string][]
-local defines = {}
-
--- @param i integer
--- @param o vim.option_meta
local function dump_option(i, o)
@@ -161,48 +335,28 @@ local function dump_option(i, o)
end
w(' .flags=' .. get_flags(o))
w(' .type_flags=' .. get_type_flags(o))
+ w(' .scope_flags=' .. get_scope_flags(o))
+ w(' .scope_idx=' .. get_scope_idx(o))
if o.enable_if then
w(get_cond(o.enable_if))
end
- -- An option cannot be both hidden and immutable.
- assert(not o.hidden or not o.immutable)
+ local is_window_local = #o.scope == 1 and o.scope[1] == 'win'
- local has_var = true
- if o.varname then
- w(' .var=&' .. o.varname)
- elseif o.hidden or o.immutable then
- -- Hidden and immutable options can directly point to the default value.
- w((' .var=&options[%u].def_val'):format(i - 1))
- elseif #o.scope == 1 and o.scope[1] == 'window' then
- w(' .var=VAR_WIN')
+ if not is_window_local then
+ if o.varname then
+ w(' .var=&' .. o.varname)
+ elseif o.immutable then
+ -- Immutable options can directly point to the default value.
+ w((' .var=&options[%u].def_val.data'):format(i - 1))
+ else
+ -- Option must be immutable or have a variable.
+ assert(false)
+ end
else
- has_var = false
+ w(' .var=NULL')
end
- -- `enable_if = false` should be present iff there is no variable.
- assert((o.enable_if == false) == not has_var)
- w(' .hidden=' .. (o.hidden and 'true' or 'false'))
w(' .immutable=' .. (o.immutable and 'true' or 'false'))
- if #o.scope == 1 and o.scope[1] == 'global' then
- w(' .indir=PV_NONE')
- else
- assert(#o.scope == 1 or #o.scope == 2)
- assert(#o.scope == 1 or o.scope[1] == 'global')
- local min_scope = o.scope[#o.scope]
- local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
- local pv_name = (
- 'OPT_'
- .. min_scope:sub(1, 3):upper()
- .. '('
- .. (min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper())
- .. ')'
- )
- if #o.scope == 2 then
- pv_name = 'OPT_BOTH(' .. pv_name .. ')'
- end
- table.insert(defines, { 'PV_' .. varname:sub(3):upper(), pv_name })
- w(' .indir=' .. pv_name)
- end
if o.cb then
w(' .opt_did_set_cb=' .. o.cb)
end
@@ -211,27 +365,33 @@ local function dump_option(i, o)
end
if o.enable_if then
w('#else')
- w(' .var=NULL')
- w(' .indir=PV_NONE')
+ -- Hidden option directly points to default value.
+ w((' .var=&options[%u].def_val.data'):format(i - 1))
+ -- Option is always immutable on the false branch of `enable_if`.
+ w(' .immutable=true')
w('#endif')
end
if o.defaults then
if o.defaults.condition then
w(get_cond(o.defaults.condition))
end
- w(' .def_val' .. get_defaults(o.defaults.if_true, o.full_name))
+ w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
if o.defaults.condition then
if o.defaults.if_false then
w('#else')
- w(' .def_val' .. get_defaults(o.defaults.if_false, o.full_name))
+ w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
end
w('#endif')
end
+ else
+ w(' .def_val=NIL_OPTVAL')
end
w(' },')
end
+-- Generate options[] array.
w([[
+#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/insexpand.h"
#include "nvim/mapping.h"
@@ -248,8 +408,3 @@ for i, o in ipairs(options.options) do
dump_option(i, o)
end
w('};')
-w('')
-
-for _, v in ipairs(defines) do
- w('#define ' .. v[1] .. ' ' .. v[2])
-end
diff --git a/src/nvim/generators/gen_options_enum.lua b/src/nvim/generators/gen_options_enum.lua
deleted file mode 100644
index d1419137d3..0000000000
--- a/src/nvim/generators/gen_options_enum.lua
+++ /dev/null
@@ -1,129 +0,0 @@
--- Generates option index enum and map of option name to option index.
--- Handles option full name, short name and aliases.
--- Also generates BV_ and WV_ enum constants.
-
-local options_enum_file = arg[1]
-local options_map_file = arg[2]
-local options_enum_fd = assert(io.open(options_enum_file, 'w'))
-local options_map_fd = assert(io.open(options_map_file, 'w'))
-
---- @param s string
-local function enum_w(s)
- options_enum_fd:write(s .. '\n')
-end
-
---- @param s string
-local function map_w(s)
- options_map_fd:write(s .. '\n')
-end
-
-enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
-enum_w('')
-
---- @param s string
---- @return string
-local lowercase_to_titlecase = function(s)
- return s:sub(1, 1):upper() .. s:sub(2)
-end
-
---- @type vim.option_meta[]
-local options = require('options').options
-
--- Generate BV_ enum constants.
-enum_w('/// "indir" values for buffer-local options.')
-enum_w('/// These need to be defined globally, so that the BV_COUNT can be used with')
-enum_w('/// b_p_script_stx[].')
-enum_w('enum {')
-
-local bv_val = 0
-
-for _, o in ipairs(options) do
- assert(#o.scope == 1 or #o.scope == 2)
- assert(#o.scope == 1 or o.scope[1] == 'global')
- local min_scope = o.scope[#o.scope]
- if min_scope == 'buffer' then
- local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
- local bv_name = 'BV_' .. varname:sub(3):upper()
- enum_w((' %s = %u,'):format(bv_name, bv_val))
- bv_val = bv_val + 1
- end
-end
-
-enum_w((' BV_COUNT = %u, ///< must be the last one'):format(bv_val))
-enum_w('};')
-enum_w('')
-
--- Generate WV_ enum constants.
-enum_w('/// "indir" values for window-local options.')
-enum_w('/// These need to be defined globally, so that the WV_COUNT can be used in the')
-enum_w('/// window structure.')
-enum_w('enum {')
-
-local wv_val = 0
-
-for _, o in ipairs(options) do
- assert(#o.scope == 1 or #o.scope == 2)
- assert(#o.scope == 1 or o.scope[1] == 'global')
- local min_scope = o.scope[#o.scope]
- if min_scope == 'window' then
- local varname = o.pv_name or o.varname or ('p_' .. (o.abbreviation or o.full_name))
- local wv_name = 'WV_' .. varname:sub(3):upper()
- enum_w((' %s = %u,'):format(wv_name, wv_val))
- wv_val = wv_val + 1
- end
-end
-
-enum_w((' WV_COUNT = %u, ///< must be the last one'):format(wv_val))
-enum_w('};')
-enum_w('')
-
---- @type { [string]: string }
-local option_index = {}
-
--- Generate option index enum and populate the `option_index` dict.
-enum_w('typedef enum {')
-enum_w(' kOptInvalid = -1,')
-
-for i, o in ipairs(options) do
- local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
- enum_w((' %s = %u,'):format(enum_val_name, i - 1))
-
- option_index[o.full_name] = enum_val_name
-
- if o.abbreviation then
- option_index[o.abbreviation] = enum_val_name
- end
-
- if o.alias then
- o.alias = type(o.alias) == 'string' and { o.alias } or o.alias
-
- for _, v in ipairs(o.alias) do
- option_index[v] = enum_val_name
- end
- end
-end
-
-enum_w(' // Option count, used when iterating through options')
-enum_w('#define kOptIndexCount ' .. tostring(#options))
-enum_w('} OptIndex;')
-enum_w('')
-
-options_enum_fd:close()
-
---- Generate option index map.
-local hashy = require('generators.hashy')
-local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx)
- return ('option_hash_elems[%s].name'):format(idx)
-end)
-
-map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
-
-for _, name in ipairs(neworder) do
- assert(option_index[name] ~= nil)
- map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
-end
-
-map_w('};\n')
-map_w('static ' .. hashfun)
-
-options_map_fd:close()
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 472bc3a850..82abd2888a 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -95,15 +95,15 @@ static FileDescriptor scriptin[NSCRIPT] = { 0 };
#define MINIMAL_SIZE 20 // minimal size for b_str
-static buffheader_T redobuff = { { NULL, { NUL } }, NULL, 0, 0 };
-static buffheader_T old_redobuff = { { NULL, { NUL } }, NULL, 0, 0 };
-static buffheader_T recordbuff = { { NULL, { NUL } }, NULL, 0, 0 };
+static buffheader_T redobuff = { { NULL, 0, { NUL } }, NULL, 0, 0, false };
+static buffheader_T old_redobuff = { { NULL, 0, { NUL } }, NULL, 0, 0, false };
+static buffheader_T recordbuff = { { NULL, 0, { NUL } }, NULL, 0, 0, false };
/// First read ahead buffer. Used for translated commands.
-static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 };
+static buffheader_T readbuf1 = { { NULL, 0, { NUL } }, NULL, 0, 0, false };
/// Second read ahead buffer. Used for redo.
-static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 };
+static buffheader_T readbuf2 = { { NULL, 0, { NUL } }, NULL, 0, 0, false };
/// Buffer used to store typed characters for vim.on_key().
static kvec_withinit_t(char, MAXMAPLEN + 1) on_key_buf = KVI_INITIAL_VALUE(on_key_buf);
@@ -183,15 +183,17 @@ static void free_buff(buffheader_T *buf)
/// K_SPECIAL in the returned string is escaped.
///
/// @param dozero count == zero is not an error
-static char *get_buffcont(buffheader_T *buffer, int dozero)
+/// @param len the length of the returned buffer
+static char *get_buffcont(buffheader_T *buffer, int dozero, size_t *len)
{
size_t count = 0;
char *p = NULL;
+ size_t i = 0;
// compute the total length of the string
for (const buffblock_T *bp = buffer->bh_first.b_next;
bp != NULL; bp = bp->b_next) {
- count += strlen(bp->b_str);
+ count += bp->b_strlen;
}
if (count > 0 || dozero) {
@@ -204,7 +206,13 @@ static char *get_buffcont(buffheader_T *buffer, int dozero)
}
}
*p2 = NUL;
+ i = (size_t)(p2 - p);
}
+
+ if (len != NULL) {
+ *len = i;
+ }
+
return p;
}
@@ -213,12 +221,12 @@ static char *get_buffcont(buffheader_T *buffer, int dozero)
/// K_SPECIAL in the returned string is escaped.
char *get_recorded(void)
{
- char *p = get_buffcont(&recordbuff, true);
+ size_t len;
+ char *p = get_buffcont(&recordbuff, true, &len);
free_buff(&recordbuff);
// Remove the characters that were added the last time, these must be the
// (possibly mapped) characters that stopped the recording.
- size_t len = strlen(p);
if (len >= last_recorded_len) {
len -= last_recorded_len;
p[len] = NUL;
@@ -237,7 +245,7 @@ char *get_recorded(void)
/// K_SPECIAL in the returned string is escaped.
char *get_inserted(void)
{
- return get_buffcont(&redobuff, false);
+ return get_buffcont(&redobuff, false, NULL);
}
/// Add string after the current block of the given buffer
@@ -257,27 +265,31 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle
}
if (buf->bh_first.b_next == NULL) { // first add to list
- buf->bh_space = 0;
buf->bh_curr = &(buf->bh_first);
+ buf->bh_create_newblock = true;
} else if (buf->bh_curr == NULL) { // buffer has already been read
iemsg(_("E222: Add to read buffer"));
return;
} else if (buf->bh_index != 0) {
memmove(buf->bh_first.b_next->b_str,
buf->bh_first.b_next->b_str + buf->bh_index,
- strlen(buf->bh_first.b_next->b_str + buf->bh_index) + 1);
+ (buf->bh_first.b_next->b_strlen - buf->bh_index) + 1);
+ buf->bh_first.b_next->b_strlen -= buf->bh_index;
+ buf->bh_space += buf->bh_index;
}
buf->bh_index = 0;
- if (buf->bh_space >= (size_t)slen) {
- size_t len = strlen(buf->bh_curr->b_str);
- xmemcpyz(buf->bh_curr->b_str + len, s, (size_t)slen);
+ if (!buf->bh_create_newblock && buf->bh_space >= (size_t)slen) {
+ xmemcpyz(buf->bh_curr->b_str + buf->bh_curr->b_strlen, s, (size_t)slen);
+ buf->bh_curr->b_strlen += (size_t)slen;
buf->bh_space -= (size_t)slen;
} else {
size_t len = MAX(MINIMAL_SIZE, (size_t)slen);
buffblock_T *p = xmalloc(offsetof(buffblock_T, b_str) + len + 1);
- buf->bh_space = len - (size_t)slen;
xmemcpyz(p->b_str, s, (size_t)slen);
+ p->b_strlen = (size_t)slen;
+ buf->bh_space = len - (size_t)slen;
+ buf->bh_create_newblock = false;
p->b_next = buf->bh_curr->b_next;
buf->bh_curr->b_next = p;
@@ -292,12 +304,12 @@ static void delete_buff_tail(buffheader_T *buf, int slen)
if (buf->bh_curr == NULL) {
return; // nothing to delete
}
- int len = (int)strlen(buf->bh_curr->b_str);
- if (len < slen) {
+ if (buf->bh_curr->b_strlen < (size_t)slen) {
return;
}
- buf->bh_curr->b_str[len - slen] = NUL;
+ buf->bh_curr->b_str[buf->bh_curr->b_strlen - (size_t)slen] = NUL;
+ buf->bh_curr->b_strlen -= (size_t)slen;
buf->bh_space += (size_t)slen;
}
@@ -305,8 +317,8 @@ static void delete_buff_tail(buffheader_T *buf, int slen)
static void add_num_buff(buffheader_T *buf, int n)
{
char number[32];
- snprintf(number, sizeof(number), "%d", n);
- add_buff(buf, number, -1);
+ int numberlen = snprintf(number, sizeof(number), "%d", n);
+ add_buff(buf, number, numberlen);
}
/// Add byte or special key 'c' to buffer "buf".
@@ -314,17 +326,20 @@ static void add_num_buff(buffheader_T *buf, int n)
static void add_byte_buff(buffheader_T *buf, int c)
{
char temp[4];
+ ptrdiff_t templen;
if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) {
// Translate special key code into three byte sequence.
temp[0] = (char)K_SPECIAL;
temp[1] = (char)K_SECOND(c);
temp[2] = (char)K_THIRD(c);
temp[3] = NUL;
+ templen = 3;
} else {
temp[0] = (char)c;
temp[1] = NUL;
+ templen = 1;
}
- add_buff(buf, temp, -1);
+ add_buff(buf, temp, templen);
}
/// Add character 'c' to buffer "buf".
@@ -385,11 +400,11 @@ static void start_stuff(void)
{
if (readbuf1.bh_first.b_next != NULL) {
readbuf1.bh_curr = &(readbuf1.bh_first);
- readbuf1.bh_space = 0;
+ readbuf1.bh_create_newblock = true; // force a new block to be created (see add_buff())
}
if (readbuf2.bh_first.b_next != NULL) {
readbuf2.bh_curr = &(readbuf2.bh_first);
- readbuf2.bh_space = 0;
+ readbuf2.bh_create_newblock = true; // force a new block to be created (see add_buff())
}
}
@@ -498,12 +513,13 @@ void saveRedobuff(save_redo_T *save_redo)
old_redobuff.bh_first.b_next = NULL;
// Make a copy, so that ":normal ." in a function works.
- char *const s = get_buffcont(&save_redo->sr_redobuff, false);
+ size_t slen;
+ char *const s = get_buffcont(&save_redo->sr_redobuff, false, &slen);
if (s == NULL) {
return;
}
- add_buff(&redobuff, s, -1);
+ add_buff(&redobuff, s, (ptrdiff_t)slen);
xfree(s);
}
@@ -1772,7 +1788,9 @@ int vgetc(void)
// Execute Lua on_key callbacks.
kvi_push(on_key_buf, NUL);
- nlua_execute_on_key(c, on_key_buf.items);
+ if (nlua_execute_on_key(c, on_key_buf.items)) {
+ c = K_IGNORE;
+ }
kvi_destroy(on_key_buf);
kvi_init(on_key_buf);
@@ -1919,9 +1937,9 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
i += utf_char2bytes((int)n, temp + i);
}
assert(i < 10);
- temp[i++] = NUL;
+ temp[i] = NUL;
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(temp);
+ rettv->vval.v_string = xmemdupz(temp, (size_t)i);
if (is_mouse_key((int)n)) {
int row = mouse_row;
@@ -1974,9 +1992,9 @@ void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
i += utf_char2bytes((int)n, temp);
}
assert(i < 7);
- temp[i++] = NUL;
+ temp[i] = NUL;
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(temp);
+ rettv->vval.v_string = xmemdupz(temp, (size_t)i);
}
/// "getcharmod()" function
@@ -2371,7 +2389,7 @@ static int handle_mapping(int *keylenp, const bool *timedout, int *mapdepth)
buf[1] = (char)KS_EXTRA;
buf[2] = KE_IGNORE;
buf[3] = NUL;
- map_str = xstrdup(buf);
+ map_str = xmemdupz(buf, 3);
if (State & MODE_CMDLINE) {
// redraw the command below the error
msg_didout = true;
diff --git a/src/nvim/getchar_defs.h b/src/nvim/getchar_defs.h
index abf812fad3..fdfd0a9942 100644
--- a/src/nvim/getchar_defs.h
+++ b/src/nvim/getchar_defs.h
@@ -9,6 +9,7 @@
/// structure used to store one block of the stuff/redo/recording buffers
typedef struct buffblock {
struct buffblock *b_next; ///< pointer to next buffblock
+ size_t b_strlen; ///< length of b_str, excluding the NUL
char b_str[1]; ///< contents (actually longer)
} buffblock_T;
@@ -18,6 +19,7 @@ typedef struct {
buffblock_T *bh_curr; ///< buffblock for appending
size_t bh_index; ///< index for reading
size_t bh_space; ///< space in bh_curr for appending
+ bool bh_create_newblock; ///< create a new block?
} buffheader_T;
typedef struct {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 45a31ce6b0..472b77ccbe 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -156,7 +156,7 @@ EXTERN bool msg_scrolled_ign INIT( = false);
EXTERN bool msg_did_scroll INIT( = false);
EXTERN char *keep_msg INIT( = NULL); // msg to be shown after redraw
-EXTERN int keep_msg_attr INIT( = 0); // highlight attr for keep_msg
+EXTERN int keep_msg_hl_id INIT( = 0); // highlight id for keep_msg
EXTERN bool need_fileinfo INIT( = false); // do fileinfo() after redraw
EXTERN int msg_scroll INIT( = false); // msg_start() will scroll
EXTERN bool msg_didout INIT( = false); // msg_outstr() was used in line
@@ -713,12 +713,6 @@ EXTERN char *escape_chars INIT( = " \t\\\"|"); // need backslash in cmd line
EXTERN bool keep_help_flag INIT( = false); // doing :ta from help file
-// When a string option is NULL (which only happens in out-of-memory situations), it is set to
-// empty_string_option, to avoid having to check for NULL everywhere.
-//
-// TODO(famiu): Remove this when refcounted strings are used for string options.
-EXTERN char *empty_string_option INIT( = "");
-
EXTERN bool redir_off INIT( = false); // no redirection for a moment
EXTERN FILE *redir_fd INIT( = NULL); // message redirection file
EXTERN int redir_reg INIT( = 0); // message redirection register
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index ac67f42fe0..8fa3092fd6 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -7,10 +7,6 @@
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
-// Includes final NUL. MAX_MCO is no longer used, but at least 4*(MAX_MCO+1)+1=29
-// ensures we can fit all composed chars which did fit before.
-#define MAX_SCHAR_SIZE 32
-
enum {
kZIndexDefaultGrid = 0,
kZIndexFloatDefault = 50,
@@ -80,8 +76,8 @@ struct ScreenGrid {
// whether the compositor should blend the grid with the background grid
bool blending;
- // whether the grid can be focused with mouse clicks.
- bool focusable;
+ // whether the grid interacts with mouse events.
+ bool mouse_enabled;
// z-index: the order in the stack of grids.
int zindex;
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index d39ffcd177..5f0a2e0e4e 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -48,7 +48,7 @@ static Set(cstr_t) urls = SET_INIT;
/// highlight entries private to a namespace
static Map(ColorKey, ColorItem) ns_hls;
-typedef int NSHlAttr[HLF_COUNT + 1];
+typedef int NSHlAttr[HLF_COUNT];
static PMap(int) ns_hl_attr;
void highlight_init(void)
@@ -371,8 +371,8 @@ void update_window_hl(win_T *wp, bool invalid)
bool float_win = wp->w_floating && !wp->w_config.external;
if (float_win && hl_def[HLF_NFLOAT] != 0 && ns_id > 0) {
wp->w_hl_attr_normal = hl_def[HLF_NFLOAT];
- } else if (hl_def[HLF_COUNT] > 0) {
- wp->w_hl_attr_normal = hl_def[HLF_COUNT];
+ } else if (hl_def[HLF_NONE] > 0) {
+ wp->w_hl_attr_normal = hl_def[HLF_NONE];
} else if (float_win) {
wp->w_hl_attr_normal = HL_ATTR(HLF_NFLOAT) > 0
? HL_ATTR(HLF_NFLOAT) : highlight_attr[HLF_NFLOAT];
@@ -433,7 +433,7 @@ void update_ns_hl(int ns_id)
}
int *hl_attrs = **alloc;
- for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
+ for (int hlf = 1; hlf < HLF_COUNT; hlf++) {
int id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
bool optional = (hlf == HLF_INACTIVE || hlf == HLF_NFLOAT);
hl_attrs[hlf] = hl_get_ui_attr(ns_id, hlf, id, optional);
@@ -444,7 +444,7 @@ void update_ns_hl(int ns_id)
//
// haha, tema engine go brrr
int normality = syn_check_group(S_LEN("Normal"));
- hl_attrs[HLF_COUNT] = hl_get_ui_attr(ns_id, -1, normality, true);
+ hl_attrs[HLF_NONE] = hl_get_ui_attr(ns_id, -1, normality, true);
// hl_get_ui_attr might have invalidated the decor provider
p = get_decor_provider(ns_id, true);
@@ -461,7 +461,7 @@ int win_bg_attr(win_T *wp)
}
if (wp == curwin || hl_attr_active[HLF_INACTIVE] == 0) {
- return hl_attr_active[HLF_COUNT];
+ return hl_attr_active[HLF_NONE];
} else {
return hl_attr_active[HLF_INACTIVE];
}
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 2ba6cf5371..1be70100de 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -84,7 +84,7 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_TSNC] = "StatusLineTermNC",
});
-EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context.
+EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups
EXTERN int highlight_user[9]; // User[1-9] attributes
EXTERN int highlight_stlnc[9]; // On top of user
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index e0cce81166..a3c4062714 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -58,7 +58,8 @@ typedef struct {
/// Values for index in highlight_attr[].
/// When making changes, also update hlf_names in highlight.h!
typedef enum {
- HLF_8 = 0, ///< Meta & special keys listed with ":map", text that is
+ HLF_NONE = 0, ///< no UI highlight active
+ HLF_8, ///< Meta & special keys listed with ":map", text that is
///< displayed different from what it is
HLF_EOB, ///< after the last line in the buffer
HLF_TERM, ///< terminal cursor focused
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 1a0ae3ac49..b3c4aca1af 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -144,6 +144,8 @@ static const char e_missing_argument_str[]
static const char *highlight_init_both[] = {
"Cursor guifg=bg guibg=fg",
"CursorLineNr gui=bold cterm=bold",
+ "PmenuMatch gui=bold cterm=bold",
+ "PmenuMatchSel gui=bold cterm=bold",
"PmenuSel gui=reverse cterm=reverse,underline blend=0",
"RedrawDebugNormal gui=reverse cterm=reverse",
"TabLineSel gui=bold cterm=bold",
@@ -170,8 +172,6 @@ static const char *highlight_init_both[] = {
"default link PmenuExtraSel PmenuSel",
"default link PmenuKind Pmenu",
"default link PmenuKindSel PmenuSel",
- "default link PmenuMatch Pmenu",
- "default link PmenuMatchSel PmenuSel",
"default link PmenuSbar Pmenu",
"default link Substitute Search",
"default link StatusLineTerm StatusLine",
@@ -215,6 +215,7 @@ static const char *highlight_init_both[] = {
"default link LspReferenceRead LspReferenceText",
"default link LspReferenceText Visual",
"default link LspReferenceWrite LspReferenceText",
+ "default link LspReferenceTarget LspReferenceText",
"default link LspSignatureActiveParameter Visual",
"default link SnippetTabstop Visual",
@@ -1621,9 +1622,9 @@ static void highlight_list_one(const int id)
if (sgp->sg_link && !got_int) {
syn_list_header(didh, 0, id, true);
didh = true;
- msg_puts_attr("links to", HL_ATTR(HLF_D));
+ msg_puts_hl("links to", HLF_D, false);
msg_putchar(' ');
- msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name, 0);
+ msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name, 0, false);
}
if (!didh) {
@@ -1751,10 +1752,10 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg
didh = true;
if (!got_int) {
if (*name != NUL) {
- msg_puts_attr(name, HL_ATTR(HLF_D));
- msg_puts_attr("=", HL_ATTR(HLF_D));
+ msg_puts_hl(name, HLF_D, false);
+ msg_puts_hl("=", HLF_D, false);
}
- msg_outtrans(ts, 0);
+ msg_outtrans(ts, 0, false);
}
return didh;
}
@@ -1884,7 +1885,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool
if (got_int) {
return true;
}
- msg_outtrans(hl_table[id - 1].sg_name, 0);
+ msg_outtrans(hl_table[id - 1].sg_name, 0, false);
name_col = msg_col;
endcol = 15;
} else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
@@ -1915,7 +1916,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool
if (endcol == Columns - 1 && endcol <= name_col) {
msg_putchar(' ');
}
- msg_puts_attr("xxx", syn_id2attr(id));
+ msg_puts_hl("xxx", id, false);
msg_putchar(' ');
}
@@ -2047,7 +2048,7 @@ static int syn_add_group(const char *name, size_t len)
return 0;
} else if (!ASCII_ISALNUM(c) && c != '_' && c != '.' && c != '@' && c != '-') {
// '.' and '@' are allowed characters for use with treesitter capture names.
- msg_source(HL_ATTR(HLF_W));
+ msg_source(HLF_W);
emsg(_(e_highlight_group_name_invalid_char));
return 0;
}
@@ -2246,8 +2247,11 @@ void highlight_changed(void)
need_highlight_changed = false;
+ // sentinel value. used when no highlight is active
+ highlight_attr[HLF_NONE] = 0;
+
/// Translate builtin highlight groups into attributes for quick lookup.
- for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
+ for (int hlf = 1; hlf < HLF_COUNT; hlf++) {
int id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
if (id == 0) {
abort();
@@ -2275,9 +2279,6 @@ void highlight_changed(void)
}
}
- // sentinel value. used when no highlight namespace is active
- highlight_attr[HLF_COUNT] = 0;
-
// Setup the user highlights
//
// Temporarily utilize 10 more hl entries:
@@ -2361,16 +2362,16 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
static void highlight_list(void)
{
for (int i = 10; --i >= 0;) {
- highlight_list_two(i, HL_ATTR(HLF_D));
+ highlight_list_two(i, HLF_D);
}
for (int i = 40; --i >= 0;) {
highlight_list_two(99, 0);
}
}
-static void highlight_list_two(int cnt, int attr)
+static void highlight_list_two(int cnt, int id)
{
- msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
+ msg_puts_hl(&("N \bI \b! \b"[cnt / 11]), id, false);
msg_clr_eos();
ui_flush();
os_delay(cnt == 99 ? 40 : (uint64_t)cnt * 50, false);
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index b7e3842aad..e487728901 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -769,9 +769,15 @@ int get_number_indent(linenr_T lnum)
return (int)col;
}
+/// Check "briopt" as 'breakindentopt' and update the members of "wp".
/// This is called when 'breakindentopt' is changed and when a window is
/// initialized
-bool briopt_check(win_T *wp)
+///
+/// @param briopt when NULL: use "wp->w_p_briopt"
+/// @param wp when NULL: only check "briopt"
+///
+/// @return FAIL for failure, OK otherwise.
+bool briopt_check(char *briopt, win_T *wp)
{
int bri_shift = 0;
int bri_min = 20;
@@ -779,7 +785,13 @@ bool briopt_check(win_T *wp)
int bri_list = 0;
int bri_vcol = 0;
- char *p = wp->w_p_briopt;
+ char *p = empty_string_option;
+ if (briopt != NULL) {
+ p = briopt;
+ } else if (wp != NULL) {
+ p = wp->w_p_briopt;
+ }
+
while (*p != NUL) {
// Note: Keep this in sync with p_briopt_values
if (strncmp(p, "shift:", 6) == 0
@@ -807,6 +819,10 @@ bool briopt_check(win_T *wp)
}
}
+ if (wp == NULL) {
+ return OK;
+ }
+
wp->w_briopt_shift = bri_shift;
wp->w_briopt_min = bri_min;
wp->w_briopt_sbr = bri_sbr;
@@ -1166,7 +1182,7 @@ int get_expr_indent(void)
sandbox++;
}
textlock++;
- current_sctx = curbuf->b_p_script_ctx[BV_INDE].script_ctx;
+ current_sctx = curbuf->b_p_script_ctx[kBufOptIndentexpr].script_ctx;
// Need to make a copy, the 'indentexpr' option could be changed while
// evaluating it.
diff --git a/src/nvim/input.c b/src/nvim/input.c
index ef400710fe..3d3240c59f 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -55,7 +55,7 @@ int ask_yesno(const char *const str, const bool direct)
int r = ' ';
while (r != 'y' && r != 'n') {
// same highlighting as for wait_return()
- smsg(HL_ATTR(HLF_R), "%s (y/n)?", str);
+ smsg(HLF_R, "%s (y/n)?", str);
if (direct) {
r = get_keystroke(NULL);
} else {
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 84dd55fa78..bd7ee55722 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -164,7 +164,7 @@ struct compl_S {
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
int cp_score; ///< fuzzy match score
- int cp_user_hlattr; ///< highlight attribute to combine with
+ int cp_user_abbr_hlattr; ///< highlight attribute for abbr
int cp_user_kind_hlattr; ///< highlight attribute for kind
};
@@ -473,7 +473,7 @@ bool check_compl_option(bool dict_opt)
ctrl_x_mode = CTRL_X_NORMAL;
edit_submode = NULL;
msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")),
- HL_ATTR(HLF_E));
+ HLF_E);
if (emsg_silent == 0 && !in_assert_fails) {
vim_beep(BO_COMPL);
setcursor();
@@ -798,7 +798,7 @@ static inline void free_cptext(char *const *const cptext)
/// returned in case of error.
static int ins_compl_add(char *const str, int len, char *const fname, char *const *const cptext,
const bool cptext_allocated, typval_T *user_data, const Direction cdir,
- int flags_arg, const bool adup, int user_hlattr, int user_kind_hlattr)
+ int flags_arg, const bool adup, int user_abbr_hlattr, int user_kind_hlattr)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -864,7 +864,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match->cp_fname = NULL;
}
match->cp_flags = flags;
- match->cp_user_hlattr = user_hlattr;
+ match->cp_user_abbr_hlattr = user_abbr_hlattr;
match->cp_user_kind_hlattr = user_kind_hlattr;
if (cptext != NULL) {
@@ -1268,7 +1268,7 @@ static int ins_compl_build_pum(void)
compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
compl_match_array[i].pum_score = comp->cp_score;
- compl_match_array[i].pum_user_hlattr = comp->cp_user_hlattr;
+ compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr;
compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr;
if (comp->cp_text[CPT_MENU] != NULL) {
compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU];
@@ -1564,7 +1564,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
msg_hist_off = true; // reset in msg_trunc()
vim_snprintf(IObuff, IOSIZE,
_("Scanning dictionary: %s"), files[i]);
- msg_trunc(IObuff, true, HL_ATTR(HLF_R));
+ msg_trunc(IObuff, true, HLF_R);
}
if (fp == NULL) {
@@ -2368,13 +2368,13 @@ static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
/// Invoked when the 'completefunc' option is set. The option value can be a
/// name of a function (string), or function(<name>) or funcref(<name>) or a
/// lambda expression.
-const char *did_set_completefunc(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_completefunc(optset_T *args)
{
- if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL) {
+ buf_T *buf = (buf_T *)args->os_buf;
+ if (option_set_callback_func(buf->b_p_cfu, &cfu_cb) == FAIL) {
return e_invarg;
}
-
- set_buflocal_cfu_callback(curbuf);
+ set_buflocal_cfu_callback(buf);
return NULL;
}
@@ -2412,14 +2412,19 @@ void set_buflocal_ofu_callback(buf_T *buf)
/// lambda expression.
const char *did_set_thesaurusfunc(optset_T *args FUNC_ATTR_UNUSED)
{
+ buf_T *buf = (buf_T *)args->os_buf;
int retval;
- if (*curbuf->b_p_tsrfu != NUL) {
+ if (args->os_flags & OPT_LOCAL) {
// buffer-local option set
- retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
+ retval = option_set_callback_func(buf->b_p_tsrfu, &buf->b_tsrfu_cb);
} else {
// global option set
retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
+ // when using :set, free the local callback
+ if (!(args->os_flags & OPT_GLOBAL)) {
+ callback_free(&buf->b_tsrfu_cb);
+ }
}
return retval == FAIL ? e_invarg : NULL;
@@ -2567,9 +2572,9 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
bool empty = false;
int flags = fast ? CP_FAST : 0;
char *(cptext[CPT_COUNT]);
- char *user_hlname = NULL;
+ char *user_abbr_hlname = NULL;
+ int user_abbr_hlattr = -1;
char *user_kind_hlname = NULL;
- int user_hlattr = -1;
int user_kind_hlattr = -1;
typval_T user_data;
@@ -2581,8 +2586,8 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true);
cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true);
- user_hlname = tv_dict_get_string(tv->vval.v_dict, "hl_group", false);
- user_hlattr = get_user_highlight_attr(user_hlname);
+ user_abbr_hlname = tv_dict_get_string(tv->vval.v_dict, "abbr_hlgroup", false);
+ user_abbr_hlattr = get_user_highlight_attr(user_abbr_hlname);
user_kind_hlname = tv_dict_get_string(tv->vval.v_dict, "kind_hlgroup", false);
user_kind_hlattr = get_user_highlight_attr(user_kind_hlname);
@@ -2608,7 +2613,8 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
return FAIL;
}
int status = ins_compl_add((char *)word, -1, NULL, cptext, true,
- &user_data, dir, flags, dup, user_hlattr, user_kind_hlattr);
+ &user_data, dir, flags, dup,
+ user_abbr_hlattr, user_kind_hlattr);
if (status != OK) {
tv_clear(&user_data);
}
@@ -3040,7 +3046,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
: st->ins_buf->b_sfname == NULL
? st->ins_buf->b_fname
: st->ins_buf->b_sfname);
- msg_trunc(IObuff, true, HL_ATTR(HLF_R));
+ msg_trunc(IObuff, true, HLF_R);
}
} else if (*st->e_cpt == NUL) {
status = INS_COMPL_CPT_END;
@@ -3068,7 +3074,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
if (!shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags."));
- msg_trunc(IObuff, true, HL_ATTR(HLF_R));
+ msg_trunc(IObuff, true, HLF_R);
}
}
@@ -4562,7 +4568,7 @@ static void ins_compl_show_statusmsg(void)
if (!p_smd) {
msg_hist_off = true;
msg(edit_submode_extra, (edit_submode_highl < HLF_COUNT
- ? HL_ATTR(edit_submode_highl) : 0));
+ ? (int)edit_submode_highl + 1 : 0));
msg_hist_off = false;
}
} else {
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index 8943e6e8a6..39dfb8eeb9 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -150,7 +150,9 @@ static int count_n_matched_chars(mmfile_t **sp, const size_t n, bool iwhite)
mmfile_t fastforward_buf_to_lnum(mmfile_t s, linenr_T lnum)
{
for (int i = 0; i < lnum - 1; i++) {
- s.ptr = strnchr(s.ptr, (size_t *)&s.size, '\n');
+ size_t n = (size_t)s.size;
+ s.ptr = strnchr(s.ptr, &n, '\n');
+ s.size = (int)n;
if (!s.ptr) {
break;
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index d4940f3add..c4fa8b0fff 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -187,7 +187,7 @@ static void nlua_luv_error_event(void **argv)
msg_ext_set_kind("lua_error");
switch (type) {
case kCallback:
- semsg_multiline("Error executing luv callback:\n%s", error);
+ semsg_multiline("Error executing callback:\n%s", error);
break;
case kThread:
semsg_multiline("Error in luv thread:\n%s", error);
@@ -201,13 +201,13 @@ static void nlua_luv_error_event(void **argv)
xfree(error);
}
-static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags)
+/// Execute callback in "fast" context. Used for luv and some vim.ui_event
+/// callbacks where using the API directly is not safe.
+static int nlua_fast_cfpcall(lua_State *lstate, int nargs, int nresult, int flags)
FUNC_ATTR_NONNULL_ALL
{
int retval;
- // luv callbacks might be executed at any os_breakcheck/line_breakcheck
- // call, so using the API directly here is not safe.
in_fast_callback++;
int top = lua_gettop(lstate);
@@ -366,11 +366,13 @@ static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg
static void nlua_schedule_event(void **argv)
{
LuaRef cb = (LuaRef)(ptrdiff_t)argv[0];
+ uint32_t ns_id = (uint32_t)(ptrdiff_t)argv[1];
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, cb);
nlua_unref_global(lstate, cb);
if (nlua_pcall(lstate, 0, 0)) {
nlua_error(lstate, _("Error executing vim.schedule lua callback: %.*s"));
+ ui_remove_cb(ns_id, true);
}
}
@@ -392,8 +394,9 @@ static int nlua_schedule(lua_State *const lstate)
}
LuaRef cb = nlua_ref_global(lstate, 1);
-
- multiqueue_put(main_loop.events, nlua_schedule_event, (void *)(ptrdiff_t)cb);
+ // Pass along UI event handler to disable on error.
+ multiqueue_put(main_loop.events, nlua_schedule_event, (void *)(ptrdiff_t)cb,
+ (void *)(ptrdiff_t)ui_event_ns_id);
return 0;
}
@@ -425,7 +428,7 @@ static int nlua_wait(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
if (in_fast_callback) {
- return luaL_error(lstate, e_luv_api_disabled, "vim.wait");
+ return luaL_error(lstate, e_fast_api_disabled, "vim.wait");
}
intptr_t timeout = luaL_checkinteger(lstate, 1);
@@ -598,7 +601,7 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_stan
luv_set_cthread(lstate, nlua_luv_thread_cfcpcall);
} else {
luv_set_loop(lstate, &main_loop.uv);
- luv_set_callback(lstate, nlua_luv_cfpcall);
+ luv_set_callback(lstate, nlua_fast_cfpcall);
}
luaopen_luv(lstate);
lua_pushvalue(lstate, -1);
@@ -724,7 +727,7 @@ static int nlua_ui_detach(lua_State *lstate)
return luaL_error(lstate, "invalid ns_id");
}
- ui_remove_cb(ns_id);
+ ui_remove_cb(ns_id, false);
return 0;
}
@@ -951,41 +954,10 @@ static void nlua_common_free_all_mem(lua_State *lstate)
static void nlua_print_event(void **argv)
{
- char *str = argv[0];
- const size_t len = (size_t)(intptr_t)argv[1] - 1; // exclude final NUL
-
- for (size_t i = 0; i < len;) {
- if (got_int) {
- break;
- }
- const size_t start = i;
- while (i < len) {
- switch (str[i]) {
- case NUL:
- str[i] = NL;
- i++;
- continue;
- case NL:
- // TODO(bfredl): use proper multiline msg? Probably should implement
- // print() in lua in terms of nvim_message(), when it is available.
- str[i] = NUL;
- i++;
- break;
- default:
- i++;
- continue;
- }
- break;
- }
- msg(str + start, 0);
- if (msg_silent == 0) {
- msg_didout = true; // Make blank lines work properly
- }
- }
- if (len && str[len - 1] == NUL) { // Last was newline
- msg("", 0);
- }
- xfree(str);
+ HlMessage msg = KV_INITIAL_VALUE;
+ HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
+ kv_push(msg, chunk);
+ msg_multihl(msg, "lua_print", true);
}
/// Print as a Vim message
@@ -1174,7 +1146,7 @@ int nlua_call(lua_State *lstate)
size_t name_len;
const char *name = luaL_checklstring(lstate, 1, &name_len);
if (!nlua_is_deferred_safe() && !viml_func_is_fast(name)) {
- return luaL_error(lstate, e_luv_api_disabled, "Vimscript function");
+ return luaL_error(lstate, e_fast_api_disabled, "Vimscript function");
}
int nargs = lua_gettop(lstate) - 1;
@@ -1231,7 +1203,7 @@ free_vim_args:
static int nlua_rpcrequest(lua_State *lstate)
{
if (!nlua_is_deferred_safe()) {
- return luaL_error(lstate, e_luv_api_disabled, "rpcrequest");
+ return luaL_error(lstate, e_fast_api_disabled, "rpcrequest");
}
return nlua_rpc(lstate, true);
}
@@ -1594,6 +1566,12 @@ bool nlua_ref_is_function(LuaRef ref)
Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, Arena *arena,
Error *err)
{
+ return nlua_call_ref_ctx(false, ref, name, args, mode, arena, err);
+}
+
+Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, LuaRetMode mode,
+ Arena *arena, Error *err)
+{
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, ref);
int nargs = (int)args.size;
@@ -1605,7 +1583,13 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode,
nlua_push_Object(lstate, &args.items[i], 0);
}
- if (nlua_pcall(lstate, nargs, 1)) {
+ if (fast) {
+ if (nlua_fast_cfpcall(lstate, nargs, 1, -1) < 0) {
+ // error is already scheduled, set anyways to convey failure.
+ api_set_error(err, kErrorTypeException, "fast context failure");
+ }
+ return NIL;
+ } else if (nlua_pcall(lstate, nargs, 1)) {
// if err is passed, the caller will deal with the error.
if (err) {
size_t len;
@@ -2063,8 +2047,16 @@ char *nlua_register_table_as_callable(const typval_T *const arg)
return name;
}
-void nlua_execute_on_key(int c, char *typed_buf)
+/// @return true to discard the key
+bool nlua_execute_on_key(int c, char *typed_buf)
{
+ static bool recursive = false;
+
+ if (recursive) {
+ return false;
+ }
+ recursive = true;
+
char buf[MB_MAXBYTES * 3 + 4];
size_t buf_len = special_to_buf(c, mod_mask, false, buf);
vim_unescape_ks(typed_buf);
@@ -2090,9 +2082,15 @@ void nlua_execute_on_key(int c, char *typed_buf)
int save_got_int = got_int;
got_int = false; // avoid interrupts when the key typed is Ctrl-C
- if (nlua_pcall(lstate, 2, 0)) {
- nlua_error(lstate,
- _("Error executing vim.on_key Lua callback: %.*s"));
+ bool discard = false;
+ // Do not use nlua_pcall here to avoid duplicate stack trace information
+ if (lua_pcall(lstate, 2, 1, 0)) {
+ nlua_error(lstate, _("Error executing vim.on_key() callbacks: %.*s"));
+ } else {
+ if (lua_isboolean(lstate, -1)) {
+ discard = lua_toboolean(lstate, -1);
+ }
+ lua_pop(lstate, 1);
}
got_int |= save_got_int;
@@ -2103,6 +2101,9 @@ void nlua_execute_on_key(int c, char *typed_buf)
// [ ]
assert(top == lua_gettop(lstate));
#endif
+
+ recursive = false;
+ return discard;
}
// Sets the editor "script context" during Lua execution. Used by :verbose.
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index ee0eabbebb..e719d99640 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -181,7 +181,9 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
} else {
idx = luaL_checkinteger(lstate, 2);
if (idx < 0 || idx > (intptr_t)s1_len) {
- return luaL_error(lstate, "index out of range");
+ lua_pushnil(lstate);
+ lua_pushnil(lstate);
+ return 2;
}
}
@@ -272,7 +274,8 @@ int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
const char *s1 = luaL_checklstring(lstate, 1, &s1_len);
intptr_t idx = luaL_checkinteger(lstate, 2);
if (idx < 0) {
- return luaL_error(lstate, "index out of range");
+ lua_pushnil(lstate);
+ return 1;
}
bool use_utf16 = false;
if (lua_gettop(lstate) >= 3) {
@@ -281,7 +284,8 @@ int nlua_str_byteindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
ssize_t byteidx = mb_utf_index_to_bytes(s1, s1_len, (size_t)idx, use_utf16);
if (byteidx == -1) {
- return luaL_error(lstate, "index out of range");
+ lua_pushnil(lstate);
+ return 1;
}
lua_pushinteger(lstate, (lua_Integer)byteidx);
@@ -695,10 +699,10 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
lua_setfield(lstate, -2, "stricmp");
// str_utfindex
lua_pushcfunction(lstate, &nlua_str_utfindex);
- lua_setfield(lstate, -2, "str_utfindex");
+ lua_setfield(lstate, -2, "_str_utfindex");
// str_byteindex
lua_pushcfunction(lstate, &nlua_str_byteindex);
- lua_setfield(lstate, -2, "str_byteindex");
+ lua_setfield(lstate, -2, "_str_byteindex");
// str_utf_pos
lua_pushcfunction(lstate, &nlua_str_utf_pos);
lua_setfield(lstate, -2, "str_utf_pos");
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index ab97704dfe..c80e7b7672 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -271,12 +271,16 @@ int tslua_inspect_lang(lua_State *L)
// not used by the API
continue;
}
- lua_createtable(L, 2, 0); // [retval, symbols, elem]
- lua_pushstring(L, ts_language_symbol_name(lang, (TSSymbol)i));
- lua_rawseti(L, -2, 1);
- lua_pushboolean(L, t == TSSymbolTypeRegular);
- lua_rawseti(L, -2, 2); // [retval, symbols, elem]
- lua_rawseti(L, -2, (int)i); // [retval, symbols]
+ const char *name = ts_language_symbol_name(lang, (TSSymbol)i);
+ bool named = t != TSSymbolTypeAnonymous;
+ lua_pushboolean(L, named); // [retval, symbols, is_named]
+ if (!named) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "\"%s\"", name);
+ lua_setfield(L, -2, buf); // [retval, symbols]
+ } else {
+ lua_setfield(L, -2, name); // [retval, symbols]
+ }
}
lua_setfield(L, -2, "symbols"); // [retval]
@@ -828,6 +832,7 @@ static struct luaL_Reg node_meta[] = {
{ "parent", node_parent },
{ "__has_ancestor", __has_ancestor },
{ "child_containing_descendant", node_child_containing_descendant },
+ { "child_with_descendant", node_child_with_descendant },
{ "iter_children", node_iter_children },
{ "next_sibling", node_next_sibling },
{ "prev_sibling", node_prev_sibling },
@@ -1146,7 +1151,7 @@ static int __has_ancestor(lua_State *L)
int const pred_len = (int)lua_objlen(L, 2);
TSNode node = ts_tree_root_node(descendant.tree);
- while (!ts_node_is_null(node)) {
+ while (node.id != descendant.id && !ts_node_is_null(node)) {
char const *node_type = ts_node_type(node);
size_t node_type_len = strlen(node_type);
@@ -1163,7 +1168,7 @@ static int __has_ancestor(lua_State *L)
lua_pop(L, 1);
}
- node = ts_node_child_containing_descendant(node, descendant);
+ node = ts_node_child_with_descendant(node, descendant);
}
lua_pushboolean(L, false);
@@ -1179,6 +1184,15 @@ static int node_child_containing_descendant(lua_State *L)
return 1;
}
+static int node_child_with_descendant(lua_State *L)
+{
+ TSNode node = node_check(L, 1);
+ TSNode descendant = node_check(L, 2);
+ TSNode child = ts_node_child_with_descendant(node, descendant);
+ push_node(L, child, 1);
+ return 1;
+}
+
static int node_next_sibling(lua_State *L)
{
TSNode node = node_check(L, 1);
@@ -1514,10 +1528,25 @@ static void query_err_string(const char *src, int error_offset, TSQueryError err
|| error_type == TSQueryErrorField
|| error_type == TSQueryErrorCapture) {
const char *suffix = src + error_offset;
+ bool is_anonymous = error_type == TSQueryErrorNodeType && suffix[-1] == '"';
int suffix_len = 0;
char c = suffix[suffix_len];
- while (isalnum(c) || c == '_' || c == '-' || c == '.') {
- c = suffix[++suffix_len];
+ if (is_anonymous) {
+ int backslashes = 0;
+ // Stop when we hit an unescaped double quote
+ while (c != '"' || backslashes % 2 != 0) {
+ if (c == '\\') {
+ backslashes += 1;
+ } else {
+ backslashes = 0;
+ }
+ c = suffix[++suffix_len];
+ }
+ } else {
+ // Stop when we hit the end of the identifier
+ while (isalnum(c) || c == '_' || c == '-' || c == '.') {
+ c = suffix[++suffix_len];
+ }
}
snprintf(err, errlen, "\"%.*s\":\n", suffix_len, suffix);
offset = strlen(err);
diff --git a/src/nvim/main.c b/src/nvim/main.c
index dc102f6f6d..695bd4c95a 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -678,12 +678,14 @@ void os_exit(int r)
} else {
ui_flush();
ui_call_stop();
- ml_close_all(true); // remove all memfiles
}
if (!event_teardown() && r == 0) {
r = 1; // Exit with error if main_loop did not teardown gracefully.
}
+ if (!ui_client_channel_id) {
+ ml_close_all(true); // remove all memfiles
+ }
if (used_stdin) {
stream_set_blocking(STDIN_FILENO, true); // normalize stream (#2598)
}
@@ -775,7 +777,11 @@ void getout(int exitval)
}
}
- if (p_shada && *p_shada != NUL) {
+ if (
+#ifdef EXITFREE
+ !entered_free_all_mem &&
+#endif
+ p_shada && *p_shada != NUL) {
// Write out the registers, history, marks etc, to the ShaDa file
shada_write_file(NULL, false);
}
@@ -1448,7 +1454,7 @@ scripterror:
p = r;
}
-#ifdef USE_FNAME_CASE
+#ifdef CASE_INSENSITIVE_FILENAME
// Make the case of the file name match the actual file.
path_fix_case(p);
#endif
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index e055ebc2fa..1a6b2c3581 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -239,9 +239,9 @@ static void showmap(mapblock_T *mp, bool local)
} while (len < 12);
if (mp->m_noremap == REMAP_NONE) {
- msg_puts_attr("*", HL_ATTR(HLF_8));
+ msg_puts_hl("*", HLF_8, false);
} else if (mp->m_noremap == REMAP_SCRIPT) {
- msg_puts_attr("&", HL_ATTR(HLF_8));
+ msg_puts_hl("&", HLF_8, false);
} else {
msg_putchar(' ');
}
@@ -256,10 +256,10 @@ static void showmap(mapblock_T *mp, bool local)
// the rhs, and not M-x etc, true gets both -- webb
if (mp->m_luaref != LUA_NOREF) {
char *str = nlua_funcref_str(mp->m_luaref, NULL);
- msg_puts_attr(str, HL_ATTR(HLF_8));
+ msg_puts_hl(str, HLF_8, false);
xfree(str);
} else if (mp->m_str[0] == NUL) {
- msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
+ msg_puts_hl("<Nop>", HLF_8, false);
} else {
msg_outtrans_special(mp->m_str, false, 0);
}
@@ -568,6 +568,12 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
mapblock_T *mp_result[2] = { NULL, NULL };
+ bool unmap_lhs_only = false;
+ if (maptype == MAPTYPE_UNMAP_LHS) {
+ unmap_lhs_only = true;
+ maptype = MAPTYPE_UNMAP;
+ }
+
// For ":noremap" don't remap, otherwise do remap.
int noremap = args->script ? REMAP_SCRIPT
: maptype == MAPTYPE_NOREMAP ? REMAP_NONE : REMAP_YES;
@@ -720,8 +726,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
// 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 (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
- && !did_it && !got_int; round++) {
+ const int num_rounds = maptype == MAPTYPE_UNMAP && !unmap_lhs_only ? 2 : 1;
+ for (int round = 0; round < num_rounds && !did_it && !got_int; round++) {
int hash_start, hash_end;
if ((round == 0 && has_lhs) || is_abbrev) {
// just use one hash
@@ -935,9 +941,11 @@ theend:
/// for :cabbr mode is MODE_CMDLINE
/// ```
///
-/// @param maptype MAPTYPE_MAP for |:map|
-/// MAPTYPE_UNMAP for |:unmap|
-/// MAPTYPE_NOREMAP for |:noremap|.
+/// @param maptype MAPTYPE_MAP for |:map| or |:abbr|
+/// MAPTYPE_UNMAP for |:unmap| or |:unabbr|
+/// MAPTYPE_NOREMAP for |:noremap| or |:noreabbr|
+/// MAPTYPE_UNMAP_LHS is like MAPTYPE_UNMAP, but doesn't try to match
+/// with {rhs} if there is no match with {lhs}.
/// @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.
@@ -2348,7 +2356,7 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
MapArguments unmap_args = MAP_ARGUMENTS_INIT;
set_maparg_lhs_rhs(lhs, strlen(lhs), "", 0, LUA_NOREF, p_cpo, &unmap_args);
unmap_args.buffer = buffer;
- buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, is_abbr, curbuf);
+ buf_do_map(MAPTYPE_UNMAP_LHS, &unmap_args, mode, is_abbr, curbuf);
xfree(unmap_args.rhs);
xfree(unmap_args.orig_rhs);
@@ -2649,7 +2657,7 @@ void ex_map(exarg_T *eap)
// If we are in a secure mode we print the mappings for security reasons.
if (secure) {
secure = 2;
- msg_outtrans(eap->cmd, 0);
+ msg_outtrans(eap->cmd, 0, false);
msg_putchar('\n');
}
do_exmap(eap, false);
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index b82117ea86..fc120b683c 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -19,9 +19,10 @@
/// Used for the first argument of do_map()
enum {
- MAPTYPE_MAP = 0,
- MAPTYPE_UNMAP = 1,
- MAPTYPE_NOREMAP = 2,
+ MAPTYPE_MAP = 0,
+ MAPTYPE_UNMAP = 1,
+ MAPTYPE_NOREMAP = 2,
+ MAPTYPE_UNMAP_LHS = 3,
};
/// Adjust chars in a language according to 'langmap' option.
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index a09ade2b03..3dbcbbd47b 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -957,9 +957,9 @@ static void show_one_mark(int c, char *arg, pos_T *p, char *name_arg, int curren
msg_putchar('\n');
if (!got_int) {
snprintf(IObuff, IOSIZE, " %c %6" PRIdLINENR " %4d ", c, p->lnum, p->col);
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
if (name != NULL) {
- msg_outtrans(name, current ? HL_ATTR(HLF_D) : 0);
+ msg_outtrans(name, current ? HLF_D : 0, false);
}
}
}
@@ -1082,9 +1082,8 @@ void ex_jumps(exarg_T *eap)
i == curwin->w_jumplistidx ? '>' : ' ',
i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx : curwin->w_jumplistidx - i,
curwin->w_jumplist[i].fmark.mark.lnum, curwin->w_jumplist[i].fmark.mark.col);
- msg_outtrans(IObuff, 0);
- msg_outtrans(name,
- curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum ? HL_ATTR(HLF_D) : 0);
+ msg_outtrans(IObuff, 0, false);
+ msg_outtrans(name, curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum ? HLF_D : 0, false);
xfree(name);
os_breakcheck();
}
@@ -1119,9 +1118,9 @@ void ex_changes(exarg_T *eap)
curwin->w_changelistidx ? i - curwin->w_changelistidx : curwin->w_changelistidx - i,
curbuf->b_changelist[i].mark.lnum,
curbuf->b_changelist[i].mark.col);
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
char *name = mark_line(&curbuf->b_changelist[i].mark, 17);
- msg_outtrans(name, HL_ATTR(HLF_D));
+ msg_outtrans(name, HLF_D, false);
xfree(name);
os_breakcheck();
}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 01e720283e..65f718f925 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -454,9 +454,6 @@ static bool prop_is_emojilike(const utf8proc_property_t *prop)
/// Is only correct for characters >= 0x80.
/// When p_ambw is "double", return 2 for a character with East Asian Width
/// class 'A'(mbiguous).
-///
-/// @note Tables `doublewidth` and `ambiguous` are generated by
-/// gen_unicode_tables.lua, which must be manually invoked as needed.
int utf_char2cells(int c)
{
if (c < 0x80) {
@@ -842,6 +839,13 @@ bool utf_composinglike(const char *p1, const char *p2, GraphemeState *state)
return arabic_combine(first, second);
}
+/// same as utf_composinglike but operating on UCS-4 values
+bool utf_iscomposing(int c1, int c2, GraphemeState *state)
+{
+ return (!utf8proc_grapheme_break_stateful(c1, c2, state)
+ || arabic_combine(c1, c2));
+}
+
/// Get the screen char at the beginning of a string
///
/// Caller is expected to check for things like unprintable chars etc
@@ -1855,8 +1859,7 @@ StrCharInfo utfc_next_impl(StrCharInfo cur)
while (true) {
uint8_t const next_len = utf8len_tab[*next];
int32_t const next_code = utf_ptr2CharInfo_impl(next, (uintptr_t)next_len);
- if (utf8proc_grapheme_break_stateful(prev_code, next_code, &state)
- && !arabic_combine(prev_code, next_code)) {
+ if (!utf_iscomposing(prev_code, next_code, &state)) {
return (StrCharInfo){
.ptr = (char *)next,
.chr = (CharInfo){ .value = next_code, .len = (next_code < 0 ? 1 : next_len) },
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 92e2ef2b55..bfe90bb680 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -775,7 +775,6 @@ void ml_recover(bool checkext)
recoverymode = true;
int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
- int attr = HL_ATTR(HLF_E);
// If the file name ends in ".s[a-w][a-z]" we assume this is the swapfile.
// Otherwise a search is done to find the swapfile(s).
@@ -853,40 +852,39 @@ void ml_recover(bool checkext)
// be set to the real value below.
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
+ int hl_id = HLF_E;
// try to read block 0
if ((hp = mf_get(mfp, 0, 1)) == NULL) {
msg_start();
- msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
- msg_outtrans(mfp->mf_fname, attr | MSG_HIST);
- msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
- attr | MSG_HIST);
+ msg_puts_hl(_("Unable to read block 0 from "), hl_id, true);
+ msg_outtrans(mfp->mf_fname, hl_id, true);
+ msg_puts_hl(_("\nMaybe no changes were made or Nvim did not update the swap file."), hl_id,
+ true);
msg_end();
goto theend;
}
ZeroBlock *b0p = hp->bh_data;
if (strncmp(b0p->b0_version, "VIM 3.0", 7) == 0) {
msg_start();
- msg_outtrans(mfp->mf_fname, MSG_HIST);
- msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
- MSG_HIST);
- msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
+ msg_outtrans(mfp->mf_fname, 0, true);
+ msg_puts_hl(_(" cannot be used with this version of Nvim.\n"), 0, true);
+ msg_puts_hl(_("Use Vim version 3.0.\n"), 0, true);
msg_end();
goto theend;
}
if (ml_check_b0_id(b0p) == FAIL) {
- semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
+ semsg(_("E307: %s does not look like a Nvim swap file"), mfp->mf_fname);
goto theend;
}
if (b0_magic_wrong(b0p)) {
msg_start();
- msg_outtrans(mfp->mf_fname, attr | MSG_HIST);
- msg_puts_attr(_(" cannot be used on this computer.\n"),
- attr | MSG_HIST);
- msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
+ msg_outtrans(mfp->mf_fname, hl_id, true);
+ msg_puts_hl(_(" cannot be used on this computer.\n"), hl_id, true);
+ msg_puts_hl(_("The file was created on "), hl_id, true);
// avoid going past the end of a corrupted hostname
b0p->b0_fname[0] = NUL;
- msg_puts_attr(b0p->b0_hname, attr | MSG_HIST);
- msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
+ msg_puts_hl(b0p->b0_hname, hl_id, true);
+ msg_puts_hl(_(",\nor the file has been damaged."), hl_id, true);
msg_end();
goto theend;
}
@@ -899,9 +897,8 @@ void ml_recover(bool checkext)
mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
if (mfp->mf_page_size < previous_page_size) {
msg_start();
- msg_outtrans(mfp->mf_fname, attr | MSG_HIST);
- msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
- attr | MSG_HIST);
+ msg_outtrans(mfp->mf_fname, hl_id, true);
+ msg_puts_hl(_(" has been damaged (page size is smaller than minimum value).\n"), hl_id, true);
msg_end();
goto theend;
}
@@ -1521,7 +1518,7 @@ static time_t swapfile_info(char *fname)
// print name of owner of the file
if (os_get_uname((uv_uid_t)file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) {
msg_puts(_(" owned by: "));
- msg_outtrans(uname, 0);
+ msg_outtrans(uname, 0, false);
msg_puts(_(" dated: "));
} else {
msg_puts(_(" dated: "));
@@ -1541,7 +1538,7 @@ static time_t swapfile_info(char *fname)
if (strncmp(b0.b0_version, "VIM 3.0", 7) == 0) {
msg_puts(_(" [from Vim version 3.0]"));
} else if (ml_check_b0_id(&b0) == FAIL) {
- msg_puts(_(" [does not look like a Vim swap file]"));
+ msg_puts(_(" [does not look like a Nvim swap file]"));
} else if (!ml_check_b0_strings(&b0)) {
msg_puts(_(" [garbled strings (not nul terminated)]"));
} else {
@@ -1549,7 +1546,7 @@ static time_t swapfile_info(char *fname)
if (b0.b0_fname[0] == NUL) {
msg_puts(_("[No Name]"));
} else {
- msg_outtrans(b0.b0_fname, 0);
+ msg_outtrans(b0.b0_fname, 0, false);
}
msg_puts(_("\n modified: "));
@@ -1557,7 +1554,7 @@ static time_t swapfile_info(char *fname)
if (*(b0.b0_uname) != NUL) {
msg_puts(_("\n user name: "));
- msg_outtrans(b0.b0_uname, 0);
+ msg_outtrans(b0.b0_uname, 0, false);
}
if (*(b0.b0_hname) != NUL) {
@@ -1566,7 +1563,7 @@ static time_t swapfile_info(char *fname)
} else {
msg_puts(_("\n host name: "));
}
- msg_outtrans(b0.b0_hname, 0);
+ msg_outtrans(b0.b0_hname, 0, false);
}
if (char_to_long(b0.b0_pid) != 0) {
@@ -3259,7 +3256,7 @@ static void attention_message(buf_T *buf, char *fname)
msg_puts("\"\n");
const time_t swap_mtime = swapfile_info(fname);
msg_puts(_("While opening file \""));
- msg_outtrans(buf->b_fname, 0);
+ msg_outtrans(buf->b_fname, 0, false);
msg_puts("\"\n");
FileInfo file_info;
if (!os_fileinfo(buf->b_fname, &file_info)) {
@@ -3281,10 +3278,10 @@ static void attention_message(buf_T *buf, char *fname)
" Quit, or continue with caution.\n"));
msg_puts(_("(2) An edit session for this file crashed.\n"));
msg_puts(_(" If this is the case, use \":recover\" or \"nvim -r "));
- msg_outtrans(buf->b_fname, 0);
+ msg_outtrans(buf->b_fname, 0, false);
msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
msg_puts(_(" If you did this already, delete the swap file \""));
- msg_outtrans(fname, 0);
+ msg_outtrans(fname, 0, false);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
no_wait_return--;
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index c33d3cc712..4d3058ee44 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -808,7 +808,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
msg_puts(" ");
}
// Same highlighting as for directories!?
- msg_outtrans(menu->name, HL_ATTR(HLF_D));
+ msg_outtrans(menu->name, HLF_D, false);
}
if (menu != NULL && menu->children == NULL) {
@@ -841,7 +841,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
}
msg_puts(" ");
if (*menu->strings[bit] == NUL) {
- msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
+ msg_puts_hl("<Nop>", HLF_8, false);
} else {
msg_outtrans_special(menu->strings[bit], false, 0);
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 79e6bc8be7..47f33c8967 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -36,6 +36,7 @@
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
@@ -73,7 +74,7 @@ struct msgchunk_S {
msgchunk_T *sb_prev;
char sb_eol; // true when line ends after this text
int sb_msg_col; // column in which text starts
- int sb_attr; // text attributes
+ int sb_hl_id; // text highlight id
char sb_text[]; // text to be displayed
};
@@ -118,7 +119,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
// msg_scrolled How many lines the screen has been scrolled (because of
// messages). Used in update_screen() to scroll the screen
// back. Incremented each time the screen scrolls a line.
-// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_attr()
+// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_hl_id()
// writes something without scrolling should not make
// need_wait_return to be set. This is a hack to make ":ts"
// work without an extra prompt.
@@ -138,6 +139,7 @@ static const char *msg_ext_kind = NULL;
static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
static sattr_T msg_ext_last_attr = -1;
+static int msg_ext_last_hl_id;
static size_t msg_ext_cur_len = 0;
static bool msg_ext_overwrite = false; ///< will overwrite last message
@@ -199,7 +201,7 @@ void msg_grid_validate(void)
ui_call_grid_resize(msg_grid.handle, msg_grid.cols, msg_grid.rows);
msg_scrolled_at_flush = msg_scrolled;
- msg_grid.focusable = false;
+ msg_grid.mouse_enabled = false;
msg_grid_adj.target = &msg_grid;
} else if (!should_alloc && msg_grid.chars) {
ui_comp_remove_grid(&msg_grid);
@@ -231,7 +233,7 @@ void msg_grid_validate(void)
int verb_msg(const char *s)
{
verbose_enter();
- int n = msg_attr_keep(s, 0, false, false);
+ int n = msg_hl_keep(s, 0, false, false);
verbose_leave();
return n;
@@ -241,45 +243,43 @@ int verb_msg(const char *s)
/// When terminal not initialized (yet) printf("%s", ..) is used.
///
/// @return true if wait_return() not called
-bool msg(const char *s, const int attr)
+bool msg(const char *s, const int hl_id)
FUNC_ATTR_NONNULL_ARG(1)
{
- return msg_attr_keep(s, attr, false, false);
+ return msg_hl_keep(s, hl_id, false, false);
}
-/// Similar to msg_outtrans, but support newlines and tabs.
-void msg_multiline(const char *s, int attr, bool check_int, bool *need_clear)
+/// Similar to msg_outtrans_len, but support newlines and tabs.
+void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_clear)
FUNC_ATTR_NONNULL_ALL
{
- const char *next_spec = s;
-
- while (next_spec != NULL) {
+ const char *s = str.data;
+ const char *chunk = s;
+ while ((size_t)(s - str.data) < str.size) {
if (check_int && got_int) {
return;
}
- next_spec = strpbrk(s, "\t\n\r");
-
- if (next_spec != NULL) {
- // Printing all char that are before the char found by strpbrk
- msg_outtrans_len(s, (int)(next_spec - s), attr);
+ if (*s == '\n' || *s == TAB || *s == '\r') {
+ // Print all chars before the delimiter
+ msg_outtrans_len(chunk, (int)(s - chunk), hl_id, hist);
- if (*next_spec != TAB && *need_clear) {
+ if (*s != TAB && *need_clear) {
msg_clr_eos();
*need_clear = false;
}
- msg_putchar_attr((uint8_t)(*next_spec), attr);
- s = next_spec + 1;
+ msg_putchar_hl((uint8_t)(*s), hl_id);
+ chunk = s + 1;
}
+ s++;
}
- // Print the rest of the message. We know there is no special
- // character because strpbrk returned NULL
- if (*s != NUL) {
- msg_outtrans(s, attr);
+ // Print the rest of the message
+ if (*chunk != NUL) {
+ msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist);
}
}
-void msg_multiattr(HlMessage hl_msg, const char *kind, bool history)
+void msg_multihl(HlMessage hl_msg, const char *kind, bool history)
{
no_wait_return++;
msg_start();
@@ -288,17 +288,17 @@ void msg_multiattr(HlMessage hl_msg, const char *kind, bool history)
msg_ext_set_kind(kind);
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
HlMessageChunk chunk = kv_A(hl_msg, i);
- msg_multiline(chunk.text.data, chunk.attr, true, &need_clear);
+ msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
}
if (history && kv_size(hl_msg)) {
- add_msg_hist_multiattr(NULL, 0, 0, true, hl_msg);
+ add_msg_hist_multihl(NULL, 0, 0, true, hl_msg);
}
no_wait_return--;
msg_end();
}
/// @param keep set keep_msg if it doesn't scroll
-bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
+bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
FUNC_ATTR_NONNULL_ALL
{
static int entered = 0;
@@ -316,7 +316,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
return true;
}
- if (attr == 0) {
+ if (hl_id == 0) {
set_vim_var_string(VV_STATUSMSG, s, -1);
}
@@ -335,7 +335,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
&& last_msg_hist != NULL
&& last_msg_hist->msg != NULL
&& strcmp(s, last_msg_hist->msg) != 0)) {
- add_msg_hist(s, -1, attr, multiline);
+ add_msg_hist(s, -1, hl_id, multiline);
}
// Truncate the message if needed.
@@ -347,9 +347,9 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
bool need_clear = true;
if (multiline) {
- msg_multiline(s, attr, false, &need_clear);
+ msg_multiline(cstr_as_string(s), hl_id, false, false, &need_clear);
} else {
- msg_outtrans(s, attr);
+ msg_outtrans(s, hl_id, false);
}
if (need_clear) {
msg_clr_eos();
@@ -484,7 +484,7 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen)
}
}
-/// Shows a printf-style message with attributes.
+/// Shows a printf-style message with highlight id.
///
/// Note: Caller must check the resulting string is shorter than IOSIZE!!!
///
@@ -492,7 +492,7 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen)
/// @see swmsg
///
/// @param s printf-style format message
-int smsg(int attr, const char *s, ...)
+int smsg(int hl_id, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
va_list arglist;
@@ -500,10 +500,10 @@ int smsg(int attr, const char *s, ...)
va_start(arglist, s);
vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
- return msg(IObuff, attr);
+ return msg(IObuff, hl_id);
}
-int smsg_attr_keep(int attr, const char *s, ...)
+int smsg_hl_keep(int hl_id, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
va_list arglist;
@@ -511,7 +511,7 @@ int smsg_attr_keep(int attr, const char *s, ...)
va_start(arglist, s);
vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
- return msg_attr_keep(IObuff, attr, true, false);
+ return msg_hl_keep(IObuff, hl_id, true, false);
}
// Remember the last sourcing name/lnum used in an error message, so that it
@@ -588,7 +588,7 @@ static char *get_emsg_lnum(void)
/// Display name and line number for the source of an error.
/// Remember the file name and line number, so that for the next error the info
/// is only displayed if it changed.
-void msg_source(int attr)
+void msg_source(int hl_id)
{
static bool recursive = false;
@@ -602,12 +602,12 @@ void msg_source(int attr)
char *p = get_emsg_source();
if (p != NULL) {
msg_scroll = true; // this will take more than one line
- msg(p, attr);
+ msg(p, hl_id);
xfree(p);
}
p = get_emsg_lnum();
if (p != NULL) {
- msg(p, HL_ATTR(HLF_N));
+ msg(p, HLF_N);
xfree(p);
last_sourcing_lnum = SOURCING_LNUM; // only once for each line
}
@@ -736,7 +736,7 @@ bool emsg_multiline(const char *s, bool multiline)
}
emsg_on_display = true; // remember there is an error message
- int attr = HL_ATTR(HLF_E); // set highlight mode for error messages
+ int hl_id = HLF_E; // set highlight mode for error messages
if (msg_scrolled != 0) {
need_wait_return = true; // needed in case emsg() is called after
} // wait_return() has reset need_wait_return
@@ -748,11 +748,11 @@ bool emsg_multiline(const char *s, bool multiline)
// Display name and line number for the source of the error.
msg_scroll = true;
- msg_source(attr);
+ msg_source(hl_id);
// Display the error message itself.
msg_nowait = false; // Wait for this msg.
- return msg_attr_keep(s, attr, false, multiline);
+ return msg_hl_keep(s, hl_id, false, multiline);
}
/// emsg() - display an error message
@@ -907,15 +907,15 @@ void msg_schedule_semsg_multiline(const char *const fmt, ...)
/// Careful: The string may be changed by msg_may_trunc()!
///
/// @return a pointer to the printed message, if wait_return() not called.
-char *msg_trunc(char *s, bool force, int attr)
+char *msg_trunc(char *s, bool force, int hl_id)
{
// Add message to history before truncating.
- add_msg_hist(s, -1, attr, false);
+ add_msg_hist(s, -1, hl_id, false);
char *ts = msg_may_trunc(force, s);
msg_hist_off = true;
- bool n = msg(ts, attr);
+ bool n = msg(ts, hl_id);
msg_hist_off = false;
if (n) {
@@ -965,21 +965,21 @@ void hl_msg_free(HlMessage hl_msg)
}
/// @param[in] len Length of s or -1.
-static void add_msg_hist(const char *s, int len, int attr, bool multiline)
+static void add_msg_hist(const char *s, int len, int hl_id, bool multiline)
{
- add_msg_hist_multiattr(s, len, attr, multiline, (HlMessage)KV_INITIAL_VALUE);
+ add_msg_hist_multihl(s, len, hl_id, multiline, (HlMessage)KV_INITIAL_VALUE);
}
-static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multiline,
- HlMessage multiattr)
+static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multiline,
+ HlMessage multihl)
{
if (msg_hist_off || msg_silent != 0) {
- hl_msg_free(multiattr);
+ hl_msg_free(multihl);
return;
}
// Don't let the message history get too big
- while (msg_hist_len > MAX_MSG_HIST_LEN) {
+ while (msg_hist_len > p_mhi) {
delete_first_msg();
}
@@ -1002,9 +1002,9 @@ static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multil
p->msg = NULL;
}
p->next = NULL;
- p->attr = attr;
+ p->hl_id = hl_id;
p->multiline = multiline;
- p->multiattr = multiattr;
+ p->multihl = multihl;
p->kind = msg_ext_kind;
if (last_msg_hist != NULL) {
last_msg_hist->next = p;
@@ -1031,7 +1031,7 @@ int delete_first_msg(void)
last_msg_hist = NULL;
}
xfree(p->msg);
- hl_msg_free(p->multiattr);
+ hl_msg_free(p->multihl);
xfree(p);
msg_hist_len--;
return OK;
@@ -1077,22 +1077,24 @@ void ex_messages(exarg_T *eap)
}
Array entries = ARRAY_DICT_INIT;
for (; p != NULL; p = p->next) {
- if (kv_size(p->multiattr) || (p->msg && p->msg[0])) {
+ if (kv_size(p->multihl) || (p->msg && p->msg[0])) {
Array entry = ARRAY_DICT_INIT;
ADD(entry, CSTR_TO_OBJ(p->kind));
Array content = ARRAY_DICT_INIT;
- if (kv_size(p->multiattr)) {
- for (uint32_t i = 0; i < kv_size(p->multiattr); i++) {
- HlMessageChunk chunk = kv_A(p->multiattr, i);
+ if (kv_size(p->multihl)) {
+ for (uint32_t i = 0; i < kv_size(p->multihl); i++) {
+ HlMessageChunk chunk = kv_A(p->multihl, i);
Array content_entry = ARRAY_DICT_INIT;
- ADD(content_entry, INTEGER_OBJ(chunk.attr));
+ ADD(content_entry, INTEGER_OBJ(chunk.hl_id ? syn_id2attr(chunk.hl_id) : 0));
ADD(content_entry, STRING_OBJ(copy_string(chunk.text, NULL)));
+ ADD(content_entry, INTEGER_OBJ(chunk.hl_id));
ADD(content, ARRAY_OBJ(content_entry));
}
} else if (p->msg && p->msg[0]) {
Array content_entry = ARRAY_DICT_INIT;
- ADD(content_entry, INTEGER_OBJ(p->attr));
+ ADD(content_entry, INTEGER_OBJ(p->hl_id ? syn_id2attr(p->hl_id) : 0));
ADD(content_entry, CSTR_TO_OBJ(p->msg));
+ ADD(content_entry, INTEGER_OBJ(p->hl_id));
ADD(content, ARRAY_OBJ(content_entry));
}
ADD(entry, ARRAY_OBJ(content));
@@ -1106,10 +1108,10 @@ void ex_messages(exarg_T *eap)
} else {
msg_hist_off = true;
for (; p != NULL && !got_int; p = p->next) {
- if (kv_size(p->multiattr)) {
- msg_multiattr(p->multiattr, p->kind, false);
+ if (kv_size(p->multihl)) {
+ msg_multihl(p->multihl, p->kind, false);
} else if (p->msg != NULL) {
- msg_attr_keep(p->msg, p->attr, false, p->multiline);
+ msg_hl_keep(p->msg, p->hl_id, false, p->multiline);
}
}
msg_hist_off = false;
@@ -1338,7 +1340,7 @@ static void hit_return_msg(bool newline_sb)
msg_puts(_("Interrupt: "));
}
- msg_puts_attr(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
+ msg_puts_hl(_("Press ENTER or type command to continue"), HLF_R, false);
if (!msg_use_printf()) {
msg_clr_eos();
}
@@ -1346,7 +1348,7 @@ static void hit_return_msg(bool newline_sb)
}
/// Set "keep_msg" to "s". Free the old value and check for NULL pointer.
-void set_keep_msg(const char *s, int attr)
+void set_keep_msg(const char *s, int hl_id)
{
xfree(keep_msg);
if (s != NULL && msg_silent == 0) {
@@ -1355,7 +1357,7 @@ void set_keep_msg(const char *s, int attr)
keep_msg = NULL;
}
keep_msg_more = false;
- keep_msg_attr = attr;
+ keep_msg_hl_id = hl_id;
}
/// Return true if printing messages should currently be done.
@@ -1480,10 +1482,10 @@ void msg_starthere(void)
void msg_putchar(int c)
{
- msg_putchar_attr(c, 0);
+ msg_putchar_hl(c, 0);
}
-void msg_putchar_attr(int c, int attr)
+void msg_putchar_hl(int c, int hl_id)
{
char buf[MB_MAXCHAR + 1];
@@ -1495,7 +1497,7 @@ void msg_putchar_attr(int c, int attr)
} else {
buf[utf_char2bytes(c, buf)] = NUL;
}
- msg_puts_attr(buf, attr);
+ msg_puts_hl(buf, hl_id, false);
}
void msg_outnum(int n)
@@ -1508,48 +1510,42 @@ void msg_outnum(int n)
void msg_home_replace(const char *fname)
{
- msg_home_replace_attr(fname, 0);
-}
-
-void msg_home_replace_hl(const char *fname)
-{
- msg_home_replace_attr(fname, HL_ATTR(HLF_D));
+ msg_home_replace_hl(fname, 0);
}
-static void msg_home_replace_attr(const char *fname, int attr)
+static void msg_home_replace_hl(const char *fname, int hl_id)
{
char *name = home_replace_save(NULL, fname);
- msg_outtrans(name, attr);
+ msg_outtrans(name, hl_id, false);
xfree(name);
}
-/// Output 'len' characters in 'str' (including NULs) with translation
-/// if 'len' is -1, output up to a NUL character.
-/// Use attributes 'attr'.
+/// Output "len" characters in "str" (including NULs) with translation
+/// if "len" is -1, output up to a NUL character. Use highlight "hl_id".
///
/// @return the number of characters it takes on the screen.
-int msg_outtrans(const char *str, int attr)
+int msg_outtrans(const char *str, int hl_id, bool hist)
{
- return msg_outtrans_len(str, (int)strlen(str), attr);
+ return msg_outtrans_len(str, (int)strlen(str), hl_id, hist);
}
/// Output one character at "p".
/// Handles multi-byte characters.
///
/// @return pointer to the next character.
-const char *msg_outtrans_one(const char *p, int attr)
+const char *msg_outtrans_one(const char *p, int hl_id, bool hist)
{
int l;
if ((l = utfc_ptr2len(p)) > 1) {
- msg_outtrans_len(p, l, attr);
+ msg_outtrans_len(p, l, hl_id, hist);
return p + l;
}
- msg_puts_attr(transchar_byte_buf(NULL, (uint8_t)(*p)), attr);
+ msg_puts_hl(transchar_byte_buf(NULL, (uint8_t)(*p)), hl_id, hist);
return p + 1;
}
-int msg_outtrans_len(const char *msgstr, int len, int attr)
+int msg_outtrans_len(const char *msgstr, int len, int hl_id, bool hist)
{
int retval = 0;
const char *str = msgstr;
@@ -1561,10 +1557,8 @@ int msg_outtrans_len(const char *msgstr, int len, int attr)
// Only quit when got_int was set in here.
got_int = false;
- // if MSG_HIST flag set, add message to history
- if (attr & MSG_HIST) {
- add_msg_hist(str, len, attr, false);
- attr &= ~MSG_HIST;
+ if (hist) {
+ add_msg_hist(str, len, hl_id, false);
}
// When drawing over the command line no need to clear it later or remove
@@ -1588,10 +1582,10 @@ int msg_outtrans_len(const char *msgstr, int len, int attr)
// Unprintable multi-byte char: print the printable chars so
// far and the translation of the unprintable char.
if (str > plain_start) {
- msg_puts_len(plain_start, str - plain_start, attr);
+ msg_puts_len(plain_start, str - plain_start, hl_id, hist);
}
plain_start = str + mb_l;
- msg_puts_attr(transchar_buf(NULL, c), attr == 0 ? HL_ATTR(HLF_8) : attr);
+ msg_puts_hl(transchar_buf(NULL, c), hl_id == 0 ? HLF_8 : hl_id, false);
retval += char2cells(c);
}
len -= mb_l - 1;
@@ -1602,10 +1596,10 @@ int msg_outtrans_len(const char *msgstr, int len, int attr)
// Unprintable char: print the printable chars so far and the
// translation of the unprintable char.
if (str > plain_start) {
- msg_puts_len(plain_start, str - plain_start, attr);
+ msg_puts_len(plain_start, str - plain_start, hl_id, hist);
}
plain_start = str + 1;
- msg_puts_attr(s, attr == 0 ? HL_ATTR(HLF_8) : attr);
+ msg_puts_hl(s, hl_id == 0 ? HLF_8 : hl_id, false);
retval += (int)strlen(s);
} else {
retval++;
@@ -1616,7 +1610,7 @@ int msg_outtrans_len(const char *msgstr, int len, int attr)
if (str > plain_start && !got_int) {
// Print the printable chars at the end.
- msg_puts_len(plain_start, str - plain_start, attr);
+ msg_puts_len(plain_start, str - plain_start, hl_id, hist);
}
got_int |= save_got_int;
@@ -1666,7 +1660,7 @@ int msg_outtrans_special(const char *strstart, bool from, int maxlen)
}
const char *str = strstart;
int retval = 0;
- int attr = HL_ATTR(HLF_8);
+ int hl_id = HLF_8;
while (*str != NUL) {
const char *text;
@@ -1686,9 +1680,7 @@ int msg_outtrans_special(const char *strstart, bool from, int maxlen)
break;
}
// Highlight special keys
- msg_puts_attr(text, (len > 1
- && utfc_ptr2len(text) <= 1
- ? attr : 0));
+ msg_puts_hl(text, (len > 1 && utfc_ptr2len(text) <= 1 ? hl_id : 0), false);
retval += len;
}
return retval;
@@ -1856,7 +1848,7 @@ void msg_prt_line(const char *s, bool list)
schar_T sc_final = 0;
const char *p_extra = NULL; // init to make SASC shut up. ASCII only!
int n;
- int attr = 0;
+ int hl_id = 0;
const char *lead = NULL;
bool in_multispace = false;
int multispace_pos = 0;
@@ -1920,7 +1912,7 @@ void msg_prt_line(const char *s, bool list)
s += l;
continue;
} else {
- attr = 0;
+ hl_id = 0;
int c = (uint8_t)(*s++);
sc_extra = NUL;
sc_final = NUL;
@@ -1944,13 +1936,13 @@ void msg_prt_line(const char *s, bool list)
: curwin->w_p_lcs_chars.tab1;
sc_extra = curwin->w_p_lcs_chars.tab2;
sc_final = curwin->w_p_lcs_chars.tab3;
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
}
} else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
p_extra = "";
n_extra = 1;
sc = curwin->w_p_lcs_chars.eol;
- attr = HL_ATTR(HLF_AT);
+ hl_id = HLF_AT;
s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1;
@@ -1958,7 +1950,7 @@ void msg_prt_line(const char *s, bool list)
sc = schar_from_ascii(*p_extra++);
// Use special coloring to be able to distinguish <hex> from
// the same in plain text.
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else if (c == ' ') {
if (lead != NULL && s <= lead && in_multispace
&& curwin->w_p_lcs_chars.leadmultispace != NULL) {
@@ -1966,23 +1958,23 @@ void msg_prt_line(const char *s, bool list)
if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
multispace_pos = 0;
}
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else if (lead != NULL && s <= lead && curwin->w_p_lcs_chars.lead != NUL) {
sc = curwin->w_p_lcs_chars.lead;
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else if (trail != NULL && s > trail) {
sc = curwin->w_p_lcs_chars.trail;
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else if (in_multispace
&& curwin->w_p_lcs_chars.multispace != NULL) {
sc = curwin->w_p_lcs_chars.multispace[multispace_pos++];
if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
multispace_pos = 0;
}
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else if (list && curwin->w_p_lcs_chars.space != NUL) {
sc = curwin->w_p_lcs_chars.space;
- attr = HL_ATTR(HLF_0);
+ hl_id = HLF_0;
} else {
sc = schar_from_ascii(' '); // SPACE!
}
@@ -1998,7 +1990,7 @@ void msg_prt_line(const char *s, bool list)
// TODO(bfredl): this is such baloney. need msg_put_schar
char buf[MAX_SCHAR_SIZE];
schar_get(buf, sc);
- msg_puts_attr(buf, attr);
+ msg_puts_hl(buf, hl_id, false);
col++;
}
msg_clr_eos();
@@ -2008,42 +2000,42 @@ void msg_prt_line(const char *s, bool list)
/// Update msg_row and msg_col for the next message.
void msg_puts(const char *s)
{
- msg_puts_attr(s, 0);
+ msg_puts_hl(s, 0, false);
}
void msg_puts_title(const char *s)
{
- msg_puts_attr(s, HL_ATTR(HLF_T));
+ msg_puts_hl(s, HLF_T, false);
}
/// Show a message in such a way that it always fits in the line. Cut out a
/// part in the middle and replace it with "..." when necessary.
/// Does not handle multi-byte characters!
-void msg_outtrans_long(const char *longstr, int attr)
+void msg_outtrans_long(const char *longstr, int hl_id)
{
int len = (int)strlen(longstr);
int slen = len;
int room = Columns - msg_col;
if (len > room && room >= 20) {
slen = (room - 3) / 2;
- msg_outtrans_len(longstr, slen, attr);
- msg_puts_attr("...", HL_ATTR(HLF_8));
+ msg_outtrans_len(longstr, slen, hl_id, false);
+ msg_puts_hl("...", HLF_8, false);
}
- msg_outtrans_len(longstr + len - slen, slen, attr);
+ msg_outtrans_len(longstr + len - slen, slen, hl_id, len);
}
-/// Basic function for writing a message with highlight attributes.
-void msg_puts_attr(const char *const s, const int attr)
+/// Basic function for writing a message with highlight id.
+void msg_puts_hl(const char *const s, const int hl_id, const bool hist)
{
- msg_puts_len(s, -1, attr);
+ msg_puts_len(s, -1, hl_id, hist);
}
-/// Write a message with highlight attributes
+/// Write a message with highlight id.
///
/// @param[in] str NUL-terminated message string.
/// @param[in] len Length of the string or -1.
-/// @param[in] attr Highlight attribute.
-void msg_puts_len(const char *const str, const ptrdiff_t len, int attr)
+/// @param[in] hl_id Highlight id.
+void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hist)
FUNC_ATTR_NONNULL_ALL
{
assert(len < 0 || memchr(str, 0, (size_t)len) == NULL);
@@ -2055,10 +2047,8 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int attr)
return;
}
- // if MSG_HIST flag set, add message to history
- if (attr & MSG_HIST) {
- add_msg_hist(str, (int)len, attr, false);
- attr &= ~MSG_HIST;
+ if (hist) {
+ add_msg_hist(str, (int)len, hl_id, false);
}
// When writing something to the screen after it has scrolled, requires a
@@ -2095,7 +2085,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int attr)
}
}
if (!msg_use_printf() || (headless_mode && default_grid.chars)) {
- msg_puts_display(str, (int)len, attr, false);
+ msg_puts_display(str, (int)len, hl_id, false);
}
need_fileinfo = false;
@@ -2104,11 +2094,11 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int attr)
/// Print a formatted message
///
/// Message printed is limited by #IOSIZE. Must not be used from inside
-/// msg_puts_attr().
+/// msg_puts_hl_id().
///
-/// @param[in] attr Highlight attributes.
+/// @param[in] hl_id Highlight id.
/// @param[in] fmt Format string.
-void msg_printf_attr(const int attr, const char *const fmt, ...)
+void msg_printf_hl(const int hl_id, const char *const fmt, ...)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PRINTF(2, 3)
{
static char msgbuf[IOSIZE];
@@ -2119,7 +2109,7 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
va_end(ap);
msg_scroll = true;
- msg_puts_len(msgbuf, (ptrdiff_t)len, attr);
+ msg_puts_len(msgbuf, (ptrdiff_t)len, hl_id, true);
}
static void msg_ext_emit_chunk(void)
@@ -2136,16 +2126,18 @@ static void msg_ext_emit_chunk(void)
msg_ext_last_attr = -1;
String text = ga_take_string(&msg_ext_last_chunk);
ADD(chunk, STRING_OBJ(text));
+ ADD(chunk, INTEGER_OBJ(msg_ext_last_hl_id));
ADD(*msg_ext_chunks, ARRAY_OBJ(chunk));
}
/// The display part of msg_puts_len().
/// May be called recursively to display scroll-back text.
-static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
+static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse)
{
const char *s = str;
const char *sb_str = str;
int sb_col = msg_col;
+ int attr = hl_id ? syn_id2attr(hl_id) : 0;
did_wait_return = false;
@@ -2153,6 +2145,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
if (attr != msg_ext_last_attr) {
msg_ext_emit_chunk();
msg_ext_last_attr = attr;
+ msg_ext_last_hl_id = hl_id;
}
// Concat pieces with the same highlight
size_t len = maxlen < 0 ? strlen(str) : strnlen(str, (size_t)maxlen);
@@ -2172,7 +2165,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
if (msg_col >= Columns) {
if (p_more && !recurse) {
// Store text for scrolling back.
- store_sb_text(&sb_str, s, attr, &sb_col, true);
+ store_sb_text(&sb_str, s, hl_id, &sb_col, true);
}
if (msg_no_more && lines_left == 0) {
break;
@@ -2262,7 +2255,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
msg_row++;
if (p_more && !recurse) {
// Store text for scrolling back.
- store_sb_text(&sb_str, s, attr, &sb_col, true);
+ store_sb_text(&sb_str, s, hl_id, &sb_col, true);
}
} else if (c == '\r') { // go to column 0
msg_col = 0;
@@ -2291,7 +2284,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse)
msg_cursor_goto(msg_row, msg_col);
if (p_more && !recurse) {
- store_sb_text(&sb_str, s, attr, &sb_col, false);
+ store_sb_text(&sb_str, s, hl_id, &sb_col, false);
}
msg_check();
@@ -2481,7 +2474,7 @@ static sb_clear_T do_clear_sb_text = SB_CLEAR_NONE;
/// @param sb_str start of string
/// @param s just after string
/// @param finish line ends
-static void store_sb_text(const char **sb_str, const char *s, int attr, int *sb_col, int finish)
+static void store_sb_text(const char **sb_str, const char *s, int hl_id, int *sb_col, int finish)
{
msgchunk_T *mp;
@@ -2499,7 +2492,7 @@ static void store_sb_text(const char **sb_str, const char *s, int attr, int *sb_
mp = xmalloc(offsetof(msgchunk_T, sb_text) + (size_t)(s - *sb_str) + 1);
mp->sb_eol = (char)finish;
mp->sb_msg_col = *sb_col;
- mp->sb_attr = attr;
+ mp->sb_hl_id = hl_id;
memcpy(mp->sb_text, *sb_str, (size_t)(s - *sb_str));
mp->sb_text[s - *sb_str] = NUL;
@@ -2637,7 +2630,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
msg_row = row;
msg_col = mp->sb_msg_col;
char *p = mp->sb_text;
- msg_puts_display(p, -1, mp->sb_attr, true);
+ msg_puts_display(p, -1, mp->sb_hl_id, true);
if (mp->sb_eol || mp->sb_next == NULL) {
break;
}
@@ -2694,12 +2687,13 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
// primitive way to compute the current column
if (*s == '\r' || *s == '\n') {
msg_col = 0;
+ msg_didout = false;
} else {
msg_col += cw;
+ msg_didout = true;
}
s += len;
}
- msg_didout = true; // assume that line is not empty
}
/// Show the more-prompt and handle the user response.
@@ -3321,17 +3315,17 @@ void give_warning(const char *message, bool hl)
set_vim_var_string(VV_WARNINGMSG, message, -1);
XFREE_CLEAR(keep_msg);
if (hl) {
- keep_msg_attr = HL_ATTR(HLF_W);
+ keep_msg_hl_id = HLF_W;
} else {
- keep_msg_attr = 0;
+ keep_msg_hl_id = 0;
}
if (msg_ext_kind == NULL) {
msg_ext_set_kind("wmsg");
}
- if (msg(message, keep_msg_attr) && msg_scrolled == 0) {
- set_keep_msg(message, keep_msg_attr);
+ if (msg(message, keep_msg_hl_id) && msg_scrolled == 0) {
+ set_keep_msg(message, keep_msg_hl_id);
}
msg_didout = false; // Overwrite this message.
msg_nowait = true; // Don't wait for this message.
@@ -3664,7 +3658,7 @@ void display_confirm_msg(void)
confirm_msg_used++;
if (confirm_msg != NULL) {
msg_ext_set_kind("confirm");
- msg_puts_attr(confirm_msg, HL_ATTR(HLF_M));
+ msg_puts_hl(confirm_msg, HLF_M, false);
}
confirm_msg_used--;
}
diff --git a/src/nvim/message.h b/src/nvim/message.h
index c11c33c039..13746406f9 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -28,8 +28,6 @@ enum {
VIM_DISCARDALL = 6,
};
-enum { MSG_HIST = 0x1000, }; ///< special attribute addition: Put message in history
-
/// First message
extern MessageHistoryEntry *first_msg_hist;
/// Last message
diff --git a/src/nvim/message_defs.h b/src/nvim/message_defs.h
index e60e60b3be..8d23b79385 100644
--- a/src/nvim/message_defs.h
+++ b/src/nvim/message_defs.h
@@ -6,7 +6,7 @@
typedef struct {
String text;
- int attr;
+ int hl_id;
} HlMessageChunk;
typedef kvec_t(HlMessageChunk) HlMessage;
@@ -16,7 +16,7 @@ typedef struct msg_hist {
struct msg_hist *next; ///< Next message.
char *msg; ///< Message text.
const char *kind; ///< Message kind (for msg_ext)
- int attr; ///< Message highlighting.
+ int hl_id; ///< Message highlighting.
bool multiline; ///< Multiline message.
- HlMessage multiattr; ///< multiattr message.
+ HlMessage multihl; ///< Multihl message.
} MessageHistoryEntry;
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 884bc88d73..1289adfabb 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1740,7 +1740,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
} else if (*gridp > 1) {
win_T *wp = get_win_by_grid_handle(*gridp);
if (wp && wp->w_grid_alloc.chars
- && !(wp->w_floating && !wp->w_config.focusable)) {
+ && !(wp->w_floating && !wp->w_config.mouse)) {
*rowp = MIN(*rowp - wp->w_grid.row_offset, wp->w_grid.rows - 1);
*colp = MIN(*colp - wp->w_grid.col_offset, wp->w_grid.cols - 1);
return wp;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index be9987cc7f..55aa385b33 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -693,7 +693,7 @@ static void normal_redraw_mode_message(NormalState *s)
keep_msg = kmsg;
kmsg = xstrdup(keep_msg);
- msg(kmsg, keep_msg_attr);
+ msg(kmsg, keep_msg_hl_id);
xfree(kmsg);
}
setcursor();
@@ -835,21 +835,29 @@ static void normal_get_additional_char(NormalState *s)
// because if it's put back with vungetc() it's too late to apply
// mapping.
no_mapping--;
+ GraphemeState state = GRAPHEME_STATE_INIT;
+ int prev_code = s->ca.nchar;
+
while ((s->c = vpeekc()) > 0
&& (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) {
s->c = plain_vgetc();
- // TODO(bfredl): only allowing up to two composing chars is cringe af.
- // Could reuse/abuse schar_T to at least allow us to input anything we are able
- // to display and use the stateful utf8proc algorithm like utf_composinglike
- if (!utf_iscomposing_legacy(s->c)) {
+
+ if (!utf_iscomposing(prev_code, s->c, &state)) {
vungetc(s->c); // it wasn't, put it back
break;
- } else if (s->ca.ncharC1 == 0) {
- s->ca.ncharC1 = s->c;
- } else {
- s->ca.ncharC2 = s->c;
}
+
+ // first composing char, first put base char into buffer
+ if (s->ca.nchar_len == 0) {
+ s->ca.nchar_len = utf_char2bytes(s->ca.nchar, s->ca.nchar_composing);
+ }
+
+ if (s->ca.nchar_len + utf_char2len(s->c) < (int)sizeof(s->ca.nchar_composing)) {
+ s->ca.nchar_len += utf_char2bytes(s->c, s->ca.nchar_composing + s->ca.nchar_len);
+ }
+ prev_code = s->c;
}
+ s->ca.nchar_composing[s->ca.nchar_len] = NUL;
no_mapping++;
// Vim may be in a different mode when the user types the next key,
// but when replaying a recording the next key is already in the
@@ -1380,7 +1388,7 @@ static void normal_redraw(NormalState *s)
// check for duplicates. Never put this message in
// history.
msg_hist_off = true;
- msg(p, keep_msg_attr);
+ msg(p, keep_msg_hl_id);
msg_hist_off = false;
xfree(p);
}
@@ -1735,7 +1743,12 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
static void prep_redo_cmd(cmdarg_T *cap)
{
prep_redo(cap->oap->regname, cap->count0,
- NUL, cap->cmdchar, NUL, NUL, cap->nchar);
+ NUL, cap->cmdchar, NUL, NUL, NUL);
+ if (cap->nchar_len > 0) {
+ AppendToRedobuff(cap->nchar_composing);
+ } else {
+ AppendCharToRedobuff(cap->nchar);
+ }
}
/// Prepare for redo of any command.
@@ -2068,11 +2081,12 @@ static void display_showcmd(void)
if (ui_has(kUIMessages)) {
MAXSIZE_TEMP_ARRAY(content, 1);
- MAXSIZE_TEMP_ARRAY(chunk, 2);
+ MAXSIZE_TEMP_ARRAY(chunk, 3);
if (!showcmd_is_clear) {
// placeholder for future highlight support
ADD_C(chunk, INTEGER_OBJ(0));
ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf));
+ ADD_C(chunk, INTEGER_OBJ(0));
ADD_C(content, ARRAY_OBJ(chunk));
}
ui_call_msg_showcmd(content);
@@ -4548,17 +4562,15 @@ static void nv_replace(cmdarg_T *cap)
// Give 'r' to edit(), to get the redo command right.
invoke_edit(cap, true, 'r', false);
} else {
- prep_redo(cap->oap->regname, cap->count1,
- NUL, 'r', NUL, had_ctrl_v, cap->nchar);
+ prep_redo(cap->oap->regname, cap->count1, NUL, 'r', NUL, had_ctrl_v, 0);
curbuf->b_op_start = curwin->w_cursor;
const int old_State = State;
- if (cap->ncharC1 != 0) {
- AppendCharToRedobuff(cap->ncharC1);
- }
- if (cap->ncharC2 != 0) {
- AppendCharToRedobuff(cap->ncharC2);
+ if (cap->nchar_len > 0) {
+ AppendToRedobuff(cap->nchar_composing);
+ } else {
+ AppendCharToRedobuff(cap->nchar);
}
// This is slow, but it handles replacing a single-byte with a
@@ -4576,15 +4588,13 @@ static void nv_replace(cmdarg_T *cap)
curwin->w_cursor.col++;
}
} else {
- ins_char(cap->nchar);
+ if (cap->nchar_len) {
+ ins_char_bytes(cap->nchar_composing, (size_t)cap->nchar_len);
+ } else {
+ ins_char(cap->nchar);
+ }
}
State = old_State;
- if (cap->ncharC1 != 0) {
- ins_char(cap->ncharC1);
- }
- if (cap->ncharC2 != 0) {
- ins_char(cap->ncharC2);
- }
}
curwin->w_cursor.col--; // cursor on the last replaced char
// if the character on the left of the current cursor is a multi-byte
@@ -5239,6 +5249,12 @@ void nv_g_home_m_cmd(cmdarg_T *cap)
curwin->w_valid &= ~VALID_WCOL;
}
curwin->w_set_curswant = true;
+ if (hasAnyFolding(curwin)) {
+ validate_cheight(curwin);
+ if (curwin->w_cline_folded) {
+ update_curswant_force();
+ }
+ }
adjust_skipcol();
}
diff --git a/src/nvim/normal_defs.h b/src/nvim/normal_defs.h
index 0309f6bc80..7b49b28a0f 100644
--- a/src/nvim/normal_defs.h
+++ b/src/nvim/normal_defs.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h"
/// Motion types, used for operators and for yank/delete registers.
///
@@ -47,8 +48,8 @@ typedef struct {
int prechar; ///< prefix character (optional, always 'g')
int cmdchar; ///< command character
int nchar; ///< next command character (optional)
- int ncharC1; ///< first composing character (optional)
- int ncharC2; ///< second composing character (optional)
+ char nchar_composing[MAX_SCHAR_SIZE]; ///< next char with composing chars (optional)
+ int nchar_len; ///< len of nchar_composing (when zero, use nchar instead)
int extra_char; ///< yet another character (optional)
int opcount; ///< count before an operator
int count0; ///< count before command, default 0
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 4b8382c971..7dd3f665ba 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -12,6 +12,7 @@
#include <uv.h>
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/autocmd.h"
@@ -273,7 +274,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
vim_snprintf(IObuff, IOSIZE,
NGETTEXT(msg_line_single, msg_line_plural, oap->line_count),
(int64_t)oap->line_count, op, amount);
- msg_attr_keep(IObuff, 0, true, false);
+ msg_hl_keep(IObuff, 0, true, false);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
@@ -871,18 +872,22 @@ static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val)
if (tv_dict_get_tv(dict, "lines", &tv) == OK) {
if (tv.v_type == VAR_STRING) {
- yankreg->y_array = (char**) xcalloc(sizeof(char*), 1);
- yankreg->y_array[0] = strdup(tv.vval.v_string);
+ yankreg->y_array = (String*) xcalloc(1, sizeof(String));
+ yankreg->y_array[0].data = strdup(tv.vval.v_string);
+ yankreg->y_array[0].size = strlen(tv.vval.v_string);
} else if (tv.v_type == VAR_LIST) {
yankreg->y_array =
- (char**) xcalloc(sizeof(char*), (size_t) tv_list_len(tv.vval.v_list));
+ (String*) xcalloc((size_t) tv_list_len(tv.vval.v_list), sizeof(String));
i = 0;
TV_LIST_ITER_CONST(tv.vval.v_list, li, {
if (li->li_tv.v_type == VAR_STRING) {
- yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv));
+ char* tmp = strdup(tv_get_string(&li->li_tv));
+ yankreg->y_array[i].data = tmp;
+ yankreg->y_array[i].size = strlen(tmp);
} else {
- yankreg->y_array[i] = NULL;
+ yankreg->y_array[i].data = NULL;
+ yankreg->y_array[i].size = 0;
}
++ i;
});
@@ -903,11 +908,13 @@ static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val)
case VAR_LIST:
yankreg->y_type = kMTLineWise;
sz = (size_t) tv_list_len(val->vval.v_list);
- yankreg->y_array = (char**) xcalloc(sizeof(char*), sz);
+ yankreg->y_array = (String*) xcalloc(sz, sizeof(String));
yankreg->y_size = sz;
i = 0;
TV_LIST_ITER_CONST(val->vval.v_list, li, {
- yankreg->y_array[i] = strdup(tv_get_string(&li->li_tv));
+ char* tmp = strdup(tv_get_string(&li->li_tv));
+ yankreg->y_array[i].data = tmp;
+ yankreg->y_array[i].size = strlen(tmp);
i ++;
});
break;
@@ -917,8 +924,10 @@ static void typval_to_yankreg(yankreg_T* yankreg, typval_T* val)
yankreg->y_size = 1;
if (val->vval.v_string) {
- yankreg->y_array = (char**) xcalloc(sizeof(char*), 1);
- yankreg->y_array[0] = strdup(tv_get_string(val));
+ yankreg->y_array = (String*) xcalloc(1, sizeof(String));
+ char* tmp = strdup(tv_get_string(val));
+ yankreg->y_array[0].data = tmp;
+ yankreg->y_array[0].size = strlen(tmp);
} else {
yankreg->y_array = NULL;
}
@@ -1024,9 +1033,9 @@ yankreg_T *copy_register(int name)
if (copy->y_size == 0) {
copy->y_array = NULL;
} else {
- copy->y_array = (char**) xcalloc(copy->y_size, sizeof(char *));
+ copy->y_array = xcalloc(copy->y_size, sizeof(String));
for (size_t i = 0; i < copy->y_size; i++) {
- copy->y_array[i] = xstrdup(reg->y_array[i]);
+ copy->y_array[i] = copy_string(reg->y_array[i], NULL);
}
}
return copy;
@@ -1129,23 +1138,24 @@ static int stuff_yank(int regname, char *p)
xfree(p);
return OK;
}
+
+ const size_t plen = strlen(p);
yankreg_T *reg = get_yank_register(regname, YREG_YANK);
if (is_append_register(regname) && reg->y_array != NULL) {
- char **pp = &(reg->y_array[reg->y_size - 1]);
- const size_t ppl = strlen(*pp);
- const size_t pl = strlen(p);
- char *lp = xmalloc(ppl + pl + 1);
- memcpy(lp, *pp, ppl);
- memcpy(lp + ppl, p, pl);
- *(lp + ppl + pl) = NUL;
+ String *pp = &(reg->y_array[reg->y_size - 1]);
+ const size_t tmplen = pp->size + plen;
+ char *tmp = xmalloc(tmplen + 1);
+ memcpy(tmp, pp->data, pp->size);
+ memcpy(tmp + pp->size, p, plen);
+ *(tmp + tmplen) = NUL;
xfree(p);
- xfree(*pp);
- *pp = lp;
+ xfree(pp->data);
+ *pp = cbuf_as_string(tmp, tmplen);
} else {
free_register(reg);
reg->additional_data = NULL;
- reg->y_array = xmalloc(sizeof(char *));
- reg->y_array[0] = p;
+ reg->y_array = xmalloc(sizeof(String));
+ reg->y_array[0] = cbuf_as_string(p, plen);
reg->y_size = 1;
reg->y_type = kMTCharWise;
}
@@ -1169,7 +1179,7 @@ static int execreg_lastc = NUL;
/// with a \. Lines that start with a comment "\ character are ignored.
/// @returns the concatenated line. The index of the line that should be
/// processed next is returned in idx.
-static char *execreg_line_continuation(char **lines, size_t *idx)
+static char *execreg_line_continuation(String *lines, size_t *idx)
{
size_t i = *idx;
assert(i > 0);
@@ -1182,7 +1192,7 @@ static char *execreg_line_continuation(char **lines, size_t *idx)
// Any line not starting with \ or "\ is the start of the
// command.
while (--i > 0) {
- char *p = skipwhite(lines[i]);
+ char *p = skipwhite(lines[i].data);
if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) {
break;
}
@@ -1190,9 +1200,9 @@ static char *execreg_line_continuation(char **lines, size_t *idx)
const size_t cmd_start = i;
// join all the lines
- ga_concat(&ga, lines[cmd_start]);
+ ga_concat(&ga, lines[cmd_start].data);
for (size_t j = cmd_start + 1; j <= cmd_end; j++) {
- char *p = skipwhite(lines[j]);
+ char *p = skipwhite(lines[j].data);
if (*p == '\\') {
// Adjust the growsize to the current length to
// speed up concatenating many lines.
@@ -1203,7 +1213,7 @@ static char *execreg_line_continuation(char **lines, size_t *idx)
}
}
ga_append(&ga, NUL);
- char *str = xstrdup(ga.ga_data);
+ char *str = xmemdupz(ga.ga_data, (size_t)ga.ga_len);
ga_clear(&ga);
*idx = i;
@@ -1296,7 +1306,7 @@ int do_execreg(int regname, int colon, int addcr, int silent)
}
// Handle line-continuation for :@<register>
- char *str = reg->y_array[i];
+ char *str = reg->y_array[i].data;
bool free_str = false;
if (colon && i > 0) {
char *p = skipwhite(str);
@@ -1434,7 +1444,7 @@ int insert_reg(int regname, bool literally_arg)
if (u_save_cursor() == FAIL) {
return FAIL;
}
- del_chars(mb_charlen(reg->y_array[0]), true);
+ del_chars(mb_charlen(reg->y_array[0].data), true);
curpos = curwin->w_cursor;
if (oneright() == FAIL) {
// hit end of line, need to put forward (after the current position)
@@ -1447,7 +1457,7 @@ int insert_reg(int regname, bool literally_arg)
AppendCharToRedobuff(regname);
do_put(regname, NULL, dir, 1, PUT_CURSEND);
} else {
- stuffescaped(reg->y_array[i], literally);
+ stuffescaped(reg->y_array[i].data, literally);
}
// Insert a newline between lines and after last line if
// y_type is kMTLineWise.
@@ -1494,7 +1504,7 @@ static dict_T* yankreg_to_dict(yankreg_T* yankreg) {
size_t i;
for (i = 0; i < yankreg->y_size; ++ i) {
tv_list_append_string(
- lines, yankreg->y_array[i], (long)strlen(yankreg->y_array[i]));
+ lines, yankreg->y_array[i].data, (long)yankreg->y_array[i].size);
}
tv_dict_add_list(dict, S_LEN("lines"), lines);
@@ -1657,7 +1667,7 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
}
for (size_t i = 0; i < reg->y_size; i++) {
- cmdline_paste_str(reg->y_array[i], literally);
+ cmdline_paste_str(reg->y_array[i].data, literally);
// Insert ^M between lines, unless `remcr` is true.
if (i < reg->y_size - 1 && !remcr) {
@@ -2786,7 +2796,7 @@ void free_register(yankreg_T *reg)
}
for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included
- xfree(reg->y_array[i]);
+ API_CLEAR_STRING(reg->y_array[i]);
}
XFREE_CLEAR(reg->y_array);
}
@@ -2823,7 +2833,6 @@ bool op_yank(oparg_T *oap, bool message)
set_clipboard(oap->regname, reg);
do_autocmd_textyankpost(oap, reg);
-
return true;
}
@@ -2859,7 +2868,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
reg->y_size = yanklines;
reg->y_type = yank_type; // set the yank register type
reg->y_width = 0;
- reg->y_array = xcalloc(yanklines, sizeof(char *));
+ reg->y_array = xcalloc(yanklines, sizeof(String));
reg->additional_data = NULL;
reg->timestamp = os_time();
@@ -2883,11 +2892,16 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
break;
case kMTLineWise:
- reg->y_array[y_idx] = xstrdup(ml_get(lnum));
+ reg->y_array[y_idx] = cbuf_to_string(ml_get(lnum), (size_t)ml_get_len(lnum));
break;
case kMTCharWise:
charwise_block_prep(oap->start, oap->end, &bd, lnum, oap->inclusive);
+ // make sure bd.textlen is not longer than the text
+ int tmp = (int)strlen(bd.textstart);
+ if (tmp < bd.textlen) {
+ bd.textlen = tmp;
+ }
yank_copy_line(reg, &bd, y_idx, false);
break;
@@ -2899,7 +2913,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
if (curr != reg) { // append the new block to the old block
size_t j;
- char **new_ptr = xmalloc(sizeof(char *) * (curr->y_size + reg->y_size));
+ String *new_ptr = xmalloc(sizeof(String) * (curr->y_size + reg->y_size));
for (j = 0; j < curr->y_size; j++) {
new_ptr[j] = curr->y_array[j];
}
@@ -2915,13 +2929,16 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
// the new block, unless being Vi compatible.
if (curr->y_type == kMTCharWise
&& vim_strchr(p_cpo, CPO_REGAPPEND) == NULL) {
- char *pnew = xmalloc(strlen(curr->y_array[curr->y_size - 1])
- + strlen(reg->y_array[0]) + 1);
- STRCPY(pnew, curr->y_array[--j]);
- strcat(pnew, reg->y_array[0]);
- xfree(curr->y_array[j]);
- xfree(reg->y_array[0]);
- curr->y_array[j++] = pnew;
+ char *pnew = xmalloc(curr->y_array[curr->y_size - 1].size
+ + reg->y_array[0].size + 1);
+ j--;
+ STRCPY(pnew, curr->y_array[j].data);
+ STRCPY(pnew + curr->y_array[j].size, reg->y_array[0].data);
+ xfree(curr->y_array[j].data);
+ curr->y_array[j] = cbuf_as_string(pnew,
+ curr->y_array[j].size + reg->y_array[0].size);
+ j++;
+ API_CLEAR_STRING(reg->y_array[0]);
y_idx = 1;
} else {
y_idx = 0;
@@ -2992,7 +3009,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
int size = bd->startspaces + bd->endspaces + bd->textlen;
assert(size >= 0);
char *pnew = xmallocz((size_t)size);
- reg->y_array[y_idx] = pnew;
+ reg->y_array[y_idx].data = pnew;
memset(pnew, ' ', (size_t)bd->startspaces);
pnew += bd->startspaces;
memmove(pnew, bd->textstart, (size_t)bd->textlen);
@@ -3008,6 +3025,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
}
}
*pnew = NUL;
+ reg->y_array[y_idx].size = (size_t)(pnew - reg->y_array[y_idx].data);
}
/// Execute autocommands for TextYankPost.
@@ -3034,7 +3052,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
// 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, reg->y_array[i], -1);
+ tv_list_append_string(list, reg->y_array[i].data, -1);
}
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
@@ -3096,7 +3114,7 @@ static void do_autocmd_textput(int regname, yankreg_T *reg, enum auto_event evt)
// 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);
+ tv_list_append_string(list, reg->y_array[i].data, reg->y_array[i].size);
}
tv_list_set_lock(list, VAR_FIXED);
(void)tv_dict_add_list(dict, S_LEN("regcontents"), list);
@@ -3138,16 +3156,8 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
size_t y_size;
int y_width = 0;
colnr_T vcol = 0;
- int incr = 0;
- struct block_def bd;
- char **y_array = NULL;
+ String *y_array = NULL;
linenr_T nr_lines = 0;
- int indent;
- int orig_indent = 0; // init for gcc
- int indent_diff = 0; // init for gcc
- bool first_indent = true;
- int lendiff = 0;
- char *insert_string = NULL;
bool allocated = false;
const pos_T orig_start = curbuf->b_op_start;
const pos_T orig_end = curbuf->b_op_end;
@@ -3157,10 +3167,6 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
do_autocmd_textput(regname, reg, EVENT_TEXTPUTPRE);
}
- if (flags & PUT_FIXINDENT) {
- orig_indent = get_indent();
- }
-
curbuf->b_op_start = curwin->w_cursor; // default for '[ mark
curbuf->b_op_end = curwin->w_cursor; // default for '] mark
@@ -3248,8 +3254,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// For special registers '%' (file name), '#' (alternate file name) and
// ':' (last command line), etc. we have to create a fake yank register.
- if (!reg && get_spec_reg(regname, &insert_string, &allocated, true)) {
- if (insert_string == NULL) {
+ String insert_string = STRING_INIT;
+ if (!reg && get_spec_reg(regname, &insert_string.data, &allocated, true)) {
+ if (insert_string.data == NULL) {
return;
}
}
@@ -3262,7 +3269,8 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
}
}
- if (insert_string != NULL) {
+ if (insert_string.data != NULL) {
+ insert_string.size = strlen(insert_string.data);
y_type = kMTCharWise;
if (regname == '=') {
// For the = register we need to split the string at NL
@@ -3270,29 +3278,37 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// Loop twice: count the number of lines and save them.
while (true) {
y_size = 0;
- char *ptr = insert_string;
+ char *ptr = insert_string.data;
+ size_t ptrlen = insert_string.size;
while (ptr != NULL) {
if (y_array != NULL) {
- y_array[y_size] = ptr;
+ y_array[y_size].data = ptr;
}
y_size++;
- ptr = vim_strchr(ptr, '\n');
- if (ptr != NULL) {
+ char *tmp = vim_strchr(ptr, '\n');
+ if (tmp == NULL) {
if (y_array != NULL) {
- *ptr = NUL;
+ y_array[y_size - 1].size = ptrlen;
}
- ptr++;
+ } else {
+ if (y_array != NULL) {
+ *tmp = NUL;
+ y_array[y_size - 1].size = (size_t)(tmp - ptr);
+ ptrlen -= y_array[y_size - 1].size + 1;
+ }
+ tmp++;
// A trailing '\n' makes the register linewise.
- if (*ptr == NUL) {
+ if (*tmp == NUL) {
y_type = kMTLineWise;
break;
}
}
+ ptr = tmp;
}
if (y_array != NULL) {
break;
}
- y_array = xmalloc(y_size * sizeof(char *));
+ y_array = xmalloc(y_size * sizeof(String));
}
} else {
y_size = 1; // use fake one-line yank register
@@ -3330,14 +3346,16 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
goto end;
}
char *curline = get_cursor_line_ptr();
- char *p = curline + curwin->w_cursor.col;
+ char *p = get_cursor_pos_ptr();
+ char *const p_orig = p;
+ const size_t plen = (size_t)get_cursor_pos_len();
if (dir == FORWARD && *p != NUL) {
MB_PTR_ADV(p);
}
// we need this later for the correct extmark_splice() event
split_pos = (colnr_T)(p - curline);
- char *ptr = xstrdup(p);
+ char *ptr = xmemdupz(p, plen - (size_t)(p - p_orig));
ml_append(curwin->w_cursor.lnum, ptr, 0, false);
xfree(ptr);
@@ -3401,8 +3419,6 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
goto end;
}
- int yanklen = (int)strlen(y_array[0]);
-
if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
int viscol = getviscol();
@@ -3426,6 +3442,8 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// Block mode
if (y_type == kMTBlockWise) {
+ int incr = 0;
+ struct block_def bd;
int c = gchar_cursor();
colnr_T endcol2 = 0;
@@ -3518,14 +3536,14 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
}
}
- yanklen = (int)strlen(y_array[i]);
+ const int yanklen = (int)y_array[i].size;
if ((flags & PUT_BLOCK_INNER) == 0) {
// calculate number of spaces required to fill right side of block
spaces = y_width + 1;
- cstype = init_charsize_arg(&csarg, curwin, 0, y_array[i]);
- ci = utf_ptr2StrCharInfo(y_array[i]);
+ cstype = init_charsize_arg(&csarg, curwin, 0, y_array[i].data);
+ ci = utf_ptr2StrCharInfo(y_array[i].data);
while (*ci.ptr != NUL) {
spaces -= win_charsize(cstype, 0, ci.ptr, ci.chr.value, &csarg).width;
ci = utfc_next(ci);
@@ -3556,7 +3574,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// insert the new text
for (int j = 0; j < count; j++) {
- memmove(ptr, y_array[i], (size_t)yanklen);
+ memmove(ptr, y_array[i].data, (size_t)yanklen);
ptr += yanklen;
// insert block's trailing spaces only if there's text behind
@@ -3608,6 +3626,8 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
curwin->w_cursor.lnum = lnum;
}
} else {
+ const int yanklen = (int)y_array[0].size;
+
// Character or Line mode
if (y_type == kMTCharWise) {
// if type is kMTCharWise, FORWARD is the same as BACKWARD on the next
@@ -3680,10 +3700,10 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
memmove(newp, oldp, (size_t)col);
char *ptr = newp + col;
for (size_t i = 0; i < (size_t)count; i++) {
- memmove(ptr, y_array[0], (size_t)yanklen);
+ memmove(ptr, y_array[0].data, (size_t)yanklen);
ptr += yanklen;
}
- STRMOVE(ptr, oldp + col);
+ memmove(ptr, oldp + col, (size_t)(oldlen - col) + 1); // +1 for NUL
ml_replace(lnum, newp, false);
// compute the byte offset for the last character
@@ -3721,6 +3741,15 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
}
} else {
linenr_T new_lnum = new_cursor.lnum;
+ int indent;
+ int orig_indent = 0;
+ int indent_diff = 0; // init for gcc
+ bool first_indent = true;
+ int lendiff = 0;
+
+ if (flags & PUT_FIXINDENT) {
+ orig_indent = get_indent();
+ }
// Insert at least one line. When y_type is kMTCharWise, break the first
// line in two.
@@ -3732,10 +3761,11 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// Then append y_array[0] to first line.
lnum = new_cursor.lnum;
char *ptr = ml_get(lnum) + col;
- totlen = strlen(y_array[y_size - 1]);
- char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)col + totlen + 1);
- STRCPY(newp, y_array[y_size - 1]);
- strcat(newp, ptr);
+ size_t ptrlen = (size_t)ml_get_len(lnum) - (size_t)col;
+ totlen = y_array[y_size - 1].size;
+ char *newp = xmalloc(ptrlen + totlen + 1);
+ STRCPY(newp, y_array[y_size - 1].data);
+ STRCPY(newp + totlen, ptr);
// insert second line
ml_append(lnum, newp, 0, false);
new_lnum++;
@@ -3746,7 +3776,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
// copy first part of line
memmove(newp, oldp, (size_t)col);
// append to first line
- memmove(newp + col, y_array[0], (size_t)yanklen + 1);
+ memmove(newp + col, y_array[0].data, (size_t)yanklen + 1);
ml_replace(lnum, newp, false);
curwin->w_cursor.lnum = lnum;
@@ -3755,7 +3785,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
for (; i < y_size; i++) {
if ((y_type != kMTCharWise || i < y_size - 1)) {
- if (ml_append(lnum, y_array[i], 0, false) == FAIL) {
+ if (ml_append(lnum, y_array[i].data, 0, false) == FAIL) {
goto error;
}
new_lnum++;
@@ -3794,9 +3824,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
if (y_type == kMTCharWise
|| (y_type == kMTLineWise && (flags & PUT_LINE_SPLIT))) {
for (i = 0; i < y_size - 1; i++) {
- totsize += (bcount_t)strlen(y_array[i]) + 1;
+ totsize += (bcount_t)y_array[i].size + 1;
}
- lastsize = (int)strlen(y_array[y_size - 1]);
+ lastsize = (int)y_array[y_size - 1].size;
totsize += lastsize;
}
if (y_type == kMTCharWise) {
@@ -3840,13 +3870,13 @@ error:
// Put the '] mark on the first byte of the last inserted character.
// Correct the length for change in indent.
curbuf->b_op_end.lnum = new_lnum;
- size_t len = strlen(y_array[y_size - 1]);
- col = (colnr_T)len - lendiff;
+ col = (colnr_T)y_array[y_size - 1].size - lendiff;
if (col > 1) {
curbuf->b_op_end.col = col - 1;
- if (len > 0) {
- curbuf->b_op_end.col -= utf_head_off(y_array[y_size - 1],
- y_array[y_size - 1] + len - 1);
+ if (y_array[y_size - 1].size > 0) {
+ curbuf->b_op_end.col -= utf_head_off(y_array[y_size - 1].data,
+ y_array[y_size - 1].data
+ + y_array[y_size - 1].size - 1);
}
} else {
curbuf->b_op_end.col = 0;
@@ -3908,7 +3938,7 @@ end:
curbuf->b_op_end = orig_end;
}
if (allocated) {
- xfree(insert_string);
+ xfree(insert_string.data);
}
if (regname == '=') {
xfree(y_array);
@@ -3989,7 +4019,7 @@ void ex_display(exarg_T *eap)
if (arg != NULL && *arg == NUL) {
arg = NULL;
}
- int attr = HL_ATTR(HLF_8);
+ int hl_id = HLF_8;
// Highlight title
msg_puts_title(_("\nType Name Content"));
@@ -4030,7 +4060,7 @@ void ex_display(exarg_T *eap)
bool do_show = false;
for (size_t j = 0; !do_show && j < yb->y_size; j++) {
- do_show = !message_filtered(yb->y_array[j]);
+ do_show = !message_filtered(yb->y_array[j].data);
}
if (do_show || yb->y_size == 0) {
@@ -4045,18 +4075,18 @@ void ex_display(exarg_T *eap)
int n = Columns - 11;
for (size_t j = 0; j < yb->y_size && n > 1; j++) {
if (j) {
- msg_puts_attr("^J", attr);
+ msg_puts_hl("^J", hl_id, false);
n -= 2;
}
- for (p = yb->y_array[j];
+ for (p = yb->y_array[j].data;
*p != NUL && (n -= ptr2cells(p)) >= 0; p++) {
int clen = utfc_ptr2len(p);
- msg_outtrans_len(p, clen, 0);
+ msg_outtrans_len(p, clen, 0, false);
p += clen - 1;
}
}
if (n > 1 && yb->y_type == kMTLineWise) {
- msg_puts_attr("^J", attr);
+ msg_puts_hl("^J", hl_id, false);
}
}
os_breakcheck();
@@ -4126,10 +4156,10 @@ static void dis_msg(const char *p, bool skip_esc)
&& (n -= ptr2cells(p)) >= 0) {
int l;
if ((l = utfc_ptr2len(p)) > 1) {
- msg_outtrans_len(p, l, 0);
+ msg_outtrans_len(p, l, 0, false);
p += l;
} else {
- msg_outtrans_len(p++, 1, 0);
+ msg_outtrans_len(p++, 1, 0, false);
}
}
os_breakcheck();
@@ -5207,7 +5237,7 @@ void *get_reg_contents(int regname, int flags)
if (flags & kGRegList) {
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, reg->y_array[i], -1);
+ tv_list_append_string(list, reg->y_array[i].data, -1);
}
return list;
@@ -5216,9 +5246,8 @@ void *get_reg_contents(int regname, int flags)
// Compute length of resulting string.
size_t len = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- len += strlen(reg->y_array[i]);
- // Insert a newline between lines and after last line if
- // y_type is kMTLineWise.
+ len += reg->y_array[i].size;
+ // Insert a newline between lines and after last line if y_type is kMTLineWise.
if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
len++;
}
@@ -5229,11 +5258,10 @@ void *get_reg_contents(int regname, int flags)
// Copy the lines of the yank register into the string.
len = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- STRCPY(retval + len, reg->y_array[i]);
- len += strlen(retval + len);
+ STRCPY(retval + len, reg->y_array[i].data);
+ len += reg->y_array[i].size;
- // Insert a NL between lines and after the last line if y_type is
- // kMTLineWise.
+ // Insert a newline between lines and after the last line if y_type is kMTLineWise.
if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
retval[len++] = '\n';
}
@@ -5355,8 +5383,7 @@ void write_reg_contents_ex(int name, const char *str, ssize_t len, bool must_app
semsg(_(e_nobufnr), (int64_t)num);
}
} else {
- buf = buflist_findnr(buflist_findpat(str, str + strlen(str),
- true, false, false));
+ buf = buflist_findnr(buflist_findpat(str, str + len, true, false, false));
}
if (buf == NULL) {
return;
@@ -5452,7 +5479,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
}
// Grow the register array to hold the pointers to the new lines.
- char **pp = xrealloc(y_ptr->y_array, (y_ptr->y_size + newlines) * sizeof(char *));
+ String *pp = xrealloc(y_ptr->y_array, (y_ptr->y_size + newlines) * sizeof(String));
y_ptr->y_array = pp;
size_t lnum = y_ptr->y_size; // The current line number.
@@ -5464,7 +5491,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
if (str_list) {
for (char **ss = (char **)str; *ss != NULL; ss++, lnum++) {
size_t ss_len = strlen(*ss);
- pp[lnum] = xmemdupz(*ss, ss_len);
+ pp[lnum] = cbuf_to_string(*ss, ss_len);
maxlen = MAX(maxlen, ss_len);
}
} else {
@@ -5477,22 +5504,22 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
maxlen = MAX(maxlen, line_len);
// When appending, copy the previous line and free it after.
- size_t extra = append ? strlen(pp[--lnum]) : 0;
+ size_t extra = append ? pp[--lnum].size : 0;
char *s = xmallocz(line_len + extra);
if (extra > 0) {
- memcpy(s, pp[lnum], extra);
+ memcpy(s, pp[lnum].data, extra);
}
memcpy(s + extra, start, line_len);
size_t s_len = extra + line_len;
if (append) {
- xfree(pp[lnum]);
+ xfree(pp[lnum].data);
append = false; // only first line is appended
}
- pp[lnum] = s;
+ pp[lnum] = cbuf_as_string(s, s_len);
// Convert NULs to '\n' to prevent truncation.
- memchrsub(pp[lnum], NUL, '\n', s_len);
+ memchrsub(pp[lnum].data, NUL, '\n', s_len);
}
}
y_ptr->y_type = yank_type;
@@ -6783,7 +6810,7 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
{
- if (reg->y_size > 0 && strlen(reg->y_array[reg->y_size - 1]) == 0) {
+ if (reg->y_size > 0 && reg->y_array[reg->y_size - 1].size == 0) {
// a known-to-be charwise yank might have a final linebreak
// but otherwise there is no line after the final newline
if (reg->y_type != kMTCharWise) {
@@ -6803,7 +6830,7 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
if (reg->y_type == kMTBlockWise) {
size_t maxlen = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- size_t rowlen = strlen(reg->y_array[i]);
+ size_t rowlen = reg->y_array[i].size;
maxlen = MAX(maxlen, rowlen);
}
assert(maxlen <= INT_MAX);
@@ -6873,7 +6900,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown;
}
- reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char *));
+ reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(String));
reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL;
reg->timestamp = 0;
@@ -6885,14 +6912,15 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err;
}
- reg->y_array[tv_idx++] = xstrdupnul(TV_LIST_ITEM_TV(li)->vval.v_string);
+ const char *s = TV_LIST_ITEM_TV(li)->vval.v_string;
+ reg->y_array[tv_idx++] = cstr_to_string(s != NULL ? s : "");
});
- if (reg->y_size > 0 && strlen(reg->y_array[reg->y_size - 1]) == 0) {
+ if (reg->y_size > 0 && reg->y_array[reg->y_size - 1].size == 0) {
// a known-to-be charwise yank might have a final linebreak
// but otherwise there is no line after the final newline
if (reg->y_type != kMTCharWise) {
- xfree(reg->y_array[reg->y_size - 1]);
+ xfree(reg->y_array[reg->y_size - 1].data);
reg->y_size--;
if (reg->y_type == kMTUnknown) {
reg->y_type = kMTLineWise;
@@ -6907,7 +6935,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (reg->y_type == kMTBlockWise) {
size_t maxlen = 0;
for (size_t i = 0; i < reg->y_size; i++) {
- size_t rowlen = strlen(reg->y_array[i]);
+ size_t rowlen = reg->y_array[i].size;
maxlen = MAX(maxlen, rowlen);
}
assert(maxlen <= INT_MAX);
@@ -6920,7 +6948,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
err:
if (reg->y_array) {
for (size_t i = 0; i < reg->y_size; i++) {
- xfree(reg->y_array[i]);
+ xfree(reg->y_array[i].data);
}
xfree(reg->y_array);
}
@@ -6944,7 +6972,7 @@ static void set_clipboard(int name, yankreg_T *reg)
list_T *const lines = tv_list_alloc((ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
- tv_list_append_string(lines, reg->y_array[i], -1);
+ tv_list_append_string(lines, reg->y_array[i].data, -1);
}
char regtype;
@@ -7028,7 +7056,7 @@ static inline bool reg_empty(const yankreg_T *const reg)
|| reg->y_size == 0
|| (reg->y_size == 1
&& reg->y_type == kMTCharWise
- && *(reg->y_array[0]) == NUL));
+ && reg->y_array[0].size == 0));
}
/// Iterate over global registers.
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 3cca52572f..35ee1e76dd 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -111,7 +111,7 @@ enum GRegFlags {
/// Definition of one register
typedef struct {
- char **y_array; ///< Pointer to an array of line pointers.
+ String *y_array; ///< Pointer to an array of Strings.
size_t y_size; ///< Number of lines in y_array.
MotionType y_type; ///< Register type
colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise).
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ff0c0e2acf..bb86d10425 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -176,7 +176,7 @@ static int p_paste_dep_opts[] = {
void set_init_tablocal(void)
{
// susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal!
- p_ch = options[kOptCmdheight].def_val.number;
+ p_ch = options[kOptCmdheight].def_val.data.number;
}
/// Initialize the 'shell' option to a default value.
@@ -291,8 +291,8 @@ static void set_init_default_cdpath(void)
}
}
buf[j] = NUL;
- options[kOptCdpath].def_val.string = buf;
- options[kOptCdpath].flags |= P_DEF_ALLOCED;
+ change_option_default(kOptCdpath, CSTR_AS_OPTVAL(buf));
+
xfree(cdpath);
}
@@ -301,29 +301,22 @@ static void set_init_default_cdpath(void)
/// only happen for non-indirect options.
/// Also set the default to the expanded value, so ":set" does not list
/// them.
-/// Don't set the P_ALLOCED flag, because we don't want to free the
-/// default.
static void set_init_expand_env(void)
{
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
vimoption_T *opt = &options[opt_idx];
- if (opt->flags & P_NO_DEF_EXP) {
+ if (opt->flags & kOptFlagNoDefExp) {
continue;
}
char *p;
- if ((opt->flags & P_GETTEXT) && opt->var != NULL) {
+ if ((opt->flags & kOptFlagGettext) && opt->var != NULL) {
p = _(*(char **)opt->var);
} else {
p = option_expand(opt_idx, NULL);
}
if (p != NULL) {
- p = xstrdup(p);
- *(char **)opt->var = p;
- if (opt->flags & P_DEF_ALLOCED) {
- xfree(opt->def_val.string);
- }
- opt->def_val.string = p;
- opt->flags |= P_DEF_ALLOCED;
+ set_option_varp(opt_idx, opt->var, CSTR_TO_OPTVAL(p), true);
+ change_option_default(opt_idx, CSTR_TO_OPTVAL(p));
}
}
}
@@ -354,6 +347,9 @@ void set_init_1(bool clean_arg)
{
langmap_init();
+ // Allocate the default option values.
+ alloc_options_default();
+
set_init_default_shell();
set_init_default_backupskip();
set_init_default_cdpath();
@@ -430,71 +426,73 @@ void set_init_1(bool clean_arg)
set_helplang_default(get_mess_lang());
}
-/// Set an option to its default value.
-/// This does not take care of side effects!
+/// Get default value for option, based on the option's type and scope.
///
-/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL.
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags.
///
-/// TODO(famiu): Refactor this when def_val uses OptVal.
-static void set_option_default(const OptIndex opt_idx, int opt_flags)
+/// @return Default value of option for the scope specified in opt_flags.
+static OptVal get_option_default(const OptIndex opt_idx, int opt_flags)
{
- bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
-
- // pointer to variable for current option
vimoption_T *opt = &options[opt_idx];
- void *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags);
- uint32_t flags = opt->flags;
- if (varp != NULL) { // skip hidden option, nothing to do for it
- if (option_has_type(opt_idx, kOptValTypeString)) {
- // Use set_option_direct() for local options to handle freeing and allocating the value.
- if (opt->indir != PV_NONE) {
- set_option_direct(opt_idx, CSTR_AS_OPTVAL(opt->def_val.string), opt_flags, 0);
- } else {
- if (flags & P_ALLOCED) {
- free_string_option(*(char **)(varp));
- }
- *(char **)varp = opt->def_val.string;
- opt->flags &= ~P_ALLOCED;
- }
- } else if (option_has_type(opt_idx, kOptValTypeNumber)) {
- if (opt->indir == PV_SCROLL) {
- win_comp_scroll(curwin);
- } else {
- OptInt def_val = opt->def_val.number;
- if ((OptInt *)varp == &curwin->w_p_so
- || (OptInt *)varp == &curwin->w_p_siso) {
- // 'scrolloff' and 'sidescrolloff' local values have a
- // different default value than the global default.
- *(OptInt *)varp = -1;
- } else {
- *(OptInt *)varp = def_val;
- }
- // May also set global value for local option.
- if (both) {
- *(OptInt *)get_varp_scope(opt, OPT_GLOBAL) = def_val;
- }
- }
- } else { // boolean
- *(int *)varp = opt->def_val.boolean;
+ bool is_global_local_option = option_is_global_local(opt_idx);
+
#ifdef UNIX
- // 'modeline' defaults to off for root
- if (opt->indir == PV_ML && getuid() == ROOT_UID) {
- *(int *)varp = false;
- }
+ if (opt_idx == kOptModeline && getuid() == ROOT_UID) {
+ // 'modeline' defaults to off for root.
+ return BOOLEAN_OPTVAL(false);
+ }
#endif
- // May also set global value for local option.
- if (both) {
- *(int *)get_varp_scope(opt, OPT_GLOBAL) =
- *(int *)varp;
- }
- }
- // The default value is not insecure.
- uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
- *flagsp = *flagsp & ~P_INSECURE;
+ if ((opt_flags & OPT_LOCAL) && is_global_local_option) {
+ // Use unset local value instead of default value for local scope of global-local options.
+ return get_option_unset_value(opt_idx);
+ } else if (option_has_type(opt_idx, kOptValTypeString) && !(opt->flags & kOptFlagNoDefExp)) {
+ // For string options, expand environment variables and ~ since the default value was already
+ // expanded, only required when an environment variable was set later.
+ char *s = option_expand(opt_idx, opt->def_val.data.string.data);
+ return s == NULL ? opt->def_val : CSTR_AS_OPTVAL(s);
+ } else {
+ return opt->def_val;
}
+}
- set_option_sctx(opt_idx, opt_flags, current_sctx);
+/// Allocate the default values for all options by copying them from the stack.
+/// This ensures that we don't need to always check if the option default is allocated or not.
+static void alloc_options_default(void)
+{
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
+ options[opt_idx].def_val = optval_copy(options[opt_idx].def_val);
+ }
+}
+
+/// Change the default value for an option.
+///
+/// @param opt_idx Option index in options[] table.
+/// @param value New default value. Must be allocated.
+static void change_option_default(const OptIndex opt_idx, OptVal value)
+{
+ optval_free(options[opt_idx].def_val);
+ options[opt_idx].def_val = value;
+}
+
+/// Set an option to its default value.
+/// This does not take care of side effects!
+///
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags.
+static void set_option_default(const OptIndex opt_idx, int opt_flags)
+{
+ OptVal def_val = get_option_default(opt_idx, opt_flags);
+ set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid);
+
+ if (opt_idx == kOptScroll) {
+ win_comp_scroll(curwin);
+ }
+
+ // The default value is not insecure.
+ uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags);
+ *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure;
}
/// Set all options (except terminal options) to their default value.
@@ -502,8 +500,8 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags)
/// @param opt_flags Option flags.
static void set_options_default(int opt_flags)
{
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
- if (!(options[opt_idx].flags & P_NODEFAULT)) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
+ if (!(options[opt_idx].flags & kOptFlagNoDefault)) {
set_option_default(opt_idx, opt_flags);
}
}
@@ -522,20 +520,13 @@ static void set_options_default(int opt_flags)
/// @param opt_idx Option index in options[] table.
/// @param val The value of the option.
/// @param allocated If true, do not copy default as it was already allocated.
+///
+/// TODO(famiu): Remove this.
static void set_string_default(OptIndex opt_idx, char *val, bool allocated)
FUNC_ATTR_NONNULL_ALL
{
- if (opt_idx == kOptInvalid) {
- return;
- }
-
- vimoption_T *opt = &options[opt_idx];
- if (opt->flags & P_DEF_ALLOCED) {
- xfree(opt->def_val.string);
- }
-
- opt->def_val.string = allocated ? val : xstrdup(val);
- opt->flags |= P_DEF_ALLOCED;
+ assert(opt_idx != kOptInvalid);
+ change_option_default(opt_idx, CSTR_AS_OPTVAL(allocated ? val : xstrdup(val)));
}
/// For an option value that contains comma separated items, find "newval" in
@@ -551,9 +542,9 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
int bs = 0;
for (char *s = origval; *s != NUL; s++) {
- if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1)))
+ if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1)))
&& strncmp(s, newval, newvallen) == 0
- && (!(flags & P_COMMA) || s[newvallen] == ',' || s[newvallen] == NUL)) {
+ && (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) {
return s;
}
// Count backslashes. Only a comma with an even number of backslashes
@@ -569,35 +560,28 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
return NULL;
}
-/// Set the Vi-default value of a number option.
-/// Used for 'lines' and 'columns'.
-void set_number_default(OptIndex opt_idx, OptInt val)
-{
- if (opt_idx != kOptInvalid) {
- options[opt_idx].def_val.number = val;
- }
-}
-
#if defined(EXITFREE)
/// Free all options.
void free_all_options(void)
{
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
- if (options[opt_idx].indir == PV_NONE) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
+ bool hidden = is_option_hidden(opt_idx);
+
+ if (option_is_global_only(opt_idx) || hidden) {
// global option: free value and default value.
- if ((options[opt_idx].flags & P_ALLOCED) && options[opt_idx].var != NULL) {
+ // hidden option: free default value only.
+ if (!hidden) {
optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
}
- if (options[opt_idx].flags & P_DEF_ALLOCED) {
- optval_free(optval_from_varp(opt_idx, &options[opt_idx].def_val));
- }
- } else if (options[opt_idx].var != VAR_WIN) {
- // buffer-local option: free global value
+ } else if (!option_is_window_local(opt_idx)) {
+ // buffer-local option: free global value.
optval_free(optval_from_varp(opt_idx, options[opt_idx].var));
}
+ optval_free(options[opt_idx].def_val);
}
free_operatorfunc_option();
free_tagfunc_option();
+ free_findfunc_option();
XFREE_CLEAR(fenc_default);
XFREE_CLEAR(p_term);
XFREE_CLEAR(p_ttytype);
@@ -612,7 +596,7 @@ void set_init_2(bool headless)
// 'scroll' defaults to half the window height. The stored default is zero,
// which results in the actual value computed from the window height.
- if (!(options[kOptScroll].flags & P_WAS_SET)) {
+ if (!(options[kOptScroll].flags & kOptFlagWasSet)) {
set_option_default(kOptScroll, OPT_LOCAL);
}
comp_col();
@@ -622,7 +606,7 @@ void set_init_2(bool headless)
if (!option_was_set(kOptWindow)) {
p_window = Rows - 1;
}
- set_number_default(kOptWindow, Rows - 1);
+ change_option_default(kOptWindow, NUMBER_OPTVAL(Rows - 1));
}
/// Initialize the options, part three: After reading the .vimrc
@@ -633,56 +617,40 @@ void set_init_3(void)
// Set 'shellpipe' and 'shellredir', depending on the 'shell' option.
// This is done after other initializations, where 'shell' might have been
// set, but only if they have not been set before.
- bool do_srr = !(options[kOptShellredir].flags & P_WAS_SET);
- bool do_sp = !(options[kOptShellpipe].flags & P_WAS_SET);
+ bool do_srr = !(options[kOptShellredir].flags & kOptFlagWasSet);
+ bool do_sp = !(options[kOptShellpipe].flags & kOptFlagWasSet);
size_t len = 0;
char *p = (char *)invocation_path_tail(p_sh, &len);
p = xmemdupz(p, len);
- {
- //
- // Default for p_sp is "| tee", for p_srr is ">".
- // For known shells it is changed here to include stderr.
- //
- if (path_fnamecmp(p, "csh") == 0
- || path_fnamecmp(p, "tcsh") == 0) {
- if (do_sp) {
- p_sp = "|& tee";
- options[kOptShellpipe].def_val.string = p_sp;
- }
- if (do_srr) {
- p_srr = ">&";
- options[kOptShellredir].def_val.string = p_srr;
- }
- } else if (path_fnamecmp(p, "sh") == 0
- || path_fnamecmp(p, "ksh") == 0
- || path_fnamecmp(p, "mksh") == 0
- || path_fnamecmp(p, "pdksh") == 0
- || path_fnamecmp(p, "zsh") == 0
- || path_fnamecmp(p, "zsh-beta") == 0
- || path_fnamecmp(p, "bash") == 0
- || path_fnamecmp(p, "fish") == 0
- || path_fnamecmp(p, "ash") == 0
- || path_fnamecmp(p, "dash") == 0) {
- // Always use POSIX shell style redirection if we reach this
- if (do_sp) {
- p_sp = "2>&1| tee";
- options[kOptShellpipe].def_val.string = p_sp;
- }
- if (do_srr) {
- p_srr = ">%s 2>&1";
- options[kOptShellredir].def_val.string = p_srr;
- }
+ bool is_csh = path_fnamecmp(p, "csh") == 0 || path_fnamecmp(p, "tcsh") == 0;
+ bool is_known_shell = path_fnamecmp(p, "sh") == 0 || path_fnamecmp(p, "ksh") == 0
+ || path_fnamecmp(p, "mksh") == 0 || path_fnamecmp(p, "pdksh") == 0
+ || path_fnamecmp(p, "zsh") == 0 || path_fnamecmp(p, "zsh-beta") == 0
+ || path_fnamecmp(p, "bash") == 0 || path_fnamecmp(p, "fish") == 0
+ || path_fnamecmp(p, "ash") == 0 || path_fnamecmp(p, "dash") == 0;
+
+ // Default for p_sp is "| tee", for p_srr is ">".
+ // For known shells it is changed here to include stderr.
+ if (is_csh || is_known_shell) {
+ if (do_sp) {
+ const OptVal sp =
+ is_csh ? STATIC_CSTR_AS_OPTVAL("|& tee") : STATIC_CSTR_AS_OPTVAL("2>&1| tee");
+ set_option_direct(kOptShellpipe, sp, 0, SID_NONE);
+ change_option_default(kOptShellpipe, optval_copy(sp));
+ }
+ if (do_srr) {
+ const OptVal srr = is_csh ? STATIC_CSTR_AS_OPTVAL(">&") : STATIC_CSTR_AS_OPTVAL(">%s 2>&1");
+ set_option_direct(kOptShellredir, srr, 0, SID_NONE);
+ change_option_default(kOptShellredir, optval_copy(srr));
}
- xfree(p);
}
+ xfree(p);
if (buf_is_empty(curbuf)) {
- int idx_ffs = find_option("ffs");
-
// Apply the first entry of 'fileformats' to the initial buffer.
- if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) {
+ if (options[kOptFileformats].flags & kOptFlagWasSet) {
set_fileformat(default_fileformat(), OPT_LOCAL);
}
}
@@ -702,13 +670,11 @@ void set_helplang_default(const char *lang)
if (lang_len < 2) { // safety check
return;
}
- if (options[kOptHelplang].flags & P_WAS_SET) {
+ if (options[kOptHelplang].flags & kOptFlagWasSet) {
return;
}
- if (options[kOptHelplang].flags & P_ALLOCED) {
- free_string_option(p_hlg);
- }
+ free_string_option(p_hlg);
p_hlg = xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
if (STRNICMP(p_hlg, "zh_", 3) == 0 && lang_len >= 5) {
@@ -720,7 +686,6 @@ void set_helplang_default(const char *lang)
p_hlg[1] = 'n';
}
p_hlg[2] = NUL;
- options[kOptHelplang].flags |= P_ALLOCED;
}
/// 'title' and 'icon' only default to true if they have not been set or reset
@@ -733,13 +698,13 @@ void set_title_defaults(void)
// If GUI is (going to be) used, we can always set the window title and
// icon name. Saves a bit of time, because the X11 display server does
// not need to be contacted.
- if (!(options[kOptTitle].flags & P_WAS_SET)) {
- options[kOptTitle].def_val.boolean = false;
- p_title = false;
+ if (!(options[kOptTitle].flags & kOptFlagWasSet)) {
+ change_option_default(kOptTitle, BOOLEAN_OPTVAL(false));
+ p_title = 0;
}
- if (!(options[kOptIcon].flags & P_WAS_SET)) {
- options[kOptIcon].def_val.boolean = false;
- p_icon = false;
+ if (!(options[kOptIcon].flags & kOptFlagWasSet)) {
+ change_option_default(kOptIcon, BOOLEAN_OPTVAL(false));
+ p_icon = 0;
}
}
@@ -758,27 +723,6 @@ void ex_set(exarg_T *eap)
do_set(eap->arg, flags);
}
-/// Get the default value for a string option.
-static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags)
-{
- char *newval = options[opt_idx].def_val.string;
- // expand environment variables and ~ since the default value was
- // already expanded, only required when an environment variable was set
- // later
- if (newval == NULL) {
- newval = empty_string_option;
- } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
- char *s = option_expand(opt_idx, newval);
- if (s == NULL) {
- s = newval;
- }
- newval = xstrdup(s);
- } else {
- newval = xstrdup(newval);
- }
- return newval;
-}
-
/// Copy the new string value into allocated memory for the option.
/// Can't use set_option_direct(), because we need to remove the backslashes.
static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
@@ -802,7 +746,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
while (*arg != NUL && !ascii_iswhite(*arg)) {
if (*arg == '\\' && arg[1] != NUL
#ifdef BACKSLASH_IN_FILENAME
- && !((flags & P_EXPAND)
+ && !((flags & kOptFlagExpand)
&& vim_isfilec((uint8_t)arg[1])
&& !ascii_iswhite(arg[1])
&& (arg[1] != '\\'
@@ -851,12 +795,12 @@ static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval,
static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, uint32_t flags)
{
int len = 0;
- int comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL);
+ int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL);
if (op == OP_ADDING) {
len = (int)strlen(origval);
// Strip a trailing comma, would get 2.
if (comma && len > 1
- && (flags & P_ONECOMMA) == P_ONECOMMA
+ && (flags & kOptFlagOneComma) == kOptFlagOneComma
&& origval[len - 1] == ','
&& origval[len - 2] != '\\') {
len--;
@@ -881,7 +825,7 @@ static void stropt_remove_val(char *origval, char *newval, uint32_t flags, char
STRCPY(newval, origval);
if (*strval) {
// may need to remove a comma
- if (flags & P_COMMA) {
+ if (flags & kOptFlagComma) {
if (strval == origval) {
// include comma after string
if (strval[len] == ',') {
@@ -903,8 +847,8 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
char *s = newval;
// Remove flags that appear twice.
for (s = newval; *s;) {
- // if options have P_FLAGLIST and P_ONECOMMA such as 'whichwrap'
- if (flags & P_ONECOMMA) {
+ // if options have kOptFlagFlagList and kOptFlagOneComma such as 'whichwrap'
+ if (flags & kOptFlagOneComma) {
if (*s != ',' && *(s + 1) == ','
&& vim_strchr(s + 2, (uint8_t)(*s)) != NULL) {
// Remove the duplicated value and the next comma.
@@ -912,7 +856,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
continue;
}
} else {
- if ((!(flags & P_COMMA) || *s != ',')
+ if ((!(flags & kOptFlagComma) || *s != ',')
&& vim_strchr(s + 1, (uint8_t)(*s)) != NULL) {
STRMOVE(s, s + 1);
continue;
@@ -922,10 +866,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
}
}
-/// Get the string value specified for a ":set" command. The following set
-/// options are supported:
-/// set {opt}&
-/// set {opt}<
+/// Get the string value specified for a ":set" command. The following set options are supported:
/// set {opt}={val}
/// set {opt}:{val}
static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp,
@@ -936,61 +877,56 @@ static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void
char *save_arg = NULL;
char *newval;
char *s = NULL;
- if (nextchar == '&') { // set to default val
- newval = stropt_get_default_val(opt_idx, flags);
- } else if (nextchar == '<') { // set to global val
- newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
- } else {
- arg++; // jump to after the '=' or ':'
- // Set 'keywordprg' to ":help" if an empty
- // value was passed to :set by the user.
- if (varp == &p_kp && (*arg == NUL || *arg == ' ')) {
- save_arg = arg;
- arg = ":help";
- }
+ arg++; // jump to after the '=' or ':'
- // Copy the new string into allocated memory.
- newval = stropt_copy_value(origval, &arg, op, flags);
+ // Set 'keywordprg' to ":help" if an empty
+ // value was passed to :set by the user.
+ if (varp == &p_kp && (*arg == NUL || *arg == ' ')) {
+ save_arg = arg;
+ arg = ":help";
+ }
- // Expand environment variables and ~.
- // Don't do it when adding without inserting a comma.
- if (op == OP_NONE || (flags & P_COMMA)) {
- newval = stropt_expand_envvar(opt_idx, origval, newval, op);
- }
+ // Copy the new string into allocated memory.
+ newval = stropt_copy_value(origval, &arg, op, flags);
- // locate newval[] in origval[] when removing it
- // and when adding to avoid duplicates
- int len = 0;
- if (op == OP_REMOVING || (flags & P_NODUP)) {
- len = (int)strlen(newval);
- s = find_dup_item(origval, newval, (size_t)len, flags);
+ // Expand environment variables and ~.
+ // Don't do it when adding without inserting a comma.
+ if (op == OP_NONE || (flags & kOptFlagComma)) {
+ newval = stropt_expand_envvar(opt_idx, origval, newval, op);
+ }
- // do not add if already there
- if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
- op = OP_NONE;
- STRCPY(newval, origval);
- }
+ // locate newval[] in origval[] when removing it
+ // and when adding to avoid duplicates
+ int len = 0;
+ if (op == OP_REMOVING || (flags & kOptFlagNoDup)) {
+ len = (int)strlen(newval);
+ s = find_dup_item(origval, newval, (size_t)len, flags);
- // if no duplicate, move pointer to end of original value
- if (s == NULL) {
- s = origval + (int)strlen(origval);
- }
+ // do not add if already there
+ if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
+ op = OP_NONE;
+ STRCPY(newval, origval);
}
- // concatenate the two strings; add a ',' if needed
- if (op == OP_ADDING || op == OP_PREPENDING) {
- stropt_concat_with_comma(origval, newval, op, flags);
- } else if (op == OP_REMOVING) {
- // Remove newval[] from origval[]. (Note: "len" has been set above
- // and is used here).
- stropt_remove_val(origval, newval, flags, s, len);
+ // if no duplicate, move pointer to end of original value
+ if (s == NULL) {
+ s = origval + (int)strlen(origval);
}
+ }
- if (flags & P_FLAGLIST) {
- // Remove flags that appear twice.
- stropt_remove_dupflags(newval, flags);
- }
+ // concatenate the two strings; add a ',' if needed
+ if (op == OP_ADDING || op == OP_PREPENDING) {
+ stropt_concat_with_comma(origval, newval, op, flags);
+ } else if (op == OP_REMOVING) {
+ // Remove newval[] from origval[]. (Note: "len" has been set above
+ // and is used here).
+ stropt_remove_val(origval, newval, flags, s, len);
+ }
+
+ if (flags & kOptFlagFlagList) {
+ // Remove flags that appear twice.
+ stropt_remove_dupflags(newval, flags);
}
if (save_arg != NULL) {
@@ -1041,38 +977,35 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_
// Skip all options that are not window-local (used when showing
// an already loaded buffer in a window).
- if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || options[opt_idx].var != VAR_WIN)) {
+ if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || !option_is_window_local(opt_idx))) {
return FAIL;
}
// Skip all options that are window-local (used for :vimgrep).
- if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && options[opt_idx].var == VAR_WIN) {
+ if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && option_is_window_local(opt_idx)) {
return FAIL;
}
// Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
- if (flags & (P_SECURE | P_NO_ML)) {
+ if (flags & (kOptFlagSecure | kOptFlagNoML)) {
*errmsg = e_not_allowed_in_modeline;
return FAIL;
}
- if ((flags & P_MLE) && !p_mle) {
+ if ((flags & kOptFlagMLE) && !p_mle) {
*errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
return FAIL;
}
// In diff mode some options are overruled. This avoids that
// 'foldmethod' becomes "marker" instead of "diff" and that
// "wrap" gets set.
- if (win->w_p_diff
- && opt_idx != kOptInvalid // shut up coverity warning
- && (options[opt_idx].indir == PV_FDM
- || options[opt_idx].indir == PV_WRAP)) {
+ if (win->w_p_diff && (opt_idx == kOptFoldmethod || opt_idx == kOptWrap)) {
return FAIL;
}
}
// Disallow changing some options in the sandbox
- if (sandbox != 0 && (flags & P_SECURE)) {
+ if (sandbox != 0 && (flags & kOptFlagSecure)) {
*errmsg = e_sandbox;
return FAIL;
}
@@ -1152,6 +1085,7 @@ const char *find_option_end(const char *arg, OptIndex *opt_idxp)
}
/// Get new option value from argp. Allocated OptVal must be freed by caller.
+/// Can unset local value of an option when ":set {option}<" is used.
static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T prefix, char **argp,
int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
const size_t errbuflen, const char **errmsg)
@@ -1162,10 +1096,24 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
vimoption_T *opt = &options[opt_idx];
char *arg = *argp;
// When setting the local value of a global option, the old value may be the global value.
- const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL);
+ const bool oldval_is_global = option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL);
OptVal oldval = optval_from_varp(opt_idx, oldval_is_global ? get_varp(opt) : varp);
OptVal newval = NIL_OPTVAL;
+ if (nextchar == '&') {
+ // ":set opt&": Reset to default value.
+ // NOTE: Use OPT_GLOBAL instead of opt_flags to ensure we don't use the unset local value for
+ // global-local options when OPT_LOCAL is used.
+ return optval_copy(get_option_default(opt_idx, OPT_GLOBAL));
+ } else if (nextchar == '<') {
+ // ":set opt<": Reset to global value.
+ // ":setlocal opt<": Copy global value to local value.
+ if (option_is_global_local(opt_idx) && !(opt_flags & OPT_LOCAL)) {
+ unset_option_local_value(opt_idx);
+ }
+ return get_option_value(opt_idx, OPT_GLOBAL);
+ }
+
switch (oldval.type) {
case kOptValTypeNil:
abort();
@@ -1173,8 +1121,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
TriState newval_bool;
// ":set opt!": invert
- // ":set opt&": reset to default value
- // ":set opt<": reset to global value
if (nextchar == '!') {
switch (oldval.data.boolean) {
case kNone:
@@ -1187,15 +1133,6 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
newval_bool = kTrue;
break;
}
- } else if (nextchar == '&') {
- newval_bool = TRISTATE_FROM_INT(options[opt_idx].def_val.boolean);
- } else if (nextchar == '<') {
- // For 'autoread', kNone means to use global value.
- if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL) {
- newval_bool = kNone;
- } else {
- newval_bool = TRISTATE_FROM_INT(*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
- }
} else {
// ":set invopt": invert
// ":set opt" or ":set noopt": set or reset
@@ -1214,31 +1151,15 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
OptInt newval_num;
// Different ways to set a number option:
- // & set to default value
- // < set to global value
// <xx> accept special key codes for 'wildchar' or 'wildcharm'
// ^x accept ctrl key codes for 'wildchar' or 'wildcharm'
// c accept any non-digit for 'wildchar' or 'wildcharm'
// [-]0-9 set number
// other error
arg++;
- if (nextchar == '&') {
- newval_num = options[opt_idx].def_val.number;
- } else if (nextchar == '<') {
- if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
- // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global newval_num
- newval_num = NO_LOCAL_UNDOLEVEL;
- } else if (opt_flags == OPT_LOCAL
- && ((OptInt *)varp == &curwin->w_p_siso || (OptInt *)varp == &curwin->w_p_so)) {
- // for 'scrolloff'/'sidescrolloff' -1 means using the global newval_num
- newval_num = -1;
- } else {
- newval_num = *(OptInt *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
- } else if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
- && (*arg == '<' || *arg == '^'
- || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
- && !ascii_isdigit(*arg)))) {
+ if (((OptInt *)varp == &p_wc || (OptInt *)varp == &p_wcm)
+ && (*arg == '<' || *arg == '^'
+ || (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1])) && !ascii_isdigit(*arg)))) {
newval_num = string_to_key(arg);
if (newval_num == 0) {
*errmsg = e_invarg;
@@ -1318,21 +1239,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
}
uint8_t nextchar = (uint8_t)(*p); // next non-white char after option name
- uint32_t flags = 0; // flags for current option
- void *varp = NULL; // pointer to variable for current option
-
- 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("=:!&<", nextchar) == NULL
- && (!option_has_type(opt_idx, kOptValTypeBoolean) || nextchar == '?')) {
- *errmsg = e_unsupportedoption;
- }
- return;
- }
-
- flags = options[opt_idx].flags;
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ // flags for current option
+ uint32_t flags = options[opt_idx].flags;
+ // pointer to variable for current option
+ void *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
if (validate_opt_idx(curwin, opt_idx, opt_flags, flags, prefix, errmsg) == FAIL) {
return;
@@ -1372,10 +1282,10 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
// Mention where the option was last set.
if (varp == options[opt_idx].var) {
option_last_set_msg(options[opt_idx].last_set);
- } else if ((int)options[opt_idx].indir & PV_WIN) {
- option_last_set_msg(curwin->w_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]);
- } else if ((int)options[opt_idx].indir & PV_BUF) {
- option_last_set_msg(curbuf->b_p_script_ctx[(int)options[opt_idx].indir & PV_MASK]);
+ } else if (option_has_scope(opt_idx, kOptScopeWin)) {
+ option_last_set_msg(curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)]);
+ } else if (option_has_scope(opt_idx, kOptScopeBuf)) {
+ option_last_set_msg(curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)]);
}
}
@@ -1402,11 +1312,6 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
}
}
- // Don't try to change hidden option.
- if (varp == NULL) {
- return;
- }
-
OptVal newval = get_option_newval(opt_idx, opt_flags, prefix, argp, nextchar, op, flags, varp,
errbuf, errbuflen, errmsg);
@@ -1414,8 +1319,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
return;
}
- *errmsg = set_option(opt_idx, varp, newval, opt_flags, 0, false, op == OP_NONE, errbuf,
- errbuflen);
+ *errmsg = set_option(opt_idx, newval, opt_flags, 0, false, op == OP_NONE, errbuf, errbuflen);
}
/// Parse 'arg' for option settings.
@@ -1664,7 +1568,7 @@ char *find_shada_parameter(int type)
static char *option_expand(OptIndex opt_idx, char *val)
{
// if option doesn't need expansion nothing to do
- if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) {
+ if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
return NULL;
}
@@ -1705,7 +1609,7 @@ static void didset_options(void)
spell_check_msm();
spell_check_sps();
compile_cap_prog(curwin->w_s);
- did_set_spell_option(true);
+ did_set_spell_option();
// set cedit_key
did_set_cedit(NULL);
// initialize the table for 'breakat'.
@@ -1736,7 +1640,7 @@ static void didset_options2(void)
/// Check for string options that are NULL (normally only termcap options).
void check_options(void)
{
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
if ((option_has_type(opt_idx, kOptValTypeString)) && options[opt_idx].var != NULL) {
check_string_option((char **)get_varp(&(options[opt_idx])));
}
@@ -1755,10 +1659,10 @@ int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags)
assert(opt_idx != kOptInvalid);
uint32_t *flagp = insecure_flag(wp, opt_idx, opt_flags);
- return (*flagp & P_INSECURE) != 0;
+ return (*flagp & kOptFlagInsecure) != 0;
}
-/// Get a pointer to the flags used for the P_INSECURE flag of option
+/// Get a pointer to the flags used for the kOptFlagInsecure flag of option
/// "opt_idx". For some local options a local flags field is used.
/// NOTE: Caller must make sure that "wp" is set to the window from which
/// the option is used.
@@ -1766,24 +1670,25 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags)
{
if (opt_flags & OPT_LOCAL) {
assert(wp != NULL);
- switch ((int)options[opt_idx].indir) {
- case PV_STL:
+ switch (opt_idx) {
+ case kOptStatusline:
return &wp->w_p_stl_flags;
- case PV_WBR:
+ case kOptWinbar:
return &wp->w_p_wbr_flags;
- case PV_FDE:
+ case kOptFoldexpr:
return &wp->w_p_fde_flags;
- case PV_FDT:
+ case kOptFoldtext:
return &wp->w_p_fdt_flags;
- case PV_INDE:
+ case kOptIndentexpr:
return &wp->w_buffer->b_p_inde_flags;
- case PV_FEX:
+ case kOptFormatexpr:
return &wp->w_buffer->b_p_fex_flags;
- case PV_INEX:
+ case kOptIncludeexpr:
return &wp->w_buffer->b_p_inex_flags;
+ default:
+ break;
}
}
-
// Nothing special, return global flags field.
return &options[opt_idx].flags;
}
@@ -1816,12 +1721,22 @@ void check_blending(win_T *wp)
}
/// Handle setting `winhighlight' in window "wp"
-bool parse_winhl_opt(win_T *wp)
+///
+/// @param winhl when NULL: use "wp->w_p_winhl"
+/// @param wp when NULL: only parse "winhl"
+///
+/// @return whether the option value is valid.
+bool parse_winhl_opt(const char *winhl, win_T *wp)
{
- const char *p = wp->w_p_winhl;
+ const char *p = empty_string_option;
+ if (winhl != NULL) {
+ p = winhl;
+ } else if (wp != NULL) {
+ p = wp->w_p_winhl;
+ }
if (!*p) {
- if (wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
+ if (wp != NULL && wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
wp->w_ns_hl = 0;
wp->w_hl_needs_update = true;
}
@@ -1829,39 +1744,49 @@ bool parse_winhl_opt(win_T *wp)
return true;
}
- if (wp->w_ns_hl_winhl == 0) {
- wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING);
- } else {
- // namespace already exist. invalidate existing items
- DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true);
- dp->hl_valid++;
+ int ns_hl = 0;
+ if (wp != NULL) {
+ if (wp->w_ns_hl_winhl == 0) {
+ wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING);
+ } else {
+ // Namespace already exists. Invalidate existing items.
+ DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true);
+ dp->hl_valid++;
+ }
+ wp->w_ns_hl = wp->w_ns_hl_winhl;
+ ns_hl = wp->w_ns_hl;
}
- wp->w_ns_hl = wp->w_ns_hl_winhl;
- int ns_hl = wp->w_ns_hl;
while (*p) {
- char *colon = strchr(p, ':');
+ const char *colon = strchr(p, ':');
if (!colon) {
return false;
}
size_t nlen = (size_t)(colon - p);
- char *hi = colon + 1;
- char *commap = xstrchrnul(hi, ',');
+ const char *hi = colon + 1;
+ const char *commap = xstrchrnul(hi, ',');
size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
if (hl_id == 0) {
return false;
}
int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
+ if (hl_id_link == 0) {
+ return false;
+ }
- HlAttrs attrs = HLATTRS_INIT;
- attrs.rgb_ae_attr |= HL_GLOBAL;
- ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL);
+ if (wp != NULL) {
+ HlAttrs attrs = HLATTRS_INIT;
+ attrs.rgb_ae_attr |= HL_GLOBAL;
+ ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL);
+ }
p = *commap ? commap + 1 : "";
}
- wp->w_hl_needs_update = true;
+ if (wp != NULL) {
+ wp->w_hl_needs_update = true;
+ }
return true;
}
@@ -1877,7 +1802,6 @@ sctx_T *get_option_sctx(OptIndex opt_idx)
void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx)
{
bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- int indir = (int)options[opt_idx].indir;
nlua_set_sctx(&script_ctx);
LastSet last_set = {
.script_ctx = script_ctx,
@@ -1891,17 +1815,17 @@ void set_option_sctx(OptIndex opt_idx, int opt_flags, sctx_T script_ctx)
// Remember where the option was set. For local options need to do that
// in the buffer or window structure.
- if (both || (opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0) {
+ if (both || (opt_flags & OPT_GLOBAL) || option_is_global_only(opt_idx)) {
options[opt_idx].last_set = last_set;
}
if (both || (opt_flags & OPT_LOCAL)) {
- if (indir & PV_BUF) {
- curbuf->b_p_script_ctx[indir & PV_MASK] = last_set;
- } else if (indir & PV_WIN) {
- curwin->w_p_script_ctx[indir & PV_MASK] = last_set;
+ if (option_has_scope(opt_idx, kOptScopeBuf)) {
+ curbuf->b_p_script_ctx[option_scope_idx(opt_idx, kOptScopeBuf)] = last_set;
+ } else if ((option_has_scope(opt_idx, kOptScopeWin))) {
+ curwin->w_p_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set;
if (both) {
// also setting the "all buffers" value
- curwin->w_allbuf_opt.wo_script_ctx[indir & PV_MASK] = last_set;
+ curwin->w_allbuf_opt.wo_script_ctx[option_scope_idx(opt_idx, kOptScopeWin)] = last_set;
}
}
}
@@ -1959,7 +1883,7 @@ static const char *did_set_arabic(optset_T *args)
// set rightleft mode
if (!win->w_p_rl) {
win->w_p_rl = true;
- changed_window_setting(curwin);
+ changed_window_setting(win);
}
// Enable Arabic shaping (major part of what Arabic requires)
@@ -1974,8 +1898,8 @@ static const char *did_set_arabic(optset_T *args)
if (strcmp(p_enc, "utf-8") != 0) {
static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'");
- msg_source(HL_ATTR(HLF_W));
- msg(_(w_arabic), HL_ATTR(HLF_W));
+ msg_source(HLF_W);
+ msg(_(w_arabic), HLF_W);
set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
}
@@ -1990,7 +1914,7 @@ static const char *did_set_arabic(optset_T *args)
// reset rightleft mode
if (win->w_p_rl) {
win->w_p_rl = false;
- changed_window_setting(curwin);
+ changed_window_setting(win);
}
// 'arabicshape' isn't reset, it is a global option and
@@ -2001,8 +1925,8 @@ static const char *did_set_arabic(optset_T *args)
// window may still want it "on".
// Revert to the default keymap
- curbuf->b_p_iminsert = B_IMODE_NONE;
- curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
+ win->w_buffer->b_p_iminsert = B_IMODE_NONE;
+ win->w_buffer->b_p_imsearch = B_IMODE_USE_INSERT;
}
return errmsg;
@@ -2124,9 +2048,7 @@ static const char *did_set_helpheight(optset_T *args)
{
// Change window height NOW
if (!ONE_WINDOW) {
- buf_T *buf = (buf_T *)args->os_buf;
- win_T *win = (win_T *)args->os_win;
- if (buf->b_help && win->w_height < p_hh) {
+ if (curbuf->b_help && curwin->w_height < p_hh) {
win_setheight((int)p_hh);
}
}
@@ -2278,7 +2200,7 @@ static const char *did_set_number_relativenumber(optset_T *args)
// When 'relativenumber'/'number' is changed and 'statuscolumn' is set, reset width.
win->w_nrwidth_line_count = 0;
}
- check_signcolumn(win);
+ check_signcolumn(NULL, win);
return NULL;
}
@@ -2455,14 +2377,16 @@ static const char *did_set_pumblend(optset_T *args FUNC_ATTR_UNUSED)
/// Process the updated 'readonly' option value.
static const char *did_set_readonly(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
+
// when 'readonly' is reset globally, also reset readonlymode
- if (!curbuf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) {
+ if (!buf->b_p_ro && (args->os_flags & OPT_LOCAL) == 0) {
readonlymode = false;
}
// when 'readonly' is set may give W10 again
- if (curbuf->b_p_ro) {
- curbuf->b_did_warn = false;
+ if (buf->b_p_ro) {
+ buf->b_did_warn = false;
}
redraw_titles();
@@ -2578,8 +2502,7 @@ static const char *did_set_swapfile(optset_T *args)
if (buf->b_p_swf && p_uc) {
ml_open_file(buf); // create the swap file
} else {
- // no need to reset curbuf->b_may_swap, ml_open_file() will check
- // buf->b_p_swf
+ // no need to reset buf->b_may_swap, ml_open_file() will check buf->b_p_swf
mf_close_file(buf, true); // remove the swap file
}
return NULL;
@@ -2589,7 +2512,7 @@ static const char *did_set_swapfile(optset_T *args)
static const char *did_set_textwidth(optset_T *args FUNC_ATTR_UNUSED)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
- check_colorcolumn(wp);
+ check_colorcolumn(NULL, wp);
}
return NULL;
@@ -2619,8 +2542,10 @@ static const char *did_set_titlelen(optset_T *args)
/// Process the updated 'undofile' option value.
static const char *did_set_undofile(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
+
// Only take action when the option was set.
- if (!curbuf->b_p_udf && !p_udf) {
+ if (!buf->b_p_udf && !p_udf) {
return NULL;
}
@@ -2633,7 +2558,7 @@ static const char *did_set_undofile(optset_T *args)
// only for the current buffer: Try to read in the undofile,
// if one exists, the buffer wasn't changed and the buffer was
// loaded
- if ((curbuf == bp
+ if ((buf == bp
|| (args->os_flags & OPT_GLOBAL) || args->os_flags == 0)
&& !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) {
u_compute_hash(bp, hash);
@@ -2673,7 +2598,7 @@ static const char *did_set_undolevels(optset_T *args)
if (pp == &p_ul) { // global 'undolevels'
did_set_global_undolevels(args->os_newval.number, args->os_oldval.number);
- } else if (pp == &curbuf->b_p_ul) { // buffer local 'undolevels'
+ } else if (pp == &buf->b_p_ul) { // buffer local 'undolevels'
did_set_buflocal_undolevels(buf, args->os_newval.number, args->os_oldval.number);
}
@@ -2738,8 +2663,7 @@ static const char *did_set_winheight(optset_T *args)
{
// Change window height NOW
if (!ONE_WINDOW) {
- win_T *win = (win_T *)args->os_win;
- if (win->w_height < p_wh) {
+ if (curwin->w_height < p_wh) {
win_setheight((int)p_wh);
}
}
@@ -2750,9 +2674,7 @@ static const char *did_set_winheight(optset_T *args)
/// Process the new 'winwidth' option value.
static const char *did_set_winwidth(optset_T *args)
{
- win_T *win = (win_T *)args->os_win;
-
- if (!ONE_WINDOW && win->w_width < p_wiw) {
+ if (!ONE_WINDOW && curwin->w_width < p_wiw) {
win_setwidth((int)p_wiw);
}
return NULL;
@@ -2815,15 +2737,14 @@ static void do_spelllang_source(win_T *win)
/// Check the bounds of numeric options.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Pointer to option variable.
/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value.
/// @param[out] errbuf Buffer for error message. Cannot be NULL.
/// @param errbuflen Length of error buffer.
///
/// @return Error message, if any.
-static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt *newval,
- char *errbuf, size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(4)
+static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, char *errbuf,
+ size_t errbuflen)
+ FUNC_ATTR_NONNULL_ARG(3)
{
const char *errmsg = NULL;
@@ -2856,9 +2777,7 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt
}
break;
case kOptScroll:
- if (varp == &(curwin->w_p_scr)
- && (*newval <= 0
- || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0))
+ if ((*newval <= 0 || (*newval > curwin->w_height_inner && curwin->w_height_inner > 0))
&& full_screen) {
if (*newval != 0) {
errmsg = e_scroll;
@@ -2876,13 +2795,12 @@ static const char *check_num_option_bounds(OptIndex opt_idx, void *varp, OptInt
/// Validate and bound check option value.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Pointer to option variable.
/// @param[in,out] newval Pointer to new option value. Will be set to bound checked value.
/// @param[out] errbuf Buffer for error message. Cannot be NULL.
/// @param errbuflen Length of error buffer.
///
/// @return Error message, if any.
-static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *newval, char *errbuf,
+static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *errbuf,
size_t errbuflen)
{
OptInt value = *newval;
@@ -2892,167 +2810,168 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new
return e_invarg;
}
- if (varp == &p_wh) {
+ switch (opt_idx) {
+ case kOptHelpheight:
+ case kOptTitlelen:
+ case kOptUpdatecount:
+ case kOptReport:
+ case kOptUpdatetime:
+ case kOptSidescroll:
+ case kOptFoldlevel:
+ case kOptShiftwidth:
+ case kOptTextwidth:
+ case kOptWritedelay:
+ case kOptTimeoutlen:
+ if (value < 0) {
+ return e_positive;
+ }
+ break;
+ case kOptWinheight:
if (value < 1) {
return e_positive;
} else if (p_wmh > value) {
return e_winheight;
}
- } else if (varp == &p_hh) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_wmh) {
+ break;
+ case kOptWinminheight:
if (value < 0) {
return e_positive;
} else if (value > p_wh) {
return e_winheight;
}
- } else if (varp == &p_wiw) {
+ break;
+ case kOptWinwidth:
if (value < 1) {
return e_positive;
} else if (p_wmw > value) {
return e_winwidth;
}
- } else if (varp == &p_wmw) {
+ break;
+ case kOptWinminwidth:
if (value < 0) {
return e_positive;
} else if (value > p_wiw) {
return e_winwidth;
}
- } else if (varp == &p_titlelen) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_uc) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_ch) {
+ break;
+ case kOptMaxcombine:
+ *newval = MAX_MCO;
+ break;
+ case kOptCmdheight:
if (value < 0) {
return e_positive;
} else {
p_ch_was_zero = value == 0;
}
- } else if (varp == &p_tm) {
+ break;
+ case kOptHistory:
if (value < 0) {
return e_positive;
+ } else if (value > 10000) {
+ return e_invarg;
}
- } else if (varp == &p_hi) {
+ break;
+ case kOptMsghistory:
if (value < 0) {
return e_positive;
} else if (value > 10000) {
return e_invarg;
}
- } else if (varp == &p_pyx) {
+ break;
+ case kOptPyxversion:
if (value == 0) {
*newval = 3;
} else if (value != 3) {
return e_invarg;
}
- } else if (varp == &p_re) {
+ break;
+ case kOptRegexpengine:
if (value < 0 || value > 2) {
return e_invarg;
}
- } else if (varp == &p_report) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_so) {
+ break;
+ case kOptScrolloff:
if (value < 0 && full_screen) {
return e_positive;
}
- } else if (varp == &p_siso) {
+ break;
+ case kOptSidescrolloff:
if (value < 0 && full_screen) {
return e_positive;
}
- } else if (varp == &p_cwh) {
+ break;
+ case kOptCmdwinheight:
if (value < 1) {
return e_positive;
}
- } else if (varp == &p_ut) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_ss) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curwin->w_p_fdl || varp == &curwin->w_allbuf_opt.wo_fdl) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curwin->w_p_cole || varp == &curwin->w_allbuf_opt.wo_cole) {
+ break;
+ case kOptConceallevel:
if (value < 0) {
return e_positive;
} else if (value > 3) {
return e_invarg;
}
- } else if (varp == &curwin->w_p_nuw || varp == &curwin->w_allbuf_opt.wo_nuw) {
+ break;
+ case kOptNumberwidth:
if (value < 1) {
return e_positive;
} else if (value > MAX_NUMBERWIDTH) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_iminsert || varp == &p_iminsert) {
+ break;
+ case kOptIminsert:
if (value < 0 || value > B_IMODE_LAST) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_imsearch || varp == &p_imsearch) {
+ break;
+ case kOptImsearch:
if (value < -1 || value > B_IMODE_LAST) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_channel || varp == &p_channel) {
+ break;
+ case kOptChannel:
return e_invarg;
- } else if (varp == &curbuf->b_p_scbk || varp == &p_scbk) {
+ case kOptScrollback:
if (value < -1 || value > SB_MAX) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_sw || varp == &p_sw) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &curbuf->b_p_ts || varp == &p_ts) {
+ break;
+ case kOptTabstop:
if (value < 1) {
return e_positive;
} else if (value > TABSTOP_MAX) {
return e_invarg;
}
- } else if (varp == &curbuf->b_p_tw || varp == &p_tw) {
- if (value < 0) {
- return e_positive;
- }
- } else if (varp == &p_wd) {
- if (value < 0) {
- return e_positive;
- }
+ break;
+ default:
+ break;
}
- return check_num_option_bounds(opt_idx, varp, newval, errbuf, errbuflen);
+ return check_num_option_bounds(opt_idx, newval, errbuf, errbuflen);
}
/// Called after an option changed: check if something needs to be redrawn.
void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags)
{
- // Careful: P_RALL is a combination of other P_ flags
- bool all = (flags & P_RALL) == P_RALL;
+ // Careful: kOptFlagRedrAll is a combination of other redraw flags
+ bool all = (flags & kOptFlagRedrAll) == kOptFlagRedrAll;
- if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty
+ if ((flags & kOptFlagRedrStat) || all) { // mark all status lines and window bars dirty
status_redraw_all();
}
- if ((flags & P_RTABL) || all) { // mark tablines dirty
+ if ((flags & kOptFlagRedrTabl) || all) { // mark tablines dirty
redraw_tabline = true;
}
- if ((flags & P_RBUF) || (flags & P_RWIN) || all) {
- if (flags & P_HLONLY) {
+ if ((flags & kOptFlagRedrBuf) || (flags & kOptFlagRedrWin) || all) {
+ if (flags & kOptFlagHLOnly) {
redraw_later(win, UPD_NOT_VALID);
} else {
changed_window_setting(win);
}
}
- if (flags & P_RBUF) {
+ if (flags & kOptFlagRedrBuf) {
redraw_buf_later(buf, UPD_NOT_VALID);
}
if (all) {
@@ -3193,11 +3112,25 @@ bool optval_equal(OptVal o1, OptVal o2)
return o1.data.number == o2.data.number;
case kOptValTypeString:
return o1.data.string.size == o2.data.string.size
- && strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size);
+ && (o1.data.string.data == o2.data.string.data
+ || strnequal(o1.data.string.data, o2.data.string.data, o1.data.string.size));
}
UNREACHABLE;
}
+/// Get type of option. Does not support multitype options.
+static OptValType option_get_type(const OptIndex opt_idx)
+{
+ assert(!option_is_multitype(opt_idx));
+
+ // If the option only supports a single type, it means that the index of the option's type flag
+ // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
+ // use that as the option's type.
+ OptValType type = xctz(options[opt_idx].type_flags);
+ assert(type > kOptValTypeNil && type < kOptValTypeSize);
+ return type;
+}
+
/// Create OptVal from var pointer.
///
/// @param opt_idx Option index in options[] table.
@@ -3205,6 +3138,7 @@ bool optval_equal(OptVal o1, OptVal o2)
///
/// @return Option value stored in varp.
OptVal optval_from_varp(OptIndex opt_idx, void *varp)
+ FUNC_ATTR_NONNULL_ARG(2)
{
// Special case: 'modified' is b_changed, but we also want to consider it set when 'ff' or 'fenc'
// changed.
@@ -3214,24 +3148,20 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
if (option_is_multitype(opt_idx)) {
// Multitype options are stored as OptVal.
- return varp == NULL ? NIL_OPTVAL : *(OptVal *)varp;
+ return *(OptVal *)varp;
}
- // If the option only supports a single type, it means that the index of the option's type flag
- // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
- // use that as the option's type.
- OptValType type = xctz(options[opt_idx].type_flags);
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
+ OptValType type = option_get_type(opt_idx);
switch (type) {
case kOptValTypeNil:
return NIL_OPTVAL;
case kOptValTypeBoolean:
- return BOOLEAN_OPTVAL(varp == NULL ? false : TRISTATE_FROM_INT(*(int *)varp));
+ return BOOLEAN_OPTVAL(TRISTATE_FROM_INT(*(int *)varp));
case kOptValTypeNumber:
- return NUMBER_OPTVAL(varp == NULL ? 0 : *(OptInt *)varp);
+ return NUMBER_OPTVAL(*(OptInt *)varp);
case kOptValTypeString:
- return STRING_OPTVAL(varp == NULL ? (String)STRING_INIT : cstr_as_string(*(char **)varp));
+ return STRING_OPTVAL(cstr_as_string(*(char **)varp));
}
UNREACHABLE;
}
@@ -3334,7 +3264,7 @@ OptVal object_as_optval(Object o, bool *error)
/// Get an allocated string containing a list of valid types for an option.
/// For options with a singular type, it returns the name of the type. For options with multiple
/// possible types, it returns a slash separated list of types. For example, if an option can be a
-/// number, boolean or string, the function returns "Number/Boolean/String"
+/// number, boolean or string, the function returns "number/boolean/string"
static char *option_get_valid_types(OptIndex opt_idx)
{
StringBuilder str = KV_INITIAL_VALUE;
@@ -3356,8 +3286,6 @@ static char *option_get_valid_types(OptIndex opt_idx)
// Ensure that the string is NUL-terminated.
kv_push(str, NUL);
return str.items;
-
-#undef OPTION_ADD_TYPE
}
/// Check if option is hidden.
@@ -3367,7 +3295,74 @@ static char *option_get_valid_types(OptIndex opt_idx)
/// @return True if option is hidden, false otherwise. Returns false if option name is invalid.
bool is_option_hidden(OptIndex opt_idx)
{
- return opt_idx == kOptInvalid ? false : get_varp(&options[opt_idx]) == NULL;
+ // Hidden options are always immutable and point to their default value
+ return opt_idx != kOptInvalid && options[opt_idx].immutable
+ && options[opt_idx].var == &options[opt_idx].def_val.data;
+}
+
+/// Check if option is multitype (supports multiple types).
+static bool option_is_multitype(OptIndex opt_idx)
+{
+ const OptTypeFlags type_flags = get_option(opt_idx)->type_flags;
+ assert(type_flags != 0);
+ return !is_power_of_two(type_flags);
+}
+
+/// Check if option supports a specific type.
+bool option_has_type(OptIndex opt_idx, OptValType type)
+{
+ // Ensure that type flags variable can hold all types.
+ STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8,
+ "Option type_flags cannot fit all option types");
+ // Ensure that the type is valid before accessing type_flags.
+ assert(type > kOptValTypeNil && type < kOptValTypeSize);
+ // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in
+ // the type_flags bit field.
+ return get_option(opt_idx)->type_flags & (1 << type);
+}
+
+/// Check if option supports a specific scope.
+bool option_has_scope(OptIndex opt_idx, OptScope scope)
+{
+ // Ensure that scope flags variable can hold all scopes.
+ STATIC_ASSERT(kOptScopeSize <= sizeof(OptScopeFlags) * 8,
+ "Option scope_flags cannot fit all option scopes");
+ // Ensure that the scope is valid before accessing scope_flags.
+ assert(scope >= kOptScopeGlobal && scope < kOptScopeSize);
+ // Bitshift 1 by the value of scope to get the scope's corresponding flag, and check if it's set
+ // in the scope_flags bit field.
+ return get_option(opt_idx)->scope_flags & (1 << scope);
+}
+
+/// Check if option is global-local.
+static inline bool option_is_global_local(OptIndex opt_idx)
+{
+ // Global-local options have at least two types, so their type flag cannot be a power of two.
+ return opt_idx != kOptInvalid && !is_power_of_two(options[opt_idx].scope_flags);
+}
+
+/// Check if option only supports global scope.
+static inline bool option_is_global_only(OptIndex opt_idx)
+{
+ // For an option to be global-only, it has to only have a single scope, which means the scope
+ // flags must be a power of two, and it must have the global scope.
+ return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags)
+ && option_has_scope(opt_idx, kOptScopeGlobal);
+}
+
+/// Check if option only supports window scope.
+static inline bool option_is_window_local(OptIndex opt_idx)
+{
+ // For an option to be window-local it has to only have a single scope, which means the scope
+ // flags must be a power of two, and it must have the window scope.
+ return opt_idx != kOptInvalid && is_power_of_two(options[opt_idx].scope_flags)
+ && option_has_scope(opt_idx, kOptScopeWin);
+}
+
+/// Get option index for scope.
+ssize_t option_scope_idx(OptIndex opt_idx, OptScope scope)
+{
+ return options[opt_idx].scope_idx[scope];
}
/// Get option flags.
@@ -3418,7 +3413,7 @@ static OptVal get_option_unset_value(OptIndex opt_idx)
vimoption_T *opt = &options[opt_idx];
// For global-local options, use the unset value of the local value.
- if (opt->indir & PV_BOTH) {
+ if (option_is_global_local(opt_idx)) {
// String global-local options always use an empty string for the unset value.
if (option_has_type(opt_idx, kOptValTypeString)) {
return STATIC_CSTR_AS_OPTVAL("");
@@ -3450,7 +3445,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx)
vimoption_T *opt = get_option(opt_idx);
// Local value of option that isn't global-local is always considered set.
- if (!((int)opt->indir & PV_BOTH)) {
+ if (!option_is_global_local(opt_idx)) {
return false;
}
@@ -3484,7 +3479,6 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
vimoption_T *opt = &options[opt_idx];
const char *errmsg = NULL;
bool restore_chartab = false;
- bool free_oldval = (opt->flags & P_ALLOCED);
bool value_changed = false;
bool value_checked = false;
@@ -3503,15 +3497,15 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
.os_win = curwin
};
- if (direct || opt->hidden) {
- // Don't do any extra processing if setting directly or if option is hidden.
+ if (direct) {
+ // Don't do any extra processing if setting directly.
}
// Disallow changing immutable options.
else if (opt->immutable && !optval_equal(old_value, new_value)) {
errmsg = e_unsupportedoption;
}
// Disallow changing some options from secure mode.
- else if ((secure || sandbox != 0) && (opt->flags & P_SECURE)) {
+ else if ((secure || sandbox != 0) && (opt->flags & kOptFlagSecure)) {
errmsg = e_secure;
}
// Check for a "normal" directory or file name in some string options.
@@ -3533,7 +3527,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
// If option is hidden or if an error is detected, restore the previous value and don't do any
// further processing.
- if (opt->hidden || errmsg != NULL) {
+ if (errmsg != NULL) {
set_option_varp(opt_idx, varp, old_value, true);
// When resetting some values, need to act on it.
if (restore_chartab) {
@@ -3560,18 +3554,12 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
set_option_sctx(opt_idx, opt_flags, script_ctx);
}
- // Free options that are in allocated memory.
- // Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()).
- if (free_oldval) {
- optval_free(old_value);
- }
- opt->flags |= P_ALLOCED;
+ optval_free(old_value);
const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- const bool opt_is_global_local = opt->indir & PV_BOTH;
if (scope_both) {
- if (opt_is_global_local) {
+ if (option_is_global_local(opt_idx)) {
// Global option with local value set to use global value.
// Free the local value and clear it.
void *varp_local = get_varp_scope(opt, OPT_LOCAL);
@@ -3618,7 +3606,8 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
}
if (curwin->w_curswant != MAXCOL
- && (opt->flags & (P_CURSWANT | P_RALL)) != 0 && (opt->flags & P_HLONLY) == 0) {
+ && (opt->flags & (kOptFlagCurswant | kOptFlagRedrAll)) != 0
+ && (opt->flags & kOptFlagHLOnly) == 0) {
curwin->w_set_curswant = true;
}
@@ -3626,14 +3615,14 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
if (errmsg == NULL) {
uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- opt->flags |= P_WAS_SET;
+ opt->flags |= kOptFlagWasSet;
- // When an option is set in the sandbox, from a modeline or in secure mode set the P_INSECURE
+ // When an option is set in the sandbox, from a modeline or in secure mode set the kOptFlagInsecure
// flag. Otherwise, if a new value is stored reset the flag.
if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) {
- *p |= P_INSECURE;
+ *p |= kOptFlagInsecure;
} else if (value_replaced) {
- *p &= ~P_INSECURE;
+ *p &= ~(unsigned)kOptFlagInsecure;
}
}
@@ -3643,14 +3632,19 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value
/// Validate the new value for an option.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param varp Pointer to option variable.
/// @param newval[in,out] New option value. Might be modified.
-static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval,
- int opt_flags, char *errbuf, size_t errbuflen)
+static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval, int opt_flags,
+ char *errbuf, size_t errbuflen)
{
const char *errmsg = NULL;
vimoption_T *opt = &options[opt_idx];
+ // Always allow unsetting local value of global-local option.
+ if (option_is_global_local(opt_idx) && (opt_flags & OPT_LOCAL)
+ && optval_equal(*newval, get_option_unset_value(opt_idx))) {
+ return NULL;
+ }
+
if (newval->type == kOptValTypeNil) {
// Don't try to unset local value if scope is global.
// TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is
@@ -3658,7 +3652,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
if (opt_flags == OPT_GLOBAL) {
errmsg = _("Cannot unset global option value");
} else {
- optval_free(*newval);
*newval = optval_copy(get_option_unset_value(opt_idx));
}
} else if (!option_has_type(opt_idx, newval->type)) {
@@ -3671,7 +3664,7 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
errmsg = errbuf;
} else if (newval->type == kOptValTypeNumber) {
// Validate and bound check num option values.
- errmsg = validate_num_option(opt_idx, varp, &newval->data.number, errbuf, errbuflen);
+ errmsg = validate_num_option(opt_idx, &newval->data.number, errbuf, errbuflen);
}
return errmsg;
@@ -3680,7 +3673,6 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
/// Set the value of an option using an OptVal.
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
-/// @param[in] varp Option variable pointer, cannot be NULL.
/// @param value New option value. Might get freed.
/// @param opt_flags Option flags.
/// @param set_sid Script ID. Special values:
@@ -3692,35 +3684,35 @@ static const char *validate_option_value(const OptIndex opt_idx, void *varp, Opt
/// @param errbuflen Length of error buffer.
///
/// @return NULL on success, an untranslated error message on error.
-static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags,
- scid_T set_sid, const bool direct, const bool value_replaced,
- char *errbuf, size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(2)
+static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
+ const bool direct, const bool value_replaced, char *errbuf,
+ size_t errbuflen)
{
assert(opt_idx != kOptInvalid);
- const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen);
+ const char *errmsg = NULL;
- if (errmsg != NULL) {
- optval_free(value);
- return errmsg;
+ if (!direct) {
+ errmsg = validate_option_value(opt_idx, &value, opt_flags, errbuf, errbuflen);
+
+ if (errmsg != NULL) {
+ optval_free(value);
+ return errmsg;
+ }
}
vimoption_T *opt = &options[opt_idx];
const bool scope_local = opt_flags & OPT_LOCAL;
const bool scope_global = opt_flags & OPT_GLOBAL;
const bool scope_both = !scope_local && !scope_global;
- const bool opt_is_global_local = opt->indir & PV_BOTH;
// Whether local value of global-local option is unset.
- // NOTE: When this is true, it also implies that opt_is_global_local is true.
+ // NOTE: When this is true, it also implies that the option is global-local.
const bool is_opt_local_unset = is_option_local_value_unset(opt_idx);
// When using ":set opt=val" for a global option with a local value the local value will be reset,
- // use the global value here.
- if (scope_both && opt_is_global_local) {
- varp = opt->var;
- }
-
+ // use the global value in that case.
+ void *varp
+ = scope_both && option_is_global_local(opt_idx) ? opt->var : get_varp_scope(opt, opt_flags);
void *varp_local = get_varp_scope(opt, OPT_LOCAL);
void *varp_global = get_varp_scope(opt, OPT_GLOBAL);
@@ -3749,9 +3741,9 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
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
+ // effects in secure mode. Also when the value was set with the kOptFlagInsecure flag and is not
// completely replaced.
- if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & P_INSECURE))) {
+ if ((opt_flags & OPT_MODELINE) || sandbox != 0 || (!value_replaced && (*p & kOptFlagInsecure))) {
secure = 1;
}
@@ -3768,7 +3760,7 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value,
apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value,
saved_old_local_value, saved_new_value, errmsg);
}
- if (opt->flags & P_UI_OPTION) {
+ if (opt->flags & kOptFlagUIOption) {
ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value));
}
}
@@ -3794,17 +3786,14 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
{
static char errbuf[IOSIZE];
- vimoption_T *opt = get_option(opt_idx);
-
- if (opt->var == NULL) {
+ if (is_option_hidden(opt_idx)) {
return;
}
- const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags);
-
- set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true, errbuf,
- sizeof(errbuf));
+ const char *errmsg = set_option(opt_idx, optval_copy(value), opt_flags, set_sid, true, true,
+ errbuf, sizeof(errbuf));
+ assert(errmsg == NULL);
+ (void)errmsg; // ignore unused warning
}
/// Set option value directly for buffer / window, without processing any side effects.
@@ -3815,10 +3804,10 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
- OptReqScope req_scope, void *const from)
+ OptScope req_scope, void *const from)
{
buf_T *save_curbuf = curbuf;
win_T *save_curwin = curwin;
@@ -3827,15 +3816,15 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T
// side-effects when setting an option directly. Just change the values of curbuf and curwin if
// needed, no need to properly switch the window / buffer.
switch (req_scope) {
- case kOptReqGlobal:
- break;
- case kOptReqBuf:
- curbuf = (buf_T *)from;
+ case kOptScopeGlobal:
break;
- case kOptReqWin:
+ case kOptScopeWin:
curwin = (win_T *)from;
curbuf = curwin->w_buffer;
break;
+ case kOptScopeBuf:
+ curbuf = (buf_T *)from;
+ break;
}
set_option_direct(opt_idx, value, opt_flags, set_sid);
@@ -3859,18 +3848,22 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt
uint32_t flags = options[opt_idx].flags;
// Disallow changing some options in the sandbox
- if (sandbox > 0 && (flags & P_SECURE)) {
+ if (sandbox > 0 && (flags & kOptFlagSecure)) {
return _(e_sandbox);
}
- void *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp == NULL) {
- // hidden option is not changed
- return NULL;
- }
+ return set_option(opt_idx, optval_copy(value), opt_flags, 0, false, true, errbuf, sizeof(errbuf));
+}
- return set_option(opt_idx, varp, optval_copy(value), opt_flags, 0, false, true, errbuf,
- sizeof(errbuf));
+/// Unset the local value of a global-local option.
+///
+/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
+///
+/// @return NULL on success, an untranslated error message on error.
+static inline const char *unset_option_local_value(const OptIndex opt_idx)
+{
+ assert(option_is_global_local(opt_idx));
+ return set_option_value(opt_idx, get_option_unset_value(opt_idx), OPT_LOCAL);
}
/// Set the value of an option. Supports TTY options, unlike set_option_value().
@@ -3918,16 +3911,17 @@ void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_fla
/// Switch current context to get/set option value for window/buffer.
///
/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return true if context was switched, false otherwise.
-static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from,
- Error *err)
+static bool switch_option_context(void *const ctx, OptScope req_scope, void *const from, Error *err)
{
switch (req_scope) {
- case kOptReqWin: {
+ case kOptScopeGlobal:
+ return false;
+ case kOptScopeWin: {
win_T *const win = (win_T *)from;
switchwin_T *const switchwin = (switchwin_T *)ctx;
@@ -3947,7 +3941,7 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *
}
return true;
}
- case kOptReqBuf: {
+ case kOptScopeBuf: {
buf_T *const buf = (buf_T *)from;
aco_save_T *const aco = (aco_save_T *)ctx;
@@ -3957,150 +3951,44 @@ static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *
aucmd_prepbuf(aco, buf);
return true;
}
- case kOptReqGlobal:
- return false;
}
UNREACHABLE;
}
/// Restore context after getting/setting option for window/buffer. See switch_option_context() for
/// params.
-static void restore_option_context(void *const ctx, OptReqScope req_scope)
+static void restore_option_context(void *const ctx, OptScope req_scope)
{
switch (req_scope) {
- case kOptReqWin:
+ case kOptScopeGlobal:
+ break;
+ case kOptScopeWin:
restore_win_noblock((switchwin_T *)ctx, true);
break;
- case kOptReqBuf:
+ case kOptScopeBuf:
aucmd_restbuf((aco_save_T *)ctx);
break;
- case kOptReqGlobal:
- break;
}
}
-/// Get attributes for an option.
-///
-/// @param opt_idx Option index in options[] table.
-///
-/// @return Option attributes.
-/// 0 for hidden or unknown option.
-/// See SOPT_* in option_defs.h for other flags.
-int get_option_attrs(OptIndex opt_idx)
-{
- if (opt_idx == kOptInvalid) {
- return 0;
- }
-
- vimoption_T *opt = get_option(opt_idx);
-
- // Hidden option
- if (opt->var == NULL) {
- return 0;
- }
-
- int attrs = 0;
-
- if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) {
- attrs |= SOPT_GLOBAL;
- }
- if (opt->indir & PV_WIN) {
- attrs |= SOPT_WIN;
- } else if (opt->indir & PV_BUF) {
- attrs |= SOPT_BUF;
- }
-
- return attrs;
-}
-
-/// Check if option has a value in the requested scope.
-///
-/// @param opt_idx Option index in options[] table.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-///
-/// @return true if option has a value in the requested scope, false otherwise.
-static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope)
-{
- if (opt_idx == kOptInvalid) {
- return false;
- }
-
- vimoption_T *opt = get_option(opt_idx);
-
- // Hidden option.
- if (opt->var == NULL) {
- return false;
- }
- // TTY option.
- if (is_tty_option(opt->fullname)) {
- return req_scope == kOptReqGlobal;
- }
-
- switch (req_scope) {
- case kOptReqGlobal:
- return opt->var != VAR_WIN;
- case kOptReqBuf:
- return opt->indir & PV_BUF;
- case kOptReqWin:
- return opt->indir & PV_WIN;
- }
- UNREACHABLE;
-}
-
-/// Get the option value in the requested scope.
-///
-/// @param opt_idx Option index in options[] table.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Pointer to buffer or window for local option value.
-/// @param[out] err Error message, if any.
-///
-/// @return Option value in the requested scope. Returns a Nil option value if option is not found,
-/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or
-/// buffer-local value depending on opt_scope).
-OptVal get_option_value_strict(OptIndex opt_idx, OptReqScope req_scope, void *from, Error *err)
-{
- if (opt_idx == kOptInvalid || !option_has_scope(opt_idx, req_scope)) {
- return NIL_OPTVAL;
- }
-
- vimoption_T *opt = get_option(opt_idx);
- switchwin_T switchwin;
- aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
- if (ERROR_SET(err)) {
- return NIL_OPTVAL;
- }
-
- char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL);
- OptVal retv = optval_from_varp(opt_idx, varp);
-
- if (switched) {
- restore_option_context(ctx, req_scope);
- }
-
- return retv;
-}
-
/// Get option value for buffer / window.
///
/// @param opt_idx Option index in options[] table.
-/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL).
+/// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL).
/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param[out] hidden Whether option is hidden.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return Option value. Must be freed by caller.
-OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_scope,
- void *const from, Error *err)
+OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scope, void *const from,
+ Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
+ void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
+ : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
bool switched = switch_option_context(ctx, req_scope, from, err);
if (ERROR_SET(err)) {
@@ -4122,17 +4010,17 @@ OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptReqScope req_s
/// @param opt_idx Option index in options[] table.
/// @param[in] value Option value.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
+/// @param req_scope Requested option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags,
- const OptReqScope req_scope, void *const from, Error *err)
+ const OptScope req_scope, void *const from, Error *err)
FUNC_ATTR_NONNULL_ARG(1)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
+ void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
+ : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
bool switched = switch_option_context(ctx, req_scope, from, err);
if (ERROR_SET(err)) {
@@ -4177,7 +4065,7 @@ static void showoptions(bool all, int opt_flags)
// collect the items in items[]
int item_count = 0;
vimoption_T *opt;
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
opt = &options[opt_idx];
// apply :filter /pat/
if (message_filtered(opt->fullname)) {
@@ -4186,7 +4074,7 @@ static void showoptions(bool all, int opt_flags)
void *varp = NULL;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
- if (opt->indir != PV_NONE) {
+ if (!option_is_global_only(opt_idx)) {
varp = get_varp_scope(opt, opt_flags);
}
} else {
@@ -4247,13 +4135,13 @@ static int optval_default(OptIndex opt_idx, void *varp)
{
vimoption_T *opt = &options[opt_idx];
- // Hidden or immutable options always use their default value.
- if (varp == NULL || opt->hidden || opt->immutable) {
+ // Hidden options always use their default value.
+ if (is_option_hidden(opt_idx)) {
return true;
}
OptVal current_val = optval_from_varp(opt_idx, varp);
- OptVal default_val = optval_from_varp(opt_idx, &opt->def_val);
+ OptVal default_val = opt->def_val;
return optval_equal(current_val, default_val);
}
@@ -4261,9 +4149,9 @@ static int optval_default(OptIndex opt_idx, void *varp)
/// Send update to UIs with values of UI relevant options
void ui_refresh_options(void)
{
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
uint32_t flags = options[opt_idx].flags;
- if (!(flags & P_UI_OPTION)) {
+ if (!(flags & kOptFlagUIOption)) {
continue;
}
String name = cstr_as_string(options[opt_idx].fullname);
@@ -4303,7 +4191,7 @@ static void showoneopt(vimoption_T *opt, int opt_flags)
msg_putchar('=');
// put value string in NameBuff
option_value2string(opt, opt_flags);
- msg_outtrans(NameBuff, 0);
+ msg_outtrans(NameBuff, 0, false);
}
silent_mode = save_silent;
@@ -4338,22 +4226,22 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// - Hidden options.
//
// Do the loop over "options[]" twice: once for options with the
- // P_PRI_MKRC flag and once without.
+ // kOptFlagPriMkrc flag and once without.
for (int pri = 1; pri >= 0; pri--) {
vimoption_T *opt;
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
opt = &options[opt_idx];
- if (!(opt->flags & P_NO_MKRC)
- && ((pri == 1) == ((opt->flags & P_PRI_MKRC) != 0))) {
+ if (!(opt->flags & kOptFlagNoMkrc)
+ && ((pri == 1) == ((opt->flags & kOptFlagPriMkrc) != 0))) {
// skip global option when only doing locals
- if (opt->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) {
+ if (option_is_global_only(opt_idx) && !(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) && (opt->flags & P_NOGLOB)) {
+ if ((opt_flags & OPT_GLOBAL) && (opt->flags & kOptFlagNoGlob)) {
continue;
}
@@ -4374,21 +4262,19 @@ int makeset(FILE *fd, int opt_flags, int local_only)
int round = 2;
void *varp_local = NULL; // fresh value
- if (opt->indir != PV_NONE) {
- if (opt->var == VAR_WIN) {
- // 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.
- if (!(opt_flags & OPT_GLOBAL) && !local_only) {
- void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value
- if (!optval_default(opt_idx, varp_fresh)) {
- round = 1;
- varp_local = varp;
- varp = varp_fresh;
- }
+ if (option_is_window_local(opt_idx)) {
+ // 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.
+ if (!(opt_flags & OPT_GLOBAL) && !local_only) {
+ void *varp_fresh = get_varp_scope(opt, OPT_GLOBAL); // local value
+ if (!optval_default(opt_idx, varp_fresh)) {
+ round = 1;
+ varp_local = varp;
+ varp = varp_fresh;
}
}
}
@@ -4403,35 +4289,24 @@ int makeset(FILE *fd, int opt_flags, int local_only)
cmd = "setlocal";
}
- if (option_has_type(opt_idx, kOptValTypeBoolean)) {
- if (put_setbool(fd, cmd, opt->fullname, *(int *)varp) == FAIL) {
- return FAIL;
- }
- } else if (option_has_type(opt_idx, kOptValTypeNumber)) {
- if (put_setnum(fd, cmd, opt->fullname, (OptInt *)varp) == FAIL) {
+ bool do_endif = false;
+ // Don't set 'syntax' and 'filetype' again if the value is already right, avoids reloading
+ // the syntax file.
+ if (opt_idx == kOptSyntax || opt_idx == kOptFiletype) {
+ if (fprintf(fd, "if &%s != '%s'", opt->fullname,
+ *(char **)(varp)) < 0
+ || put_eol(fd) < 0) {
return FAIL;
}
- } else { // string
- bool do_endif = false;
-
- // Don't set 'syntax' and 'filetype' again if the value is
- // already right, avoids reloading the syntax file.
- if (opt->indir == PV_SYN || opt->indir == PV_FT) {
- if (fprintf(fd, "if &%s != '%s'", opt->fullname,
- *(char **)(varp)) < 0
- || put_eol(fd) < 0) {
- return FAIL;
- }
- do_endif = true;
- }
- if (put_setstring(fd, cmd, opt->fullname, (char **)varp, opt->flags) == FAIL) {
+ do_endif = true;
+ }
+ if (put_set(fd, cmd, opt_idx, varp) == FAIL) {
+ return FAIL;
+ }
+ if (do_endif) {
+ if (put_line(fd, "endif") == FAIL) {
return FAIL;
}
- if (do_endif) {
- if (put_line(fd, "endif") == FAIL) {
- return FAIL;
- }
- }
}
}
}
@@ -4444,110 +4319,133 @@ int makeset(FILE *fd, int opt_flags, int local_only)
/// 'sessionoptions' or 'viewoptions' contains "folds" but not "options".
int makefoldset(FILE *fd)
{
- if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0) == FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0) == FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0) == 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) {
+ if (put_set(fd, "setlocal", kOptFoldmethod, &curwin->w_p_fdm) == FAIL
+ || put_set(fd, "setlocal", kOptFoldexpr, &curwin->w_p_fde) == FAIL
+ || put_set(fd, "setlocal", kOptFoldmarker, &curwin->w_p_fmr) == FAIL
+ || put_set(fd, "setlocal", kOptFoldignore, &curwin->w_p_fdi) == FAIL
+ || put_set(fd, "setlocal", kOptFoldlevel, &curwin->w_p_fdl) == FAIL
+ || put_set(fd, "setlocal", kOptFoldminlines, &curwin->w_p_fml) == FAIL
+ || put_set(fd, "setlocal", kOptFoldnestmax, &curwin->w_p_fdn) == FAIL
+ || put_set(fd, "setlocal", kOptFoldenable, &curwin->w_p_fen) == FAIL) {
return FAIL;
}
return OK;
}
-static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_t flags)
+/// Print the ":set" command to set a single option to file.
+///
+/// @param fd File descriptor.
+/// @param cmd Command name.
+/// @param opt_idx Option index in options[] table.
+/// @param varp Pointer to option variable.
+///
+/// @return FAIL on error, OK otherwise.
+static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
{
- if (fprintf(fd, "%s %s=", cmd, name) < 0) {
- return FAIL;
+ OptVal value = optval_from_varp(opt_idx, varp);
+ vimoption_T *opt = &options[opt_idx];
+ char *name = opt->fullname;
+ uint64_t flags = opt->flags;
+
+ if (option_is_global_local(opt_idx) && varp != opt->var
+ && optval_equal(value, get_option_unset_value(opt_idx))) {
+ // Processing unset local value of global-local option. Do nothing.
+ return OK;
+ }
+
+ switch (value.type) {
+ case kOptValTypeNil:
+ abort();
+ case kOptValTypeBoolean: {
+ assert(value.data.boolean != kNone);
+ bool value_bool = TRISTATE_TO_BOOL(value.data.boolean, false);
+
+ if (fprintf(fd, "%s %s%s", cmd, value_bool ? "" : "no", name) < 0) {
+ return FAIL;
+ }
+ break;
+ }
+ case kOptValTypeNumber: {
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
+ return FAIL;
+ }
+
+ OptInt value_num = value.data.number;
+
+ OptInt wc;
+ if (wc_use_keyname(varp, &wc)) {
+ // print 'wildchar' and 'wildcharm' as a key name
+ if (fputs(get_special_key_name((int)wc, 0), fd) < 0) {
+ return FAIL;
+ }
+ } else if (fprintf(fd, "%" PRId64, value_num) < 0) {
+ return FAIL;
+ }
+ break;
}
+ case kOptValTypeString: {
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
+ return FAIL;
+ }
- char *buf = NULL;
- char *part = NULL;
+ char *value_str = value.data.string.data;
+ char *buf = NULL;
+ char *part = NULL;
- if (*valuep != NULL) {
- if ((flags & P_EXPAND) != 0) {
- size_t size = (size_t)strlen(*valuep) + 1;
+ if (value_str != NULL) {
+ if ((flags & kOptFlagExpand) != 0) {
+ size_t size = (size_t)strlen(value_str) + 1;
- // replace home directory in the whole option value into "buf"
- buf = xmalloc(size);
- home_replace(NULL, *valuep, buf, size, false);
+ // replace home directory in the whole option value into "buf"
+ buf = xmalloc(size);
+ home_replace(NULL, value_str, buf, size, false);
- // If the option value is longer than MAXPATHL, we need to append
- // each comma separated part of the option separately, so that it
- // can be expanded when read back.
- if (size >= MAXPATHL && (flags & P_COMMA) != 0
- && vim_strchr(*valuep, ',') != NULL) {
- part = xmalloc(size);
+ // If the option value is longer than MAXPATHL, we need to append
+ // each comma separated part of the option separately, so that it
+ // can be expanded when read back.
+ if (size >= MAXPATHL && (flags & kOptFlagComma) != 0
+ && vim_strchr(value_str, ',') != NULL) {
+ part = xmalloc(size);
- // write line break to clear the option, e.g. ':set rtp='
- if (put_eol(fd) == FAIL) {
- goto fail;
- }
- char *p = buf;
- while (*p != NUL) {
- // for each comma separated option part, append value to
- // the option, :set rtp+=value
- if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
+ // write line break to clear the option, e.g. ':set rtp='
+ if (put_eol(fd) == FAIL) {
goto fail;
}
- copy_option_part(&p, part, size, ",");
- if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
- goto fail;
+ char *p = buf;
+ while (*p != NUL) {
+ // for each comma separated option part, append value to
+ // the option, :set rtp+=value
+ if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
+ goto fail;
+ }
+ copy_option_part(&p, part, size, ",");
+ if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
+ goto fail;
+ }
}
+ xfree(buf);
+ xfree(part);
+ return OK;
+ }
+ if (put_escstr(fd, buf, 2) == FAIL) {
+ xfree(buf);
+ return FAIL;
}
xfree(buf);
- xfree(part);
- return OK;
- }
- if (put_escstr(fd, buf, 2) == FAIL) {
- xfree(buf);
+ } else if (put_escstr(fd, value_str, 2) == FAIL) {
return FAIL;
}
- xfree(buf);
- } else if (put_escstr(fd, *valuep, 2) == FAIL) {
- return FAIL;
}
- }
- if (put_eol(fd) < 0) {
- return FAIL;
- }
- return OK;
-fail:
- xfree(buf);
- xfree(part);
- return FAIL;
-}
-
-static int put_setnum(FILE *fd, char *cmd, char *name, OptInt *valuep)
-{
- if (fprintf(fd, "%s %s=", cmd, name) < 0) {
- return FAIL;
- }
- OptInt wc;
- if (wc_use_keyname(valuep, &wc)) {
- // print 'wildchar' and 'wildcharm' as a key name
- if (fputs(get_special_key_name((int)wc, 0), fd) < 0) {
- return FAIL;
- }
- } else if (fprintf(fd, "%" PRId64, (int64_t)(*valuep)) < 0) {
+ break;
+ fail:
+ xfree(buf);
+ xfree(part);
return FAIL;
}
- 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
- return OK;
- }
- if (fprintf(fd, "%s %s%s", cmd, value ? "" : "no", name) < 0
- || put_eol(fd) < 0) {
+ if (put_eol(fd) < 0) {
return FAIL;
}
return OK;
@@ -4555,74 +4453,80 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
{
- if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
- if (p->var == VAR_WIN) {
+ OptIndex opt_idx = get_opt_idx(p);
+
+ if ((scope & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
+ if (option_is_window_local(opt_idx)) {
return GLOBAL_WO(get_varp_from(p, buf, win));
}
return p->var;
}
- if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
- switch ((int)p->indir) {
- case PV_FP:
+
+ if ((scope & OPT_LOCAL) && option_is_global_local(opt_idx)) {
+ switch (opt_idx) {
+ case kOptFormatprg:
return &(buf->b_p_fp);
- case PV_EFM:
+ case kOptFindfunc:
+ return &(buf->b_p_ffu);
+ case kOptErrorformat:
return &(buf->b_p_efm);
- case PV_GP:
+ case kOptGrepprg:
return &(buf->b_p_gp);
- case PV_MP:
+ case kOptMakeprg:
return &(buf->b_p_mp);
- case PV_EP:
+ case kOptEqualprg:
return &(buf->b_p_ep);
- case PV_KP:
+ case kOptKeywordprg:
return &(buf->b_p_kp);
- case PV_PATH:
+ case kOptPath:
return &(buf->b_p_path);
- case PV_AR:
+ case kOptAutoread:
return &(buf->b_p_ar);
- case PV_TAGS:
+ case kOptTags:
return &(buf->b_p_tags);
- case PV_TC:
+ case kOptTagcase:
return &(buf->b_p_tc);
- case PV_SISO:
+ case kOptSidescrolloff:
return &(win->w_p_siso);
- case PV_SO:
+ case kOptScrolloff:
return &(win->w_p_so);
- case PV_DEF:
+ case kOptDefine:
return &(buf->b_p_def);
- case PV_INC:
+ case kOptInclude:
return &(buf->b_p_inc);
- case PV_COT:
+ case kOptCompleteopt:
return &(buf->b_p_cot);
- case PV_DICT:
+ case kOptDictionary:
return &(buf->b_p_dict);
- case PV_TSR:
+ case kOptThesaurus:
return &(buf->b_p_tsr);
- case PV_TSRFU:
+ case kOptThesaurusfunc:
return &(buf->b_p_tsrfu);
- case PV_TFU:
+ case kOptTagfunc:
return &(buf->b_p_tfu);
- case PV_SBR:
+ case kOptShowbreak:
return &(win->w_p_sbr);
- case PV_STL:
+ case kOptStatusline:
return &(win->w_p_stl);
- case PV_WBR:
+ case kOptWinbar:
return &(win->w_p_wbr);
- case PV_UL:
+ case kOptUndolevels:
return &(buf->b_p_ul);
- case PV_LW:
+ case kOptLispwords:
return &(buf->b_p_lw);
- case PV_BKC:
+ case kOptBackupcopy:
return &(buf->b_p_bkc);
- case PV_MENC:
+ case kOptMakeencoding:
return &(buf->b_p_menc);
- case PV_FCS:
+ case kOptFillchars:
return &(win->w_p_fcs);
- case PV_LCS:
+ case kOptListchars:
return &(win->w_p_lcs);
- case PV_VE:
+ case kOptVirtualedit:
return &(win->w_p_ve);
+ default:
+ abort();
}
- return NULL; // "cannot happen"
}
return get_varp_from(p, buf, win);
}
@@ -4644,291 +4548,292 @@ void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T
void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{
- // hidden option, always return NULL
- if (p->var == NULL) {
- return NULL;
- }
+ OptIndex opt_idx = get_opt_idx(p);
- switch ((int)p->indir) {
- case PV_NONE:
+ // Hidden options and global-only options always use the same var pointer
+ if (is_option_hidden(opt_idx) || option_is_global_only(opt_idx)) {
return p->var;
+ }
+ switch (opt_idx) {
// global option with local value: use local value if it's been set
- case PV_EP:
+ case kOptEqualprg:
return *buf->b_p_ep != NUL ? &buf->b_p_ep : p->var;
- case PV_KP:
+ case kOptKeywordprg:
return *buf->b_p_kp != NUL ? &buf->b_p_kp : p->var;
- case PV_PATH:
+ case kOptPath:
return *buf->b_p_path != NUL ? &(buf->b_p_path) : p->var;
- case PV_AR:
+ case kOptAutoread:
return buf->b_p_ar >= 0 ? &(buf->b_p_ar) : p->var;
- case PV_TAGS:
+ case kOptTags:
return *buf->b_p_tags != NUL ? &(buf->b_p_tags) : p->var;
- case PV_TC:
+ case kOptTagcase:
return *buf->b_p_tc != NUL ? &(buf->b_p_tc) : p->var;
- case PV_SISO:
+ case kOptSidescrolloff:
return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var;
- case PV_SO:
+ case kOptScrolloff:
return win->w_p_so >= 0 ? &(win->w_p_so) : p->var;
- case PV_BKC:
+ case kOptBackupcopy:
return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var;
- case PV_DEF:
+ case kOptDefine:
return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var;
- case PV_INC:
+ case kOptInclude:
return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var;
- case PV_COT:
+ case kOptCompleteopt:
return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var;
- case PV_DICT:
+ case kOptDictionary:
return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var;
- case PV_TSR:
+ case kOptThesaurus:
return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var;
- case PV_TSRFU:
+ case kOptThesaurusfunc:
return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var;
- case PV_FP:
+ case kOptFormatprg:
return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var;
- case PV_EFM:
+ case kOptFindfunc:
+ return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var;
+ case kOptErrorformat:
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
- case PV_GP:
+ case kOptGrepprg:
return *buf->b_p_gp != NUL ? &(buf->b_p_gp) : p->var;
- case PV_MP:
+ case kOptMakeprg:
return *buf->b_p_mp != NUL ? &(buf->b_p_mp) : p->var;
- case PV_SBR:
+ case kOptShowbreak:
return *win->w_p_sbr != NUL ? &(win->w_p_sbr) : p->var;
- case PV_STL:
+ case kOptStatusline:
return *win->w_p_stl != NUL ? &(win->w_p_stl) : p->var;
- case PV_WBR:
+ case kOptWinbar:
return *win->w_p_wbr != NUL ? &(win->w_p_wbr) : p->var;
- case PV_UL:
+ case kOptUndolevels:
return buf->b_p_ul != NO_LOCAL_UNDOLEVEL ? &(buf->b_p_ul) : p->var;
- case PV_LW:
+ case kOptLispwords:
return *buf->b_p_lw != NUL ? &(buf->b_p_lw) : p->var;
- case PV_MENC:
+ case kOptMakeencoding:
return *buf->b_p_menc != NUL ? &(buf->b_p_menc) : p->var;
- case PV_FCS:
+ case kOptFillchars:
return *win->w_p_fcs != NUL ? &(win->w_p_fcs) : p->var;
- case PV_LCS:
+ case kOptListchars:
return *win->w_p_lcs != NUL ? &(win->w_p_lcs) : p->var;
- case PV_VE:
+ case kOptVirtualedit:
return *win->w_p_ve != NUL ? &win->w_p_ve : p->var;
- case PV_ARAB:
+ case kOptArabic:
return &(win->w_p_arab);
- case PV_LIST:
+ case kOptList:
return &(win->w_p_list);
- case PV_SPELL:
+ case kOptSpell:
return &(win->w_p_spell);
- case PV_CUC:
+ case kOptCursorcolumn:
return &(win->w_p_cuc);
- case PV_CUL:
+ case kOptCursorline:
return &(win->w_p_cul);
- case PV_CULOPT:
+ case kOptCursorlineopt:
return &(win->w_p_culopt);
- case PV_CC:
+ case kOptColorcolumn:
return &(win->w_p_cc);
- case PV_DIFF:
+ case kOptDiff:
return &(win->w_p_diff);
- case PV_FDC:
+ case kOptFoldcolumn:
return &(win->w_p_fdc);
- case PV_FEN:
+ case kOptFoldenable:
return &(win->w_p_fen);
- case PV_FDI:
+ case kOptFoldignore:
return &(win->w_p_fdi);
- case PV_FDL:
+ case kOptFoldlevel:
return &(win->w_p_fdl);
- case PV_FDM:
+ case kOptFoldmethod:
return &(win->w_p_fdm);
- case PV_FML:
+ case kOptFoldminlines:
return &(win->w_p_fml);
- case PV_FDN:
+ case kOptFoldnestmax:
return &(win->w_p_fdn);
- case PV_FDE:
+ case kOptFoldexpr:
return &(win->w_p_fde);
- case PV_FDT:
+ case kOptFoldtext:
return &(win->w_p_fdt);
- case PV_FMR:
+ case kOptFoldmarker:
return &(win->w_p_fmr);
- case PV_NU:
+ case kOptNumber:
return &(win->w_p_nu);
- case PV_RNU:
+ case kOptRelativenumber:
return &(win->w_p_rnu);
- case PV_NUW:
+ case kOptNumberwidth:
return &(win->w_p_nuw);
- case PV_WFB:
+ case kOptWinfixbuf:
return &(win->w_p_wfb);
- case PV_WFH:
+ case kOptWinfixheight:
return &(win->w_p_wfh);
- case PV_WFW:
+ case kOptWinfixwidth:
return &(win->w_p_wfw);
- case PV_PVW:
+ case kOptPreviewwindow:
return &(win->w_p_pvw);
- case PV_RL:
+ case kOptRightleft:
return &(win->w_p_rl);
- case PV_RLC:
+ case kOptRightleftcmd:
return &(win->w_p_rlc);
- case PV_SCROLL:
+ case kOptScroll:
return &(win->w_p_scr);
- case PV_SMS:
+ case kOptSmoothscroll:
return &(win->w_p_sms);
- case PV_WRAP:
+ case kOptWrap:
return &(win->w_p_wrap);
- case PV_LBR:
+ case kOptLinebreak:
return &(win->w_p_lbr);
- case PV_BRI:
+ case kOptBreakindent:
return &(win->w_p_bri);
- case PV_BRIOPT:
+ case kOptBreakindentopt:
return &(win->w_p_briopt);
- case PV_SCBIND:
+ case kOptScrollbind:
return &(win->w_p_scb);
- case PV_CRBIND:
+ case kOptCursorbind:
return &(win->w_p_crb);
- case PV_COCU:
+ case kOptConcealcursor:
return &(win->w_p_cocu);
- case PV_COLE:
+ case kOptConceallevel:
return &(win->w_p_cole);
- case PV_AI:
+ case kOptAutoindent:
return &(buf->b_p_ai);
- case PV_BIN:
+ case kOptBinary:
return &(buf->b_p_bin);
- case PV_BOMB:
+ case kOptBomb:
return &(buf->b_p_bomb);
- case PV_BH:
+ case kOptBufhidden:
return &(buf->b_p_bh);
- case PV_BT:
+ case kOptBuftype:
return &(buf->b_p_bt);
- case PV_BL:
+ case kOptBuflisted:
return &(buf->b_p_bl);
- case PV_CHANNEL:
+ case kOptChannel:
return &(buf->b_p_channel);
- case PV_CI:
+ case kOptCopyindent:
return &(buf->b_p_ci);
- case PV_CIN:
+ case kOptCindent:
return &(buf->b_p_cin);
- case PV_CINK:
+ case kOptCinkeys:
return &(buf->b_p_cink);
- case PV_CINO:
+ case kOptCinoptions:
return &(buf->b_p_cino);
- case PV_CINSD:
+ case kOptCinscopedecls:
return &(buf->b_p_cinsd);
- case PV_CINW:
+ case kOptCinwords:
return &(buf->b_p_cinw);
- case PV_COM:
+ case kOptComments:
return &(buf->b_p_com);
- case PV_CMS:
+ case kOptCommentstring:
return &(buf->b_p_cms);
- case PV_CPT:
+ case kOptComplete:
return &(buf->b_p_cpt);
#ifdef BACKSLASH_IN_FILENAME
- case PV_CSL:
+ case kOptCompleteslash:
return &(buf->b_p_csl);
#endif
- case PV_CFU:
+ case kOptCompletefunc:
return &(buf->b_p_cfu);
- case PV_OFU:
+ case kOptOmnifunc:
return &(buf->b_p_ofu);
- case PV_URF:
+ case kOptUserregfunc:
return &(buf->b_p_urf);
- case PV_EOF:
+ case kOptEndoffile:
return &(buf->b_p_eof);
- case PV_EOL:
+ case kOptEndofline:
return &(buf->b_p_eol);
- case PV_FIXEOL:
+ case kOptFixendofline:
return &(buf->b_p_fixeol);
- case PV_ET:
+ case kOptExpandtab:
return &(buf->b_p_et);
- case PV_FENC:
+ case kOptFileencoding:
return &(buf->b_p_fenc);
- case PV_FF:
+ case kOptFileformat:
return &(buf->b_p_ff);
- case PV_FT:
+ case kOptFiletype:
return &(buf->b_p_ft);
- case PV_FO:
+ case kOptFormatoptions:
return &(buf->b_p_fo);
- case PV_FLP:
+ case kOptFormatlistpat:
return &(buf->b_p_flp);
- case PV_IMI:
+ case kOptIminsert:
return &(buf->b_p_iminsert);
- case PV_IMS:
+ case kOptImsearch:
return &(buf->b_p_imsearch);
- case PV_INF:
+ case kOptInfercase:
return &(buf->b_p_inf);
- case PV_ISK:
+ case kOptIskeyword:
return &(buf->b_p_isk);
- case PV_INEX:
+ case kOptIncludeexpr:
return &(buf->b_p_inex);
- case PV_INDE:
+ case kOptIndentexpr:
return &(buf->b_p_inde);
- case PV_INDK:
+ case kOptIndentkeys:
return &(buf->b_p_indk);
- case PV_FEX:
+ case kOptFormatexpr:
return &(buf->b_p_fex);
- case PV_LISP:
+ case kOptLisp:
return &(buf->b_p_lisp);
- case PV_LOP:
+ case kOptLispoptions:
return &(buf->b_p_lop);
- case PV_ML:
+ case kOptModeline:
return &(buf->b_p_ml);
- case PV_MPS:
+ case kOptMatchpairs:
return &(buf->b_p_mps);
- case PV_MA:
+ case kOptModifiable:
return &(buf->b_p_ma);
- case PV_MOD:
+ case kOptModified:
return &(buf->b_changed);
- case PV_NF:
+ case kOptNrformats:
return &(buf->b_p_nf);
- case PV_PI:
+ case kOptPreserveindent:
return &(buf->b_p_pi);
- case PV_QE:
+ case kOptQuoteescape:
return &(buf->b_p_qe);
- case PV_RO:
+ case kOptReadonly:
return &(buf->b_p_ro);
- case PV_SCBK:
+ case kOptScrollback:
return &(buf->b_p_scbk);
- case PV_SI:
+ case kOptSmartindent:
return &(buf->b_p_si);
- case PV_STS:
+ case kOptSofttabstop:
return &(buf->b_p_sts);
- case PV_SUA:
+ case kOptSuffixesadd:
return &(buf->b_p_sua);
- case PV_SWF:
+ case kOptSwapfile:
return &(buf->b_p_swf);
- case PV_SMC:
+ case kOptSynmaxcol:
return &(buf->b_p_smc);
- case PV_SYN:
+ case kOptSyntax:
return &(buf->b_p_syn);
- case PV_SPC:
+ case kOptSpellcapcheck:
return &(win->w_s->b_p_spc);
- case PV_SPF:
+ case kOptSpellfile:
return &(win->w_s->b_p_spf);
- case PV_SPL:
+ case kOptSpelllang:
return &(win->w_s->b_p_spl);
- case PV_SPO:
+ case kOptSpelloptions:
return &(win->w_s->b_p_spo);
- case PV_SW:
+ case kOptShiftwidth:
return &(buf->b_p_sw);
- case PV_TFU:
+ case kOptTagfunc:
return &(buf->b_p_tfu);
- case PV_TS:
+ case kOptTabstop:
return &(buf->b_p_ts);
- case PV_TW:
+ case kOptTextwidth:
return &(buf->b_p_tw);
- case PV_UDF:
+ case kOptUndofile:
return &(buf->b_p_udf);
- case PV_WM:
+ case kOptWrapmargin:
return &(buf->b_p_wm);
- case PV_VSTS:
+ case kOptVarsofttabstop:
return &(buf->b_p_vsts);
- case PV_VTS:
+ case kOptVartabstop:
return &(buf->b_p_vts);
- case PV_KMAP:
+ case kOptKeymap:
return &(buf->b_p_keymap);
- case PV_SCL:
+ case kOptSigncolumn:
return &(win->w_p_scl);
- case PV_WINHL:
+ case kOptWinhighlight:
return &(win->w_p_winhl);
- case PV_WINBL:
+ case kOptWinblend:
return &(win->w_p_winbl);
- case PV_STC:
+ case kOptStatuscolumn:
return &(win->w_p_stc);
default:
iemsg(_("E356: get_varp ERROR"));
@@ -4959,6 +4864,15 @@ char *get_equalprg(void)
return curbuf->b_p_ep;
}
+/// Get the value of 'findfunc', either the buffer-local one or the global one.
+char *get_findfunc(void)
+{
+ if (*curbuf->b_p_ffu == NUL) {
+ return p_ffu;
+ }
+ return curbuf->b_p_ffu;
+}
+
/// Copy options from one window to another.
/// Used when splitting a window.
void win_copy_options(win_T *wp_from, win_T *wp_to)
@@ -5110,38 +5024,20 @@ void didset_window_options(win_T *wp, bool valid_cursor)
} else {
wp->w_skipcol = 0;
}
- check_colorcolumn(wp);
- briopt_check(wp);
+ check_colorcolumn(NULL, wp);
+ briopt_check(NULL, wp);
fill_culopt_flags(NULL, wp);
set_chars_option(wp, wp->w_p_fcs, kFillchars, true, NULL, 0);
set_chars_option(wp, wp->w_p_lcs, kListchars, true, NULL, 0);
- parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl
+ parse_winhl_opt(NULL, wp); // sets w_hl_needs_update also for w_p_winbl
check_blending(wp);
set_winbar_win(wp, false, valid_cursor);
- check_signcolumn(wp);
+ check_signcolumn(NULL, wp);
wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
}
-/// Index into the options table for a buffer-local option enum.
-static OptIndex buf_opt_idx[BV_COUNT];
#define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set
-/// Initialize buf_opt_idx[] if not done already.
-static void init_buf_opt_idx(void)
-{
- static bool did_init_buf_opt_idx = false;
-
- if (did_init_buf_opt_idx) {
- return;
- }
- did_init_buf_opt_idx = true;
- for (OptIndex i = 0; i < kOptIndexCount; i++) {
- if (options[i].indir & PV_BUF) {
- buf_opt_idx[options[i].indir & PV_MASK] = i;
- }
- }
-}
-
/// Copy global option values to local options for one buffer.
/// Used when creating a new buffer and sometimes when entering a buffer.
/// flags:
@@ -5179,7 +5075,6 @@ void buf_copy_options(buf_T *buf, int flags)
if (should_copy || (flags & BCO_ALWAYS)) {
CLEAR_FIELD(buf->b_p_script_ctx);
- init_buf_opt_idx();
// Don't copy the options specific to a help buffer when
// BCO_NOHELP is given or the options were initialized already
// (jumping back to a help file with CTRL-T or CTRL-O)
@@ -5196,13 +5091,13 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_fenc = xstrdup(p_fenc);
switch (*p_ffs) {
case 'm':
- buf->b_p_ff = xstrdup(FF_MAC);
+ buf->b_p_ff = xstrdup("mac");
break;
case 'd':
- buf->b_p_ff = xstrdup(FF_DOS);
+ buf->b_p_ff = xstrdup("dos");
break;
case 'u':
- buf->b_p_ff = xstrdup(FF_UNIX);
+ buf->b_p_ff = xstrdup("unix");
break;
default:
buf->b_p_ff = xstrdup(p_ff);
@@ -5215,63 +5110,63 @@ void buf_copy_options(buf_T *buf, int flags)
}
buf->b_p_ai = p_ai;
- COPY_OPT_SCTX(buf, BV_AI);
+ COPY_OPT_SCTX(buf, kBufOptAutoindent);
buf->b_p_ai_nopaste = p_ai_nopaste;
buf->b_p_sw = p_sw;
- COPY_OPT_SCTX(buf, BV_SW);
+ COPY_OPT_SCTX(buf, kBufOptShiftwidth);
buf->b_p_scbk = p_scbk;
- COPY_OPT_SCTX(buf, BV_SCBK);
+ COPY_OPT_SCTX(buf, kBufOptScrollback);
buf->b_p_tw = p_tw;
- COPY_OPT_SCTX(buf, BV_TW);
+ COPY_OPT_SCTX(buf, kBufOptTextwidth);
buf->b_p_tw_nopaste = p_tw_nopaste;
buf->b_p_tw_nobin = p_tw_nobin;
buf->b_p_wm = p_wm;
- COPY_OPT_SCTX(buf, BV_WM);
+ COPY_OPT_SCTX(buf, kBufOptWrapmargin);
buf->b_p_wm_nopaste = p_wm_nopaste;
buf->b_p_wm_nobin = p_wm_nobin;
buf->b_p_bin = p_bin;
- COPY_OPT_SCTX(buf, BV_BIN);
+ COPY_OPT_SCTX(buf, kBufOptBinary);
buf->b_p_bomb = p_bomb;
- COPY_OPT_SCTX(buf, BV_BOMB);
+ COPY_OPT_SCTX(buf, kBufOptBomb);
buf->b_p_et = p_et;
- COPY_OPT_SCTX(buf, BV_ET);
+ COPY_OPT_SCTX(buf, kBufOptExpandtab);
buf->b_p_fixeol = p_fixeol;
- COPY_OPT_SCTX(buf, BV_FIXEOL);
+ COPY_OPT_SCTX(buf, kBufOptFixendofline);
buf->b_p_et_nobin = p_et_nobin;
buf->b_p_et_nopaste = p_et_nopaste;
buf->b_p_ml = p_ml;
- COPY_OPT_SCTX(buf, BV_ML);
+ COPY_OPT_SCTX(buf, kBufOptModeline);
buf->b_p_ml_nobin = p_ml_nobin;
buf->b_p_inf = p_inf;
- COPY_OPT_SCTX(buf, BV_INF);
+ COPY_OPT_SCTX(buf, kBufOptInfercase);
if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) {
buf->b_p_swf = false;
} else {
buf->b_p_swf = p_swf;
- COPY_OPT_SCTX(buf, BV_SWF);
+ COPY_OPT_SCTX(buf, kBufOptSwapfile);
}
buf->b_p_cpt = xstrdup(p_cpt);
- COPY_OPT_SCTX(buf, BV_CPT);
+ COPY_OPT_SCTX(buf, kBufOptComplete);
#ifdef BACKSLASH_IN_FILENAME
buf->b_p_csl = xstrdup(p_csl);
- COPY_OPT_SCTX(buf, BV_CSL);
+ COPY_OPT_SCTX(buf, kBufOptCompleteslash);
#endif
buf->b_p_cfu = xstrdup(p_cfu);
- COPY_OPT_SCTX(buf, BV_CFU);
+ COPY_OPT_SCTX(buf, kBufOptCompletefunc);
set_buflocal_cfu_callback(buf);
buf->b_p_ofu = xstrdup(p_ofu);
- COPY_OPT_SCTX(buf, BV_OFU);
+ COPY_OPT_SCTX(buf, kBufOptOmnifunc);
buf->b_p_urf = xstrdup(p_urf);
- COPY_OPT_SCTX(buf, BV_URF);
set_buflocal_ofu_callback(buf);
+ COPY_OPT_SCTX(buf, kBufOptUserregfunc);
buf->b_p_tfu = xstrdup(p_tfu);
- COPY_OPT_SCTX(buf, BV_TFU);
+ COPY_OPT_SCTX(buf, kBufOptTagfunc);
set_buflocal_tfu_callback(buf);
buf->b_p_sts = p_sts;
- COPY_OPT_SCTX(buf, BV_STS);
+ COPY_OPT_SCTX(buf, kBufOptSofttabstop);
buf->b_p_sts_nopaste = p_sts_nopaste;
buf->b_p_vsts = xstrdup(p_vsts);
- COPY_OPT_SCTX(buf, BV_VSTS);
+ COPY_OPT_SCTX(buf, kBufOptVarsofttabstop);
if (p_vsts && p_vsts != empty_string_option) {
tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
@@ -5279,74 +5174,75 @@ void buf_copy_options(buf_T *buf, int flags)
}
buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL;
buf->b_p_com = xstrdup(p_com);
- COPY_OPT_SCTX(buf, BV_COM);
+ COPY_OPT_SCTX(buf, kBufOptComments);
buf->b_p_cms = xstrdup(p_cms);
- COPY_OPT_SCTX(buf, BV_CMS);
+ COPY_OPT_SCTX(buf, kBufOptCommentstring);
buf->b_p_fo = xstrdup(p_fo);
- COPY_OPT_SCTX(buf, BV_FO);
+ COPY_OPT_SCTX(buf, kBufOptFormatoptions);
buf->b_p_flp = xstrdup(p_flp);
- COPY_OPT_SCTX(buf, BV_FLP);
+ COPY_OPT_SCTX(buf, kBufOptFormatlistpat);
buf->b_p_nf = xstrdup(p_nf);
- COPY_OPT_SCTX(buf, BV_NF);
+ COPY_OPT_SCTX(buf, kBufOptNrformats);
buf->b_p_mps = xstrdup(p_mps);
- COPY_OPT_SCTX(buf, BV_MPS);
+ COPY_OPT_SCTX(buf, kBufOptMatchpairs);
buf->b_p_si = p_si;
- COPY_OPT_SCTX(buf, BV_SI);
+ COPY_OPT_SCTX(buf, kBufOptSmartindent);
buf->b_p_channel = 0;
buf->b_p_ci = p_ci;
- COPY_OPT_SCTX(buf, BV_CI);
+ COPY_OPT_SCTX(buf, kBufOptCopyindent);
buf->b_p_cin = p_cin;
- COPY_OPT_SCTX(buf, BV_CIN);
+ COPY_OPT_SCTX(buf, kBufOptCindent);
buf->b_p_cink = xstrdup(p_cink);
- COPY_OPT_SCTX(buf, BV_CINK);
+ COPY_OPT_SCTX(buf, kBufOptCinkeys);
buf->b_p_cino = xstrdup(p_cino);
- COPY_OPT_SCTX(buf, BV_CINO);
+ COPY_OPT_SCTX(buf, kBufOptCinoptions);
buf->b_p_cinsd = xstrdup(p_cinsd);
- COPY_OPT_SCTX(buf, BV_CINSD);
+ COPY_OPT_SCTX(buf, kBufOptCinscopedecls);
buf->b_p_lop = xstrdup(p_lop);
- COPY_OPT_SCTX(buf, BV_LOP);
+ COPY_OPT_SCTX(buf, kBufOptLispoptions);
// Don't copy 'filetype', it must be detected
buf->b_p_ft = empty_string_option;
buf->b_p_pi = p_pi;
- COPY_OPT_SCTX(buf, BV_PI);
+ COPY_OPT_SCTX(buf, kBufOptPreserveindent);
buf->b_p_cinw = xstrdup(p_cinw);
- COPY_OPT_SCTX(buf, BV_CINW);
+ COPY_OPT_SCTX(buf, kBufOptCinwords);
buf->b_p_lisp = p_lisp;
- COPY_OPT_SCTX(buf, BV_LISP);
+ COPY_OPT_SCTX(buf, kBufOptLisp);
// Don't copy 'syntax', it must be set
buf->b_p_syn = empty_string_option;
buf->b_p_smc = p_smc;
- COPY_OPT_SCTX(buf, BV_SMC);
+ COPY_OPT_SCTX(buf, kBufOptSynmaxcol);
buf->b_s.b_syn_isk = empty_string_option;
buf->b_s.b_p_spc = xstrdup(p_spc);
- COPY_OPT_SCTX(buf, BV_SPC);
+ COPY_OPT_SCTX(buf, kBufOptSpellcapcheck);
compile_cap_prog(&buf->b_s);
buf->b_s.b_p_spf = xstrdup(p_spf);
- COPY_OPT_SCTX(buf, BV_SPF);
+ COPY_OPT_SCTX(buf, kBufOptSpellfile);
buf->b_s.b_p_spl = xstrdup(p_spl);
- COPY_OPT_SCTX(buf, BV_SPL);
+ COPY_OPT_SCTX(buf, kBufOptSpelllang);
buf->b_s.b_p_spo = xstrdup(p_spo);
- COPY_OPT_SCTX(buf, BV_SPO);
+ COPY_OPT_SCTX(buf, kBufOptSpelloptions);
+ buf->b_s.b_p_spo_flags = spo_flags;
buf->b_p_inde = xstrdup(p_inde);
- COPY_OPT_SCTX(buf, BV_INDE);
+ COPY_OPT_SCTX(buf, kBufOptIndentexpr);
buf->b_p_indk = xstrdup(p_indk);
- COPY_OPT_SCTX(buf, BV_INDK);
+ COPY_OPT_SCTX(buf, kBufOptIndentkeys);
buf->b_p_fp = empty_string_option;
buf->b_p_fex = xstrdup(p_fex);
- COPY_OPT_SCTX(buf, BV_FEX);
+ COPY_OPT_SCTX(buf, kBufOptFormatexpr);
buf->b_p_sua = xstrdup(p_sua);
- COPY_OPT_SCTX(buf, BV_SUA);
+ COPY_OPT_SCTX(buf, kBufOptSuffixesadd);
buf->b_p_keymap = xstrdup(p_keymap);
- COPY_OPT_SCTX(buf, BV_KMAP);
+ COPY_OPT_SCTX(buf, kBufOptKeymap);
buf->b_kmap_state |= KEYMAP_INIT;
// This isn't really an option, but copying the langmap and IME
// state from the current buffer is better than resetting it.
buf->b_p_iminsert = p_iminsert;
- COPY_OPT_SCTX(buf, BV_IMI);
+ COPY_OPT_SCTX(buf, kBufOptIminsert);
buf->b_p_imsearch = p_imsearch;
- COPY_OPT_SCTX(buf, BV_IMS);
+ COPY_OPT_SCTX(buf, kBufOptImsearch);
// options that are normally global but also have a local value
// are not copied, start using the global value
@@ -5358,6 +5254,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_mp = empty_string_option;
buf->b_p_efm = empty_string_option;
buf->b_p_ep = empty_string_option;
+ buf->b_p_ffu = empty_string_option;
buf->b_p_kp = empty_string_option;
buf->b_p_path = empty_string_option;
buf->b_p_tags = empty_string_option;
@@ -5366,16 +5263,16 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_def = empty_string_option;
buf->b_p_inc = empty_string_option;
buf->b_p_inex = xstrdup(p_inex);
- COPY_OPT_SCTX(buf, BV_INEX);
+ COPY_OPT_SCTX(buf, kBufOptIncludeexpr);
buf->b_p_cot = empty_string_option;
buf->b_cot_flags = 0;
buf->b_p_dict = empty_string_option;
buf->b_p_tsr = empty_string_option;
buf->b_p_tsrfu = empty_string_option;
buf->b_p_qe = xstrdup(p_qe);
- COPY_OPT_SCTX(buf, BV_QE);
+ COPY_OPT_SCTX(buf, kBufOptQuoteescape);
buf->b_p_udf = p_udf;
- COPY_OPT_SCTX(buf, BV_UDF);
+ COPY_OPT_SCTX(buf, kBufOptUndofile);
buf->b_p_lw = empty_string_option;
buf->b_p_menc = empty_string_option;
@@ -5392,12 +5289,12 @@ void buf_copy_options(buf_T *buf, int flags)
}
} else {
buf->b_p_isk = xstrdup(p_isk);
- COPY_OPT_SCTX(buf, BV_ISK);
+ COPY_OPT_SCTX(buf, kBufOptIskeyword);
did_isk = true;
buf->b_p_ts = p_ts;
- COPY_OPT_SCTX(buf, BV_TS);
+ COPY_OPT_SCTX(buf, kBufOptTabstop);
buf->b_p_vts = xstrdup(p_vts);
- COPY_OPT_SCTX(buf, BV_VTS);
+ COPY_OPT_SCTX(buf, kBufOptVartabstop);
if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
@@ -5408,7 +5305,7 @@ void buf_copy_options(buf_T *buf, int flags)
clear_string_option(&buf->b_p_bt);
}
buf->b_p_ma = p_ma;
- COPY_OPT_SCTX(buf, BV_MA);
+ COPY_OPT_SCTX(buf, kBufOptModifiable);
}
}
@@ -5430,7 +5327,7 @@ void reset_modifiable(void)
{
curbuf->b_p_ma = false;
p_ma = false;
- options[kOptModifiable].def_val.boolean = false;
+ change_option_default(kOptModifiable, BOOLEAN_OPTVAL(false));
}
/// Set the global value for 'iminsert' to the local value.
@@ -5537,7 +5434,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
nextchar = *p;
opt_idx = find_option_len(arg, (size_t)(p - arg));
- if (opt_idx == kOptInvalid || options[opt_idx].var == NULL) {
+ if (opt_idx == kOptInvalid || is_option_hidden(opt_idx)) {
xp->xp_context = EXPAND_NOTHING;
return;
}
@@ -5613,8 +5510,8 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// Only string options below
- // Options that have P_EXPAND are considered to all use file/dir expansion.
- if (flags & P_EXPAND) {
+ // Options that have kOptFlagExpand are considered to all use file/dir expansion.
+ if (flags & kOptFlagExpand) {
p = options[opt_idx].var;
if (p == (char *)&p_bdir
|| p == (char *)&p_dir
@@ -5638,7 +5535,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
xp->xp_backslash = XP_BS_ONE;
}
}
- if (flags & P_COMMA) {
+ if (flags & kOptFlagComma) {
xp->xp_backslash |= XP_BS_COMMA;
}
}
@@ -5648,21 +5545,21 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// pattern, while accounting for backslash-escaped space/commas/colons.
// Triple-backslashed escaped file names (e.g. 'path') can also be
// delimited by space.
- if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON)) {
+ if ((flags & kOptFlagExpand) || (flags & kOptFlagComma) || (flags & kOptFlagColon)) {
for (p = argend - 1; p > xp->xp_pattern; p--) {
// count number of backslashes before ' ' or ','
- if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) {
+ if (*p == ' ' || *p == ',' || (*p == ':' && (flags & kOptFlagColon))) {
char *s = p;
while (s > xp->xp_pattern && *(s - 1) == '\\') {
s--;
}
if ((*p == ' ' && ((xp->xp_backslash & XP_BS_THREE) && (p - s) < 3))
#if defined(BACKSLASH_IN_FILENAME)
- || (*p == ',' && (flags & P_COMMA) && (p - s) < 1)
+ || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 1)
#else
- || (*p == ',' && (flags & P_COMMA) && (p - s) < 2)
+ || (*p == ',' && (flags & kOptFlagComma) && (p - s) < 2)
#endif
- || (*p == ':' && (flags & P_COLON))) {
+ || (*p == ':' && (flags & kOptFlagColon))) {
xp->xp_pattern = p + 1;
break;
}
@@ -5672,7 +5569,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
// An option that is a list of single-character flags should always start
// at the end as we don't complete words.
- if (flags & P_FLAGLIST) {
+ if (flags & kOptFlagFlagList) {
xp->xp_pattern = argend;
}
@@ -5759,9 +5656,9 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM
}
}
char *str;
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
str = options[opt_idx].fullname;
- if (options[opt_idx].var == NULL) {
+ if (is_option_hidden(opt_idx)) {
continue;
}
if (xp->xp_context == EXPAND_BOOL_SETTINGS
@@ -5825,7 +5722,7 @@ static char *escape_option_str_cmdline(char *var)
for (var = buf; *var != NUL; MB_PTR_ADV(var)) {
if (var[0] == '\\' && var[1] == '\\'
&& expand_option_idx != kOptInvalid
- && (options[expand_option_idx].flags & P_EXPAND)
+ && (options[expand_option_idx].flags & kOptFlagExpand)
&& vim_isfilec((uint8_t)var[2])
&& (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
STRMOVE(var, var + 1);
@@ -5911,11 +5808,11 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
if (option_has_type(expand_option_idx, kOptValTypeNumber)) {
return ExpandOldSetting(numMatches, matches);
- } else if (option_flags & P_COMMA) {
+ } else if (option_flags & kOptFlagComma) {
// Split the option by comma, then present each option to the user if
// it matches the pattern.
// This condition needs to go first, because 'whichwrap' has both
- // P_COMMA and P_FLAGLIST.
+ // kOptFlagComma and kOptFlagFlagList.
if (*option_val == NUL) {
return FAIL;
@@ -5962,7 +5859,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
*matches = ga.ga_data;
*numMatches = ga.ga_len;
return OK;
- } else if (option_flags & P_FLAGLIST) {
+ } else if (option_flags & kOptFlagFlagList) {
// Only present the flags that are set on the option as the other flags
// are not meaningful to do set-= on.
@@ -6009,6 +5906,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
static void option_value2string(vimoption_T *opt, int scope)
{
void *varp = get_varp_scope(opt, scope);
+ assert(varp != NULL);
if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
OptInt wc = 0;
@@ -6025,9 +5923,8 @@ static void option_value2string(vimoption_T *opt, int scope)
}
} else { // string
varp = *(char **)(varp);
- if (varp == NULL) { // Just in case.
- NameBuff[0] = NUL;
- } else if (opt->flags & P_EXPAND) {
+
+ if (opt->flags & kOptFlagExpand) {
home_replace(NULL, varp, NameBuff, MAXPATHL, false);
} else {
xstrlcpy(NameBuff, varp, MAXPATHL);
@@ -6088,7 +5985,7 @@ void vimrc_found(char *fname, char *envname)
bool option_was_set(OptIndex opt_idx)
{
assert(opt_idx != kOptInvalid);
- return options[opt_idx].flags & P_WAS_SET;
+ return options[opt_idx].flags & kOptFlagWasSet;
}
/// Reset the flag indicating option "name" was set.
@@ -6097,7 +5994,7 @@ bool option_was_set(OptIndex opt_idx)
void reset_option_was_set(OptIndex opt_idx)
{
assert(opt_idx != kOptInvalid);
- options[opt_idx].flags &= ~P_WAS_SET;
+ options[opt_idx].flags &= ~(unsigned)kOptFlagWasSet;
}
/// fill_culopt_flags() -- called when 'culopt' changes value
@@ -6331,13 +6228,13 @@ void set_fileformat(int eol_style, int opt_flags)
switch (eol_style) {
case EOL_UNIX:
- p = FF_UNIX;
+ p = "unix";
break;
case EOL_MAC:
- p = FF_MAC;
+ p = "mac";
break;
case EOL_DOS:
- p = FF_DOS;
+ p = "dos";
break;
}
@@ -6420,11 +6317,11 @@ dict_T *get_winbuf_options(const int bufopt)
{
dict_T *const d = tv_dict_alloc();
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
vimoption_T *opt = &options[opt_idx];
- if ((bufopt && (opt->indir & PV_BUF))
- || (!bufopt && (opt->indir & PV_WIN))) {
+ if ((bufopt && (option_has_scope(opt_idx, kOptScopeBuf)))
+ || (!bufopt && (option_has_scope(opt_idx, kOptScopeWin)))) {
void *varp = get_varp(opt);
if (varp != NULL) {
@@ -6467,8 +6364,8 @@ Dict get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena,
Dict get_all_vimoptions(Arena *arena)
{
- Dict retval = arena_dict(arena, kOptIndexCount);
- for (OptIndex opt_idx = 0; opt_idx < kOptIndexCount; opt_idx++) {
+ Dict retval = arena_dict(arena, kOptCount);
+ for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
Dict opt_dict = vimoption2dict(&options[opt_idx], OPT_GLOBAL, curbuf, curwin, arena);
PUT_C(retval, options[opt_idx].fullname, DICT_OBJ(opt_dict));
}
@@ -6477,15 +6374,16 @@ Dict get_all_vimoptions(Arena *arena)
static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, Arena *arena)
{
+ OptIndex opt_idx = get_opt_idx(opt);
Dict dict = arena_dict(arena, 13);
PUT_C(dict, "name", CSTR_AS_OBJ(opt->fullname));
PUT_C(dict, "shortname", CSTR_AS_OBJ(opt->shortname));
const char *scope;
- if (opt->indir & PV_BUF) {
+ if (option_has_scope(opt_idx, kOptScopeBuf)) {
scope = "buf";
- } else if (opt->indir & PV_WIN) {
+ } else if (option_has_scope(opt_idx, kOptScopeWin)) {
scope = "win";
} else {
scope = "global";
@@ -6494,22 +6392,22 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
PUT_C(dict, "scope", CSTR_AS_OBJ(scope));
// welcome to the jungle
- PUT_C(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH));
- PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA));
- PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST));
+ PUT_C(dict, "global_local", BOOLEAN_OBJ(option_is_global_local(opt_idx)));
+ PUT_C(dict, "commalist", BOOLEAN_OBJ(opt->flags & kOptFlagComma));
+ PUT_C(dict, "flaglist", BOOLEAN_OBJ(opt->flags & kOptFlagFlagList));
- PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET));
+ PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet));
LastSet last_set = { .channel_id = 0 };
if (req_scope == OPT_GLOBAL) {
last_set = opt->last_set;
} else {
// Scope is either OPT_LOCAL or a fallback mode was requested.
- if (opt->indir & PV_BUF) {
- last_set = buf->b_p_script_ctx[opt->indir & PV_MASK];
+ if (option_has_scope(opt_idx, kOptScopeBuf)) {
+ last_set = buf->b_p_script_ctx[opt->scope_idx[kOptScopeBuf]];
}
- if (opt->indir & PV_WIN) {
- last_set = win->w_p_script_ctx[opt->indir & PV_MASK];
+ if (option_has_scope(opt_idx, kOptScopeWin)) {
+ last_set = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]];
}
if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
last_set = opt->last_set;
@@ -6520,33 +6418,9 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
PUT_C(dict, "last_set_linenr", INTEGER_OBJ(last_set.script_ctx.sc_lnum));
PUT_C(dict, "last_set_chan", INTEGER_OBJ((int64_t)last_set.channel_id));
- // TODO(bfredl): do you even nocp?
- OptVal def = optval_from_varp(get_opt_idx(opt), &opt->def_val);
-
- PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(def.type)));
- PUT_C(dict, "default", optval_as_object(def));
- PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP)));
+ PUT_C(dict, "type", CSTR_AS_OBJ(optval_type_get_name(option_get_type(get_opt_idx(opt)))));
+ PUT_C(dict, "default", optval_as_object(opt->def_val));
+ PUT_C(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & kOptFlagNoDup)));
return dict;
}
-
-/// Check if option is multitype (supports multiple types).
-static bool option_is_multitype(OptIndex opt_idx)
-{
- const OptTypeFlags type_flags = get_option(opt_idx)->type_flags;
- assert(type_flags != 0);
- return !is_power_of_two(type_flags);
-}
-
-/// Check if option supports a specific type.
-bool option_has_type(OptIndex opt_idx, OptValType type)
-{
- // Ensure that type flags variable can hold all types.
- STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8,
- "Option type_flags cannot fit all option types");
- // Ensure that the type is valid before accessing type_flags.
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
- // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in
- // the type_flags bit field.
- return get_option(opt_idx)->type_flags & (1 << type);
-}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 19764c0121..cba5b00d95 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -13,63 +13,6 @@
#include "nvim/option_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
-/// The options that are local to a window or buffer have "indir" set to one of
-/// these values. Special values:
-/// PV_NONE: global option.
-/// PV_WIN is added: window-local option
-/// PV_BUF is added: buffer-local option
-/// PV_BOTH is added: global option which also has a local value.
-enum {
- PV_BOTH = 0x1000,
- PV_WIN = 0x2000,
- PV_BUF = 0x4000,
- PV_MASK = 0x0fff,
-};
-#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x))
-#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x))
-#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
-
-/// 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
-} idopt_T;
-
-// Options local to a window have a value local to a buffer and global to all
-// buffers. Indicate this by setting "var" to VAR_WIN.
-#define VAR_WIN ((char *)-1)
-
-typedef struct {
- char *fullname; ///< full option name
- char *shortname; ///< permissible abbreviation
- uint32_t flags; ///< see above
- OptTypeFlags type_flags; ///< option type flags, see OptValType
- void *var; ///< global option: pointer to variable;
- ///< window-local option: VAR_WIN;
- ///< buffer-local option: global value
- idopt_T indir; ///< global option: PV_NONE;
- ///< local option: indirect option index
- bool hidden; ///< option is hidden, any attempt to set its value will be ignored.
- bool immutable; ///< option is immutable, trying to set its value will give an error.
-
- /// callback function to invoke after an option is modified to validate and
- /// apply the new value.
- opt_did_set_cb_T opt_did_set_cb;
-
- /// callback function to invoke when expanding possible values on the
- /// cmdline. Only useful for string options.
- opt_expand_cb_T opt_expand_cb;
-
- // TODO(famiu): Use OptVal for def_val.
- union {
- int boolean;
- OptInt number;
- char *string;
- } def_val; ///< default value for variable
-
- LastSet last_set; ///< script in which the option was last set
-} vimoption_T;
-
/// flags for buf_copy_options()
enum {
BCO_ENTER = 1, ///< going to enter the buffer
@@ -92,13 +35,6 @@ typedef enum {
OPT_SKIPRTP = 0x80, ///< "skiprtp" in 'sessionoptions'
} OptionSetFlags;
-/// Return value from get_option_attrs().
-enum {
- SOPT_GLOBAL = 0x01, ///< Option has global value
- SOPT_WIN = 0x02, ///< Option has window-local value
- SOPT_BUF = 0x04, ///< Option has buffer-local value
-};
-
/// Get name of OptValType as a string.
static inline const char *optval_type_get_name(const OptValType type)
{
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index ae9ccd371c..832e03148a 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -11,6 +11,40 @@
# include "options_enum.generated.h"
#endif
+/// Option flags.
+typedef enum {
+ kOptFlagExpand = 1 << 0, ///< Environment expansion.
+ ///< NOTE: kOptFlagExpand can never be used for local or hidden options.
+ kOptFlagNoDefExp = 1 << 1, ///< Don't expand default value.
+ kOptFlagNoDefault = 1 << 2, ///< Don't set to default value.
+ kOptFlagWasSet = 1 << 3, ///< Option has been set/reset.
+ kOptFlagNoMkrc = 1 << 4, ///< Don't include in :mkvimrc output.
+ kOptFlagUIOption = 1 << 5, ///< Send option to remote UI.
+ kOptFlagRedrTabl = 1 << 6, ///< Redraw tabline.
+ kOptFlagRedrStat = 1 << 7, ///< Redraw status lines.
+ kOptFlagRedrWin = 1 << 8, ///< Redraw current window and recompute text.
+ kOptFlagRedrBuf = 1 << 9, ///< Redraw current buffer and recompute text.
+ kOptFlagRedrAll = kOptFlagRedrBuf | kOptFlagRedrWin, ///< Redraw all windows and recompute text.
+ kOptFlagRedrClear = kOptFlagRedrAll | kOptFlagRedrStat, ///< Clear and redraw all and recompute text.
+ kOptFlagComma = 1 << 10, ///< Comma-separated list.
+ kOptFlagOneComma = (1 << 11) | kOptFlagComma, ///< Comma-separated list that cannot have two consecutive commas.
+ kOptFlagNoDup = 1 << 12, ///< Don't allow duplicate strings.
+ kOptFlagFlagList = 1 << 13, ///< List of single-char flags.
+ kOptFlagSecure = 1 << 14, ///< Cannot change in modeline or secure mode.
+ kOptFlagGettext = 1 << 15, ///< Expand default value with _().
+ kOptFlagNoGlob = 1 << 16, ///< Do not use local value for global vimrc.
+ kOptFlagNFname = 1 << 17, ///< Only normal file name chars allowed.
+ kOptFlagInsecure = 1 << 18, ///< Option was set from a modeline.
+ kOptFlagPriMkrc = 1 << 19, ///< Priority for :mkvimrc (setting option has side effects).
+ kOptFlagNoML = 1 << 20, ///< Not allowed in modeline.
+ kOptFlagCurswant = 1 << 21, ///< Update curswant required; not needed when there is a redraw flag.
+ kOptFlagNDname = 1 << 22, ///< Only normal directory name chars allowed.
+ kOptFlagHLOnly = 1 << 23, ///< Option only changes highlight, not text.
+ kOptFlagMLE = 1 << 24, ///< Under control of 'modelineexpr'.
+ kOptFlagFunc = 1 << 25, ///< Accept a function reference or a lambda.
+ kOptFlagColon = 1 << 26, ///< Values use colons to create sublists.
+} OptFlags;
+
/// Option value type.
/// These types are also used as type flags by using the type value as an index for the type_flags
/// bit field (@see option_has_type()).
@@ -20,12 +54,20 @@ typedef enum {
kOptValTypeNumber,
kOptValTypeString,
} OptValType;
-
/// Always update this whenever a new option type is added.
#define kOptValTypeSize (kOptValTypeString + 1)
-
typedef uint32_t OptTypeFlags;
+/// Scopes that an option can support.
+typedef enum {
+ kOptScopeGlobal = 0, ///< Request global option value
+ kOptScopeWin, ///< Request window-local option value
+ kOptScopeBuf, ///< Request buffer-local option value
+} OptScope;
+/// Always update this whenever a new option scope is added.
+#define kOptScopeSize (kOptScopeBuf + 1)
+typedef uint8_t OptScopeFlags;
+
typedef union {
// boolean options are actually tri-states because they have a third "None" value.
TriState boolean;
@@ -62,7 +104,7 @@ typedef struct {
/// New value of the option.
OptValData os_newval;
- /// Option value was checked to be safe, no need to set P_INSECURE
+ /// Option value was checked to be safe, no need to set kOptFlagInsecure
/// Used for the 'keymap', 'filetype' and 'syntax' options.
bool os_value_checked;
/// Option value changed. Used for the 'filetype' and 'syntax' options.
@@ -127,9 +169,26 @@ typedef struct {
/// caller.
typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches);
-/// Requested option scopes for various functions in option.c
-typedef enum {
- kOptReqGlobal = 0, ///< Request global option value
- kOptReqWin = 1, ///< Request window-local option value
- kOptReqBuf = 2, ///< Request buffer-local option value
-} OptReqScope;
+typedef struct {
+ char *fullname; ///< full option name
+ char *shortname; ///< permissible abbreviation
+ uint32_t flags; ///< see above
+ OptTypeFlags type_flags; ///< option type flags, see OptValType
+ OptScopeFlags scope_flags; ///< option scope flags, see OptScope
+ void *var; ///< global option: pointer to variable;
+ ///< window-local option: NULL;
+ ///< buffer-local option: global value
+ ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope.
+ bool immutable; ///< option is immutable, trying to set it will give an error.
+
+ /// callback function to invoke after an option is modified to validate and
+ /// apply the new value.
+ opt_did_set_cb_T opt_did_set_cb;
+
+ /// callback function to invoke when expanding possible values on the
+ /// cmdline. Only useful for string options.
+ opt_expand_cb_T opt_expand_cb;
+
+ OptVal def_val; ///< default value
+ LastSet last_set; ///< script in which the option was last set
+} vimoption_T;
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 6b78007253..9ed24c4c9c 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -7,51 +7,6 @@
// option_vars.h: definition of global variables for settable options
-// Option Flags
-#define P_ALLOCED 0x01U ///< the option is in allocated memory,
- ///< must use free_string_option() when
- ///< assigning new value. Not set if default is
- ///< the same.
-#define P_EXPAND 0x02U ///< environment expansion. NOTE: P_EXPAND can
- ///< never be used for local or hidden options
-#define P_NO_DEF_EXP 0x04U ///< do not expand default value
-#define P_NODEFAULT 0x08U ///< don't set to default value
-#define P_DEF_ALLOCED 0x10U ///< default value is in allocated memory, must
- ///< use free() when assigning new value
-#define P_WAS_SET 0x20U ///< option has been set/reset
-#define P_NO_MKRC 0x40U ///< don't include in :mkvimrc output
-
-// when option changed, what to display:
-#define P_UI_OPTION 0x80U ///< send option to remote UI
-#define P_RTABL 0x100U ///< redraw tabline
-#define P_RSTAT 0x200U ///< redraw status lines
-#define P_RWIN 0x400U ///< redraw current window and recompute text
-#define P_RBUF 0x800U ///< redraw current buffer and recompute text
-#define P_RALL 0xC00U ///< redraw all windows and recompute text
-#define P_RCLR 0xE00U ///< clear and redraw all and recompute text
-
-#define P_COMMA 0x1000U ///< comma separated list
-#define P_ONECOMMA 0x3000U ///< P_COMMA and cannot have two consecutive
- ///< commas
-#define P_NODUP 0x4000U ///< don't allow duplicate strings
-#define P_FLAGLIST 0x8000U ///< list of single-char flags
-
-#define P_SECURE 0x10000U ///< cannot change in modeline or secure mode
-#define P_GETTEXT 0x20000U ///< expand default value with _()
-#define P_NOGLOB 0x40000U ///< do not use local value for global vimrc
-#define P_NFNAME 0x80000U ///< only normal file name chars allowed
-#define P_INSECURE 0x100000U ///< option was set from a modeline
-#define P_PRI_MKRC 0x200000U ///< priority for :mkvimrc (setting option
- ///< has side effects)
-#define P_NO_ML 0x400000U ///< not allowed in modeline
-#define P_CURSWANT 0x800000U ///< update curswant required; not needed
- ///< when there is a redraw flag
-#define P_NDNAME 0x1000000U ///< only normal dir name chars allowed
-#define P_HLONLY 0x2000000U ///< option only changes highlight, not text
-#define P_MLE 0x4000000U ///< under control of 'modelineexpr'
-#define P_FUNC 0x8000000U ///< accept a function reference or a lambda
-#define P_COLON 0x10000000U ///< values use colons to create sublists
-
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
"i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
@@ -76,21 +31,6 @@
#define DFLT_GREPFORMAT "%f:%l:%m,%f:%l%m,%f %l%m"
-// default values for b_p_ff 'fileformat' and p_ffs 'fileformats'
-#define FF_DOS "dos"
-#define FF_MAC "mac"
-#define FF_UNIX "unix"
-
-#ifdef USE_CRNL
-# define DFLT_FF "dos"
-# define DFLT_FFS_VIM "dos,unix"
-# define DFLT_FFS_VI "dos,unix" // also autodetect in compatible mode
-#else
-# define DFLT_FF "unix"
-# define DFLT_FFS_VIM "unix,dos"
-# define DFLT_FFS_VI ""
-#endif
-
// Possible values for 'encoding'
#define ENC_UCSBOM "ucs-bom" // check for BOM at start of file
@@ -348,6 +288,12 @@ enum {
#define LISPWORD_VALUE \
"defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object"
+// When a string option is NULL, it is set to empty_string_option,
+// to avoid having to check for NULL everywhere.
+//
+// TODO(famiu): Remove this when refcounted strings are used for string options.
+EXTERN char empty_string_option[] INIT( = "");
+
// The following are actual variables for the options
EXTERN char *p_ambw; ///< 'ambiwidth'
@@ -403,7 +349,8 @@ EXTERN unsigned bo_flags;
#define BO_REG 0x8000
#define BO_SH 0x10000
#define BO_SPELL 0x20000
-#define BO_WILD 0x40000
+#define BO_TERM 0x40000
+#define BO_WILD 0x80000
EXTERN char *p_bsk; ///< 'backupskip'
EXTERN char *p_breakat; ///< 'breakat'
@@ -489,6 +436,7 @@ EXTERN char *p_ffs; ///< 'fileformats'
EXTERN int p_fic; ///< 'fileignorecase'
EXTERN char *p_ft; ///< 'filetype'
EXTERN char *p_fcs; ///< 'fillchar'
+EXTERN char *p_ffu; ///< 'findfunc'
EXTERN int p_fixeol; ///< 'fixendofline'
EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
@@ -567,6 +515,7 @@ EXTERN char *p_mef; ///< 'makeef'
EXTERN char *p_mp; ///< 'makeprg'
EXTERN char *p_mps; ///< 'matchpairs'
EXTERN OptInt p_mat; ///< 'matchtime'
+EXTERN OptInt p_mco; ///< 'maxcombine'
EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
@@ -587,6 +536,7 @@ EXTERN OptInt p_mousescroll_vert INIT( = MOUSESCROLL_VERT_DFLT);
EXTERN OptInt p_mousescroll_hor INIT( = MOUSESCROLL_HOR_DFLT);
EXTERN OptInt p_mouset; ///< 'mousetime'
EXTERN int p_more; ///< 'more'
+EXTERN OptInt p_mhi; ///< 'msghistory'
EXTERN char *p_nf; ///< 'nrformats'
EXTERN char *p_opfunc; ///< 'operatorfunc'
EXTERN char *p_para; ///< 'paragraphs'
@@ -770,7 +720,7 @@ EXTERN unsigned ve_flags;
#define VE_NONEU 32U // "NONE"
EXTERN OptInt p_verbose; ///< 'verbose'
#ifdef IN_OPTION_C
-char *p_vfile = ""; ///< used before options are initialized
+char *p_vfile = empty_string_option; ///< used before options are initialized
#else
extern char *p_vfile; ///< 'verbosefile'
#endif
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 932d8f8d0e..9f86ef7489 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -7,14 +7,12 @@
--- @field alias? string|string[]
--- @field short_desc? string|fun(): string
--- @field varname? string
---- @field pv_name? string
---- @field type 'boolean'|'number'|'string'
---- @field hidden? boolean
+--- @field type vim.option_type|vim.option_type[]
--- @field immutable? boolean
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
---- @field enable_if? string|false
+--- @field enable_if? string
--- @field defaults? vim.option_defaults
--- @field secure? true
--- @field noglob? true
@@ -42,7 +40,9 @@
--- @field doc? string Default to show in options.txt
--- @field meta? integer|boolean|string Default to use in Lua meta files
---- @alias vim.option_scope 'global'|'buffer'|'window'
+--- @alias vim.option_scope 'global'|'buf'|'win'
+--- @alias vim.option_type 'boolean'|'number'|'string'
+--- @alias vim.option_value boolean|number|string
--- @alias vim.option_redraw
--- |'statuslines'
@@ -61,18 +61,11 @@ local function cstr(s)
end
--- @param s string
---- @return fun(): string
-local function macros(s)
- return function()
- return '.string=' .. s
- end
-end
-
---- @param s string
---- @return fun(): string
-local function imacros(s)
+--- @param t vim.option_type
+--- @return fun(): string, vim.option_type
+local function macros(s, t)
return function()
- return '.number=' .. s
+ return s, t
end
end
@@ -87,17 +80,19 @@ end
-- luacheck: ignore 621
return {
cstr = cstr,
+ --- @type string[]
+ valid_scopes = { 'global', 'buf', 'win' },
--- @type vim.option_meta[]
--- The order of the options MUST be alphabetic for ":set all".
options = {
{
abbreviation = 'al',
defaults = { if_true = 224 },
- enable_if = false,
full_name = 'aleph',
scope = { 'global' },
short_desc = N_('ASCII code of the letter Aleph (Hebrew)'),
type = 'number',
+ immutable = true,
},
{
abbreviation = 'ari',
@@ -179,7 +174,7 @@ return {
]=],
full_name = 'arabic',
redraw = { 'curswant' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('Arabic as a default second language'),
type = 'boolean',
},
@@ -242,7 +237,7 @@ return {
a different way.
]=],
full_name = 'autoindent',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('take indent for new line from previous line'),
type = 'boolean',
varname = 'p_ai',
@@ -262,7 +257,7 @@ return {
<
]=],
full_name = 'autoread',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('autom. read file when changed outside of Vim'),
type = 'boolean',
varname = 'p_ar',
@@ -435,12 +430,13 @@ return {
useful for example in source trees where all the files are symbolic or
hard links and any changes should stay in the local source tree, not
be propagated back to the original source.
- *crontab*
+ *crontab*
One situation where "no" and "auto" will cause problems: A program
that opens a file, invokes Vim to edit that file, and then tests if
the open file was changed (through the file descriptor) will check the
backup file instead of the newly created file. "crontab -e" is an
- example.
+ example, as are several |file-watcher| daemons like inotify. In that
+ case you probably want to switch this option.
When a copy is made, the original file is truncated and then filled
with the new text. This means that protection bits, owner and
@@ -462,7 +458,7 @@ return {
expand_cb = 'expand_set_backupcopy',
full_name = 'backupcopy',
list = 'onecomma',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_("make backup as a copy, don't rename the file"),
type = 'string',
varname = 'p_bkc',
@@ -597,6 +593,7 @@ return {
separated list of items. For each item that is present, the bell
will be silenced. This is most useful to specify specific events in
insert mode to be silenced.
+ You can also make it flash by using 'visualbell'.
item meaning when present ~
all All events.
@@ -620,6 +617,7 @@ return {
register Unknown register after <C-R> in |Insert-mode|.
shell Bell from shell output |:!|.
spell Error happened on spell suggest.
+ term Bell from |:terminal| output.
wildmode More matches in |cmdline-completion| available
(depends on the 'wildmode' setting).
@@ -670,7 +668,7 @@ return {
]=],
full_name = 'binary',
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('read/write/edit file in binary mode'),
type = 'boolean',
varname = 'p_bin',
@@ -698,7 +696,7 @@ return {
full_name = 'bomb',
no_mkrc = true,
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('a Byte Order Mark to the file'),
type = 'boolean',
varname = 'p_bomb',
@@ -732,13 +730,12 @@ return {
]=],
full_name = 'breakindent',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('wrapped line repeats indent'),
type = 'boolean',
},
{
abbreviation = 'briopt',
- alloced = true,
cb = 'did_set_breakindentopt',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -775,7 +772,7 @@ return {
full_name = 'breakindentopt',
list = 'onecomma',
redraw = { 'current_buffer' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_("settings for 'breakindent'"),
type = 'string',
},
@@ -793,15 +790,14 @@ return {
current Use the current directory.
{path} Use the specified directory
]=],
- enable_if = false,
full_name = 'browsedir',
scope = { 'global' },
short_desc = N_('which directory to start browsing in'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'bh',
- alloced = true,
cb = 'did_set_bufhidden',
defaults = { if_true = '' },
desc = [=[
@@ -828,7 +824,7 @@ return {
expand_cb = 'expand_set_bufhidden',
full_name = 'bufhidden',
noglob = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('what to do when buffer is no longer in window'),
type = 'string',
varname = 'p_bh',
@@ -846,7 +842,7 @@ return {
]=],
full_name = 'buflisted',
noglob = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('whether the buffer shows up in the buffer list'),
tags = { 'E85' },
type = 'boolean',
@@ -854,7 +850,6 @@ return {
},
{
abbreviation = 'bt',
- alloced = true,
cb = 'did_set_buftype',
defaults = { if_true = '' },
desc = [=[
@@ -906,7 +901,7 @@ return {
expand_cb = 'expand_set_buftype',
full_name = 'buftype',
noglob = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
tags = { 'E382' },
short_desc = N_('special type of buffer'),
type = 'string',
@@ -991,7 +986,7 @@ return {
{
cb = 'did_set_cedit',
defaults = {
- if_true = macros('CTRL_F_STR'),
+ if_true = macros('CTRL_F_STR', 'string'),
doc = 'CTRL-F',
},
desc = [=[
@@ -1021,7 +1016,7 @@ return {
full_name = 'channel',
no_mkrc = true,
nodefault = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('Channel connected to the buffer'),
type = 'number',
varname = 'p_channel',
@@ -1099,14 +1094,13 @@ return {
option or 'indentexpr'.
]=],
full_name = 'cindent',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('do C program indenting'),
type = 'boolean',
varname = 'p_cin',
},
{
abbreviation = 'cink',
- alloced = true,
defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
deny_duplicates = true,
desc = [=[
@@ -1118,14 +1112,13 @@ return {
]=],
full_name = 'cinkeys',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("keys that trigger indent when 'cindent' is set"),
type = 'string',
varname = 'p_cink',
},
{
abbreviation = 'cino',
- alloced = true,
cb = 'did_set_cinoptions',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -1136,14 +1129,13 @@ return {
]=],
full_name = 'cinoptions',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("how to do indenting when 'cindent' is set"),
type = 'string',
varname = 'p_cino',
},
{
abbreviation = 'cinsd',
- alloced = true,
defaults = { if_true = 'public,protected,private' },
deny_duplicates = true,
desc = [=[
@@ -1155,14 +1147,13 @@ return {
]=],
full_name = 'cinscopedecls',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("words that are recognized by 'cino-g'"),
type = 'string',
varname = 'p_cinsd',
},
{
abbreviation = 'cinw',
- alloced = true,
defaults = { if_true = 'if,else,while,do,for,switch' },
deny_duplicates = true,
desc = [=[
@@ -1175,7 +1166,7 @@ return {
]=],
full_name = 'cinwords',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("words where 'si' and 'cin' add an indent"),
type = 'string',
varname = 'p_cinw',
@@ -1277,7 +1268,7 @@ return {
full_name = 'colorcolumn',
list = 'onecomma',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('columns to highlight'),
type = 'string',
},
@@ -1285,7 +1276,7 @@ return {
abbreviation = 'co',
cb = 'did_set_lines_or_columns',
defaults = {
- if_true = imacros('DFLT_COLS'),
+ if_true = macros('DFLT_COLS', 'number'),
doc = '80 or terminal width',
},
desc = [=[
@@ -1312,7 +1303,6 @@ return {
},
{
abbreviation = 'com',
- alloced = true,
cb = 'did_set_comments',
defaults = { if_true = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•' },
deny_duplicates = true,
@@ -1323,7 +1313,7 @@ return {
]=],
full_name = 'comments',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('patterns that can start a comment line'),
tags = { 'E524', 'E525' },
type = 'string',
@@ -1331,7 +1321,6 @@ return {
},
{
abbreviation = 'cms',
- alloced = true,
cb = 'did_set_commentstring',
defaults = { if_true = '' },
desc = [=[
@@ -1340,7 +1329,7 @@ return {
Used for |commenting| and to add markers for folding, see |fold-marker|.
]=],
full_name = 'commentstring',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('template for comments; used for fold marker'),
tags = { 'E537' },
type = 'string',
@@ -1357,7 +1346,6 @@ return {
},
{
abbreviation = 'cpt',
- alloced = true,
cb = 'did_set_complete',
defaults = { if_true = '.,w,b,u,t' },
deny_duplicates = true,
@@ -1398,7 +1386,7 @@ return {
expand_cb = 'expand_set_complete',
full_name = 'complete',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('specify how Insert mode completion works'),
tags = { 'E535' },
type = 'string',
@@ -1406,7 +1394,6 @@ return {
},
{
abbreviation = 'cfu',
- alloced = true,
cb = 'did_set_completefunc',
defaults = { if_true = '' },
desc = [=[
@@ -1421,7 +1408,7 @@ return {
]=],
full_name = 'completefunc',
func = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
secure = true,
short_desc = N_('function to be used for Insert mode completion'),
type = 'string',
@@ -1497,7 +1484,7 @@ return {
expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',
list = 'onecomma',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('options for Insert mode completion'),
type = 'string',
varname = 'p_cot',
@@ -1507,7 +1494,7 @@ return {
cb = 'did_set_completeslash',
defaults = { if_true = '' },
desc = [=[
- only for MS-Windows
+ only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion:
- When this option is set to "slash", a forward slash is used for path
completion in insert mode. This is useful when editing HTML tag, or
@@ -1522,13 +1509,12 @@ return {
enable_if = 'BACKSLASH_IN_FILENAME',
expand_cb = 'expand_set_completeslash',
full_name = 'completeslash',
- scope = { 'buffer' },
+ scope = { 'buf' },
type = 'string',
varname = 'p_csl',
},
{
abbreviation = 'cocu',
- alloced = true,
cb = 'did_set_concealcursor',
defaults = { if_true = '' },
desc = [=[
@@ -1552,7 +1538,7 @@ return {
full_name = 'concealcursor',
list = 'flags',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('whether concealable text is hidden in cursor line'),
type = 'string',
},
@@ -1581,7 +1567,7 @@ return {
]=],
full_name = 'conceallevel',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('whether concealable text is shown or hidden'),
type = 'number',
},
@@ -1619,7 +1605,7 @@ return {
See 'preserveindent'.
]=],
full_name = 'copyindent',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("make 'autoindent' use existing indent structure"),
type = 'boolean',
varname = 'p_ci',
@@ -1627,7 +1613,7 @@ return {
{
abbreviation = 'cpo',
cb = 'did_set_cpoptions',
- defaults = { if_true = macros('CPO_VIM') },
+ defaults = { if_true = macros('CPO_VIM', 'string') },
desc = [=[
A sequence of single character flags. When a character is present
this indicates Vi-compatible behavior. This is used for things where
@@ -1880,8 +1866,7 @@ return {
taken into account.
]=],
full_name = 'cursorbind',
- pv_name = 'p_crbind',
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('move cursor in window as it moves in other windows'),
type = 'boolean',
},
@@ -1900,7 +1885,7 @@ return {
]=],
full_name = 'cursorcolumn',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('highlight the screen column of the cursor'),
type = 'boolean',
},
@@ -1915,7 +1900,7 @@ return {
]=],
full_name = 'cursorline',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('highlight the screen line of the cursor'),
type = 'boolean',
},
@@ -1943,7 +1928,7 @@ return {
full_name = 'cursorlineopt',
list = 'onecomma',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_("settings for 'cursorline'"),
type = 'string',
},
@@ -1971,7 +1956,6 @@ return {
},
{
abbreviation = 'def',
- alloced = true,
defaults = { if_true = '' },
desc = [=[
Pattern to be used to find a macro definition. It is a search
@@ -1995,7 +1979,7 @@ return {
<
]=],
full_name = 'define',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('pattern to be used to find a macro definition'),
type = 'string',
varname = 'p_def',
@@ -2052,7 +2036,7 @@ return {
full_name = 'dictionary',
list = 'onecomma',
normal_dname_chars = true,
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('list of file names used for keyword completion'),
type = 'string',
varname = 'p_dict',
@@ -2067,7 +2051,7 @@ return {
full_name = 'diff',
noglob = true,
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('diff mode for the current window'),
type = 'boolean',
},
@@ -2091,7 +2075,6 @@ return {
},
{
abbreviation = 'dip',
- alloced = true,
cb = 'did_set_diffopt',
defaults = { if_true = 'internal,filler,closeoff' },
deny_duplicates = true,
@@ -2365,7 +2348,7 @@ return {
{
abbreviation = 'enc',
cb = 'did_set_encoding',
- defaults = { if_true = macros('ENC_DFLT') },
+ defaults = { if_true = macros('ENC_DFLT', 'string') },
deny_in_modelines = true,
desc = [=[
String-encoding used internally and for |RPC| communication.
@@ -2394,7 +2377,7 @@ return {
full_name = 'endoffile',
no_mkrc = true,
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('write CTRL-Z for last line in file'),
type = 'boolean',
varname = 'p_eof',
@@ -2420,7 +2403,7 @@ return {
full_name = 'endofline',
no_mkrc = true,
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('write <EOL> for last line in file'),
type = 'boolean',
varname = 'p_eol',
@@ -2465,7 +2448,7 @@ return {
]=],
expand = true,
full_name = 'equalprg',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('external program to use for "=" command'),
type = 'string',
@@ -2489,7 +2472,7 @@ return {
},
{
abbreviation = 'ef',
- defaults = { if_true = macros('DFLT_ERRORFILE') },
+ defaults = { if_true = macros('DFLT_ERRORFILE', 'string') },
desc = [=[
Name of the errorfile for the QuickFix mode (see |:cf|).
When the "-q" command-line argument is used, 'errorfile' is set to the
@@ -2511,7 +2494,7 @@ return {
{
abbreviation = 'efm',
defaults = {
- if_true = macros('DFLT_EFM'),
+ if_true = macros('DFLT_EFM', 'string'),
doc = 'is very long',
},
deny_duplicates = true,
@@ -2521,7 +2504,7 @@ return {
]=],
full_name = 'errorformat',
list = 'onecomma',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('description of the lines in the error file'),
type = 'string',
varname = 'p_efm',
@@ -2557,7 +2540,7 @@ return {
on, use CTRL-V<Tab>. See also |:retab| and |ins-expandtab|.
]=],
full_name = 'expandtab',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('use spaces when <Tab> is inserted'),
type = 'boolean',
varname = 'p_et',
@@ -2586,7 +2569,6 @@ return {
},
{
abbreviation = 'fenc',
- alloced = true,
cb = 'did_set_encoding',
defaults = { if_true = '' },
desc = [=[
@@ -2632,7 +2614,7 @@ return {
full_name = 'fileencoding',
no_mkrc = true,
redraw = { 'statuslines', 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('file encoding for multi-byte text'),
tags = { 'E213' },
type = 'string',
@@ -2700,10 +2682,11 @@ return {
},
{
abbreviation = 'ff',
- alloced = true,
cb = 'did_set_fileformat',
defaults = {
- if_true = macros('DFLT_FF'),
+ condition = 'USE_CRNL',
+ if_true = 'dos',
+ if_false = 'unix',
doc = 'Windows: "dos", Unix: "unix"',
},
desc = [=[
@@ -2727,7 +2710,7 @@ return {
full_name = 'fileformat',
no_mkrc = true,
redraw = { 'curswant', 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('file format used for file I/O'),
type = 'string',
varname = 'p_ff',
@@ -2736,7 +2719,9 @@ return {
abbreviation = 'ffs',
cb = 'did_set_fileformats',
defaults = {
- if_true = macros('DFLT_FFS_VIM'),
+ condition = 'USE_CRNL',
+ if_true = 'dos,unix',
+ if_false = 'unix,dos',
doc = 'Windows: "dos,unix", Unix: "unix,dos"',
},
deny_duplicates = true,
@@ -2816,7 +2801,6 @@ return {
},
{
abbreviation = 'ft',
- alloced = true,
cb = 'did_set_filetype_or_syntax',
defaults = { if_true = '' },
desc = [=[
@@ -2845,14 +2829,13 @@ return {
full_name = 'filetype',
noglob = true,
normal_fname_chars = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('type of file, used for autocommands'),
type = 'string',
varname = 'p_ft',
},
{
abbreviation = 'fcs',
- alloced = true,
cb = 'did_set_chars_option',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -2921,12 +2904,72 @@ return {
full_name = 'fillchars',
list = 'onecomma',
redraw = { 'current_window' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('characters to use for displaying special items'),
type = 'string',
varname = 'p_fcs',
},
{
+ abbreviation = 'ffu',
+ cb = 'did_set_findfunc',
+ defaults = { if_true = '' },
+ desc = [=[
+ Function that is called to obtain the filename(s) for the |:find|
+ command. When this option is empty, the internal |file-searching|
+ mechanism is used.
+
+ The value can be the name of a function, a |lambda| or a |Funcref|.
+ See |option-value-function| for more information.
+
+ The function is called with two arguments. The first argument is a
+ |String| and is the |:find| command argument. The second argument is
+ a |Boolean| and is set to |v:true| when the function is called to get
+ a List of command-line completion matches for the |:find| command.
+ The function should return a List of strings.
+
+ The function is called only once per |:find| command invocation.
+ The function can process all the directories specified in 'path'.
+
+ If a match is found, the function should return a |List| containing
+ one or more file names. If a match is not found, the function
+ should return an empty List.
+
+ If any errors are encountered during the function invocation, an
+ empty List is used as the return value.
+
+ It is not allowed to change text or jump to another window while
+ executing the 'findfunc' |textlock|.
+
+ This option cannot be set from a |modeline| or in the |sandbox|, for
+ security reasons.
+
+ Examples:
+ >vim
+ " Use glob()
+ func FindFuncGlob(cmdarg, cmdcomplete)
+ let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
+ return glob(pat, v:false, v:true)
+ endfunc
+ set findfunc=FindFuncGlob
+
+ " Use the 'git ls-files' output
+ func FindGitFiles(cmdarg, cmdcomplete)
+ let fnames = systemlist('git ls-files')
+ return fnames->filter('v:val =~? a:cmdarg')
+ endfunc
+ set findfunc=FindGitFiles
+ <
+ ]=],
+ full_name = 'findfunc',
+ func = true,
+ scope = { 'global', 'buf' },
+ secure = true,
+ short_desc = N_('function called for :find'),
+ tags = { 'E1514' },
+ type = 'string',
+ varname = 'p_ffu',
+ },
+ {
abbreviation = 'fixeol',
cb = 'did_set_eof_eol_fixeol_bomb',
defaults = { if_true = true },
@@ -2941,7 +2984,7 @@ return {
]=],
full_name = 'fixendofline',
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('make sure last line in file has <EOL>'),
type = 'boolean',
varname = 'p_fixeol',
@@ -2967,7 +3010,6 @@ return {
},
{
abbreviation = 'fdc',
- alloced = true,
cb = 'did_set_foldcolumn',
defaults = { if_true = '0' },
desc = [=[
@@ -2982,7 +3024,7 @@ return {
expand_cb = 'expand_set_foldcolumn',
full_name = 'foldcolumn',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('width of the column used to indicate folds'),
type = 'string',
},
@@ -3000,13 +3042,12 @@ return {
]=],
full_name = 'foldenable',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('set to display all folds open'),
type = 'boolean',
},
{
abbreviation = 'fde',
- alloced = true,
cb = 'did_set_foldexpr',
defaults = { if_true = '0' },
desc = [=[
@@ -3026,13 +3067,12 @@ return {
full_name = 'foldexpr',
modelineexpr = true,
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('expression used when \'foldmethod\' is "expr"'),
type = 'string',
},
{
abbreviation = 'fdi',
- alloced = true,
cb = 'did_set_foldignore',
defaults = { if_true = '#' },
desc = [=[
@@ -3043,7 +3083,7 @@ return {
]=],
full_name = 'foldignore',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('ignore lines when \'foldmethod\' is "indent"'),
type = 'string',
},
@@ -3060,7 +3100,7 @@ return {
]=],
full_name = 'foldlevel',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('close folds with a level higher than this'),
type = 'number',
},
@@ -3087,7 +3127,6 @@ return {
},
{
abbreviation = 'fmr',
- alloced = true,
cb = 'did_set_foldmarker',
defaults = { if_true = '{{{,}}}' },
deny_duplicates = true,
@@ -3100,14 +3139,13 @@ return {
full_name = 'foldmarker',
list = 'onecomma',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('markers used when \'foldmethod\' is "marker"'),
tags = { 'E536' },
type = 'string',
},
{
abbreviation = 'fdm',
- alloced = true,
cb = 'did_set_foldmethod',
defaults = { if_true = 'manual' },
desc = [=[
@@ -3122,7 +3160,7 @@ return {
expand_cb = 'expand_set_foldmethod',
full_name = 'foldmethod',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('folding type'),
type = 'string',
},
@@ -3141,7 +3179,7 @@ return {
]=],
full_name = 'foldminlines',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('minimum number of lines for a fold to be closed'),
type = 'number',
},
@@ -3156,7 +3194,7 @@ return {
]=],
full_name = 'foldnestmax',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('maximum fold depth'),
type = 'number',
},
@@ -3208,7 +3246,6 @@ return {
},
{
abbreviation = 'fdt',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = 'foldtext()' },
desc = [=[
@@ -3230,13 +3267,12 @@ return {
full_name = 'foldtext',
modelineexpr = true,
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('expression used to display for a closed fold'),
type = 'string',
},
{
abbreviation = 'fex',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -3283,14 +3319,13 @@ return {
]=],
full_name = 'formatexpr',
modelineexpr = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('expression used with "gq" command'),
type = 'string',
varname = 'p_fex',
},
{
abbreviation = 'flp',
- alloced = true,
defaults = { if_true = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*' },
desc = [=[
A pattern that is used to recognize a list header. This is used for
@@ -3304,16 +3339,15 @@ return {
character and white space.
]=],
full_name = 'formatlistpat',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('pattern used to recognize a list header'),
type = 'string',
varname = 'p_flp',
},
{
abbreviation = 'fo',
- alloced = true,
cb = 'did_set_formatoptions',
- defaults = { if_true = macros('DFLT_FO_VIM') },
+ defaults = { if_true = macros('DFLT_FO_VIM', 'string') },
desc = [=[
This is a sequence of letters which describes how automatic
formatting is to be done.
@@ -3325,7 +3359,7 @@ return {
expand_cb = 'expand_set_formatoptions',
full_name = 'formatoptions',
list = 'flags',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('how automatic formatting is to be done'),
type = 'string',
varname = 'p_fo',
@@ -3348,7 +3382,7 @@ return {
]=],
expand = true,
full_name = 'formatprg',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('name of external program used with "gq" command'),
type = 'string',
@@ -3406,7 +3440,7 @@ return {
},
{
abbreviation = 'gfm',
- defaults = { if_true = macros('DFLT_GREPFORMAT') },
+ defaults = { if_true = macros('DFLT_GREPFORMAT', 'string') },
deny_duplicates = true,
desc = [=[
Format to recognize for the ":grep" command output.
@@ -3456,7 +3490,7 @@ return {
]=],
expand = true,
full_name = 'grepprg',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('program to use for ":grep"'),
type = 'string',
@@ -3761,15 +3795,16 @@ return {
try to keep 'lines' and 'columns' the same when adding and
removing GUI components.
]=],
- enable_if = false,
full_name = 'guioptions',
list = 'flags',
scope = { 'global' },
short_desc = N_('GUI: Which components and options are used'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'gtl',
+ defaults = { if_true = '' },
desc = [=[
When non-empty describes the text to use in a label of the GUI tab
pages line. When empty and when the result is empty Vim will use a
@@ -3785,16 +3820,17 @@ return {
present in 'guioptions'. For the non-GUI tab pages line 'tabline' is
used.
]=],
- enable_if = false,
full_name = 'guitablabel',
modelineexpr = true,
redraw = { 'current_window' },
scope = { 'global' },
short_desc = N_('GUI: custom label for a tab page'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'gtt',
+ defaults = { if_true = '' },
desc = [=[
When non-empty describes the text to use in a tooltip for the GUI tab
pages line. When empty Vim will use a default tooltip.
@@ -3803,18 +3839,18 @@ return {
let &guitabtooltip = "line one\nline two"
<
]=],
- enable_if = false,
full_name = 'guitabtooltip',
redraw = { 'current_window' },
scope = { 'global' },
short_desc = N_('GUI: custom tooltip for a tab page'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'hf',
cb = 'did_set_helpfile',
defaults = {
- if_true = macros('DFLT_HELPFILE'),
+ if_true = macros('DFLT_HELPFILE', 'string'),
doc = [[(MS-Windows) "$VIMRUNTIME\doc\help.txt"
(others) "$VIMRUNTIME/doc/help.txt"]],
},
@@ -3911,7 +3947,7 @@ return {
{
abbreviation = 'hl',
cb = 'did_set_highlight',
- defaults = { if_true = macros('HIGHLIGHT_INIT') },
+ defaults = { if_true = macros('HIGHLIGHT_INIT', 'string') },
deny_duplicates = true,
full_name = 'highlight',
list = 'onecomma',
@@ -3926,7 +3962,8 @@ return {
desc = [=[
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing|).
+ each of these histories (see |cmdline-editing| and 'msghistory' for
+ the number of messages to remember).
The maximum value is 10000.
]=],
full_name = 'history',
@@ -4050,11 +4087,11 @@ return {
English characters directly, e.g., when it's used to type accented
characters with dead keys.
]=],
- enable_if = false,
full_name = 'imcmdline',
scope = { 'global' },
short_desc = N_('use IM when starting to edit a command line'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'imd',
@@ -4068,16 +4105,16 @@ return {
Currently this option is on by default for SGI/IRIX machines. This
may change in later releases.
]=],
- enable_if = false,
full_name = 'imdisable',
scope = { 'global' },
short_desc = N_('do not use the IM in any mode'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'imi',
cb = 'did_set_iminsert',
- defaults = { if_true = imacros('B_IMODE_NONE') },
+ defaults = { if_true = macros('B_IMODE_NONE', 'number') },
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used in
Insert mode. Valid values:
@@ -4095,15 +4132,14 @@ return {
It is also used for the argument of commands like "r" and "f".
]=],
full_name = 'iminsert',
- pv_name = 'p_imi',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('use :lmap or IM in Insert mode'),
type = 'number',
varname = 'p_iminsert',
},
{
abbreviation = 'ims',
- defaults = { if_true = imacros('B_IMODE_USE_INSERT') },
+ defaults = { if_true = macros('B_IMODE_USE_INSERT', 'number') },
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used when
entering a search pattern. Valid values:
@@ -4118,8 +4154,7 @@ return {
option to a valid keymap name.
]=],
full_name = 'imsearch',
- pv_name = 'p_ims',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('use :lmap or IM when typing a search pattern'),
type = 'number',
varname = 'p_imsearch',
@@ -4152,7 +4187,6 @@ return {
},
{
abbreviation = 'inc',
- alloced = true,
defaults = { if_true = '' },
desc = [=[
Pattern to be used to find an include command. It is a search
@@ -4167,14 +4201,13 @@ return {
See |option-backslash| about including spaces and backslashes.
]=],
full_name = 'include',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('pattern to be used to find an include file'),
type = 'string',
varname = 'p_inc',
},
{
abbreviation = 'inex',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -4210,7 +4243,7 @@ return {
]=],
full_name = 'includeexpr',
modelineexpr = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('expression used to process an include line'),
type = 'string',
varname = 'p_inex',
@@ -4259,7 +4292,6 @@ return {
},
{
abbreviation = 'inde',
- alloced = true,
cb = 'did_set_optexpr',
defaults = { if_true = '' },
desc = [=[
@@ -4306,14 +4338,13 @@ return {
]=],
full_name = 'indentexpr',
modelineexpr = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('expression used to obtain the indent of a line'),
type = 'string',
varname = 'p_inde',
},
{
abbreviation = 'indk',
- alloced = true,
defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
deny_duplicates = true,
desc = [=[
@@ -4324,7 +4355,7 @@ return {
]=],
full_name = 'indentkeys',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_("keys that trigger indenting with 'indentexpr'"),
type = 'string',
varname = 'p_indk',
@@ -4343,7 +4374,7 @@ return {
With 'noinfercase' the match is used as-is.
]=],
full_name = 'infercase',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('adjust case of match for keyword completion'),
type = 'boolean',
varname = 'p_inf',
@@ -4455,8 +4486,7 @@ return {
},
{
abbreviation = 'isk',
- alloced = true,
- cb = 'did_set_isopt',
+ cb = 'did_set_iskeyword',
defaults = { if_true = '@,48-57,_,192-255' },
deny_duplicates = true,
desc = [=[
@@ -4475,7 +4505,7 @@ return {
]=],
full_name = 'iskeyword',
list = 'comma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('characters included in keywords'),
type = 'string',
varname = 'p_isk',
@@ -4564,7 +4594,6 @@ return {
},
{
abbreviation = 'kmp',
- alloced = true,
cb = 'did_set_keymap',
defaults = { if_true = '' },
desc = [=[
@@ -4577,9 +4606,8 @@ return {
full_name = 'keymap',
normal_fname_chars = true,
pri_mkrc = true,
- pv_name = 'p_kmap',
redraw = { 'statuslines', 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('name of a keyboard mapping'),
type = 'string',
varname = 'p_keymap',
@@ -4631,7 +4659,7 @@ return {
]=],
expand = true,
full_name = 'keywordprg',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('program to use for the "K" command'),
type = 'string',
@@ -4802,14 +4830,14 @@ return {
]=],
full_name = 'linebreak',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('wrap long lines at a blank'),
type = 'boolean',
},
{
cb = 'did_set_lines_or_columns',
defaults = {
- if_true = imacros('DFLT_ROWS'),
+ if_true = macros('DFLT_ROWS', 'number'),
doc = '24 or terminal height',
},
desc = [=[
@@ -4866,7 +4894,7 @@ return {
calling an external program if 'equalprg' is empty.
]=],
full_name = 'lisp',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('indenting for Lisp'),
type = 'boolean',
varname = 'p_lisp',
@@ -4888,8 +4916,7 @@ return {
expand_cb = 'expand_set_lispoptions',
full_name = 'lispoptions',
list = 'onecomma',
- pv_name = 'p_lop',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('options for lisp indenting'),
type = 'string',
varname = 'p_lop',
@@ -4897,7 +4924,7 @@ return {
{
abbreviation = 'lw',
defaults = {
- if_true = macros('LISPWORD_VALUE'),
+ if_true = macros('LISPWORD_VALUE', 'string'),
doc = 'is very long',
},
deny_duplicates = true,
@@ -4907,8 +4934,7 @@ return {
]=],
full_name = 'lispwords',
list = 'onecomma',
- pv_name = 'p_lw',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('words that change how lisp indenting works'),
type = 'string',
varname = 'p_lispwords',
@@ -4935,13 +4961,12 @@ return {
]=],
full_name = 'list',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('<Tab> and <EOL>'),
type = 'boolean',
},
{
abbreviation = 'lcs',
- alloced = true,
cb = 'did_set_chars_option',
defaults = { if_true = 'tab:> ,trail:-,nbsp:+' },
deny_duplicates = true,
@@ -5047,7 +5072,7 @@ return {
full_name = 'listchars',
list = 'onecomma',
redraw = { 'current_window' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('characters for displaying in list mode'),
type = 'string',
varname = 'p_lcs',
@@ -5128,7 +5153,7 @@ return {
]=],
expand_cb = 'expand_set_encoding',
full_name = 'makeencoding',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('Converts the output of external commands'),
type = 'string',
varname = 'p_menc',
@@ -5155,7 +5180,7 @@ return {
]=],
expand = true,
full_name = 'makeprg',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('program to use for the ":make" command'),
type = 'string',
@@ -5163,7 +5188,6 @@ return {
},
{
abbreviation = 'mps',
- alloced = true,
cb = 'did_set_matchpairs',
defaults = { if_true = '(:),{:},[:]' },
deny_duplicates = true,
@@ -5186,7 +5210,7 @@ return {
]=],
full_name = 'matchpairs',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('pairs of characters that "%" can match'),
type = 'string',
varname = 'p_mps',
@@ -5207,12 +5231,12 @@ return {
},
{
abbreviation = 'mco',
- defaults = { if_true = imacros('MAX_MCO') },
+ defaults = { if_true = macros('MAX_MCO', 'number') },
full_name = 'maxcombine',
scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'),
type = 'number',
- hidden = true,
+ varname = 'p_mco',
},
{
abbreviation = 'mfd',
@@ -5348,7 +5372,7 @@ return {
no lines are checked. See |modeline|.
]=],
full_name = 'modeline',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('recognize modelines at start or end of file'),
type = 'boolean',
varname = 'p_ml',
@@ -5396,7 +5420,7 @@ return {
]=],
full_name = 'modifiable',
noglob = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('changes to the text are not possible'),
tags = { 'E21' },
type = 'boolean',
@@ -5432,7 +5456,7 @@ return {
full_name = 'modified',
no_mkrc = true,
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('buffer has been modified'),
type = 'boolean',
varname = 'p_mod',
@@ -5708,13 +5732,13 @@ return {
indicate no input when the hit-enter prompt is displayed (since
clicking the mouse has no effect in this state.)
]=],
- enable_if = false,
full_name = 'mouseshape',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('shape of the mouse pointer in different modes'),
tags = { 'E547' },
type = 'string',
+ immutable = true,
},
{
abbreviation = 'mouset',
@@ -5730,8 +5754,20 @@ return {
varname = 'p_mouset',
},
{
+ abbreviation = 'mhi',
+ defaults = { if_true = 500 },
+ desc = [=[
+ Determines how many entries are remembered in the |:messages| history.
+ The maximum value is 10000.
+ ]=],
+ full_name = 'msghistory',
+ scope = { 'global' },
+ short_desc = N_('how many messages are remembered'),
+ type = 'number',
+ varname = 'p_mhi',
+ },
+ {
abbreviation = 'nf',
- alloced = true,
cb = 'did_set_nrformats',
defaults = { if_true = 'bin,hex' },
deny_duplicates = true,
@@ -5780,7 +5816,7 @@ return {
expand_cb = 'expand_set_nrformats',
full_name = 'nrformats',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('number formats recognized for CTRL-A command'),
type = 'string',
varname = 'p_nf',
@@ -5814,7 +5850,7 @@ return {
]=],
full_name = 'number',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('print the line number in front of each line'),
type = 'boolean',
},
@@ -5836,13 +5872,12 @@ return {
]=],
full_name = 'numberwidth',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('number of columns used for the line number'),
type = 'number',
},
{
abbreviation = 'ofu',
- alloced = true,
cb = 'did_set_omnifunc',
defaults = { if_true = '' },
desc = [=[
@@ -5859,7 +5894,7 @@ return {
]=],
full_name = 'omnifunc',
func = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
secure = true,
short_desc = N_('function for filetype-specific completion'),
type = 'string',
@@ -5876,11 +5911,11 @@ return {
Note that on Windows editing "aux.h", "lpt1.txt" and the like also
result in editing a device.
]=],
- enable_if = false,
full_name = 'opendevice',
scope = { 'global' },
short_desc = N_('allow reading/writing devices on MS-Windows'),
type = 'boolean',
+ immutable = true,
},
{
abbreviation = 'opfunc',
@@ -5953,11 +5988,11 @@ return {
{
abbreviation = 'pt',
defaults = { if_true = '' },
- enable_if = false,
full_name = 'pastetoggle',
scope = { 'global' },
short_desc = N_('No description'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'pex',
@@ -6062,7 +6097,7 @@ return {
expand = true,
full_name = 'path',
list = 'comma',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('list of directories searched with "gf" et.al.'),
tags = { 'E343', 'E345', 'E347', 'E854' },
type = 'string',
@@ -6086,7 +6121,7 @@ return {
Use |:retab| to clean up white space.
]=],
full_name = 'preserveindent',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('preserve the indent structure when reindenting'),
type = 'boolean',
varname = 'p_pi',
@@ -6116,7 +6151,7 @@ return {
full_name = 'previewwindow',
noglob = true,
redraw = { 'statuslines' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('identifies the preview window'),
tags = { 'E590' },
type = 'boolean',
@@ -6226,7 +6261,6 @@ return {
},
{
abbreviation = 'qe',
- alloced = true,
defaults = { if_true = '\\' },
desc = [=[
The characters that are used to escape quotes in a string. Used for
@@ -6236,7 +6270,7 @@ return {
text "foo\"bar\\" considered to be one string.
]=],
full_name = 'quoteescape',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('escape characters used in a string'),
type = 'string',
varname = 'p_qe',
@@ -6258,7 +6292,7 @@ return {
full_name = 'readonly',
noglob = true,
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('disallow writing the buffer'),
type = 'boolean',
varname = 'p_ro',
@@ -6374,7 +6408,7 @@ return {
]=],
full_name = 'relativenumber',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('show relative line number in front of each line'),
type = 'boolean',
},
@@ -6431,13 +6465,12 @@ return {
]=],
full_name = 'rightleft',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('window is right-to-left oriented'),
type = 'boolean',
},
{
abbreviation = 'rlc',
- alloced = true,
cb = 'did_set_rightleftcmd',
defaults = { if_true = 'search' },
desc = [=[
@@ -6452,7 +6485,7 @@ return {
expand_cb = 'expand_set_rightleftcmd',
full_name = 'rightleftcmd',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('commands for which editing works right-to-left'),
type = 'string',
},
@@ -6492,7 +6525,6 @@ return {
},
{
abbreviation = 'ruf',
- alloced = true,
cb = 'did_set_rulerformat',
defaults = { if_true = '' },
desc = [=[
@@ -6635,8 +6667,7 @@ return {
]=],
full_name = 'scroll',
no_mkrc = true,
- pv_name = 'p_scroll',
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('lines to scroll with CTRL-U and CTRL-D'),
type = 'number',
},
@@ -6658,7 +6689,7 @@ return {
]=],
full_name = 'scrollback',
redraw = { 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('lines to scroll with CTRL-U and CTRL-D'),
type = 'number',
varname = 'p_scbk',
@@ -6679,8 +6710,7 @@ return {
with scroll-binding, but ":split file" does not.
]=],
full_name = 'scrollbind',
- pv_name = 'p_scbind',
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('scroll in window as other windows scroll'),
type = 'boolean',
},
@@ -6717,7 +6747,7 @@ return {
< For scrolling horizontally see 'sidescrolloff'.
]=],
full_name = 'scrolloff',
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('minimum nr. of lines above and below cursor'),
type = 'number',
varname = 'p_so',
@@ -7093,7 +7123,7 @@ return {
*shell-powershell*
To use PowerShell: >vim
let &shell = executable('pwsh') ? 'pwsh' : 'powershell'
- let &shellcmdflag = '-NoLogo -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';Remove-Alias -Force -ErrorAction SilentlyContinue tee;'
+ let &shellcmdflag = '-NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();$PSDefaultParameterValues[''Out-File:Encoding'']=''utf8'';$PSStyle.OutputRendering=''plaintext'';Remove-Alias -Force -ErrorAction SilentlyContinue tee;'
let &shellredir = '2>&1 | %%{ "$_" } | Out-File %s; exit $LastExitCode'
let &shellpipe = '2>&1 | %%{ "$_" } | tee %s; exit $LastExitCode'
set shellquote= shellxquote=
@@ -7251,9 +7281,14 @@ return {
{
abbreviation = 'ssl',
cb = 'did_set_shellslash',
- defaults = { if_true = false },
+ defaults = {
+ condition = 'MSWIN',
+ if_true = false,
+ if_false = true,
+ doc = 'on, Windows: off',
+ },
desc = [=[
- only for MS-Windows
+ only modifiable in MS-Windows
When set, a forward slash is used when expanding file names. This is
useful when a Unix-like shell is used instead of cmd.exe. Backward
slashes can still be typed, but they are changed to forward slashes by
@@ -7362,7 +7397,7 @@ return {
function to get the effective shiftwidth value.
]=],
full_name = 'shiftwidth',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('number of spaces to use for (auto)indent step'),
type = 'number',
varname = 'p_sw',
@@ -7460,7 +7495,7 @@ return {
]=],
full_name = 'showbreak',
redraw = { 'all_windows' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('string to use at the start of wrapped lines'),
tags = { 'E595' },
type = 'string',
@@ -7634,14 +7669,13 @@ return {
<
]=],
full_name = 'sidescrolloff',
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('min. nr. of columns to left and right of cursor'),
type = 'number',
varname = 'p_siso',
},
{
abbreviation = 'scl',
- alloced = true,
cb = 'did_set_signcolumn',
defaults = { if_true = 'auto' },
desc = [=[
@@ -7665,7 +7699,7 @@ return {
expand_cb = 'expand_set_signcolumn',
full_name = 'signcolumn',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('when to display the sign column'),
type = 'string',
},
@@ -7711,7 +7745,7 @@ return {
right.
]=],
full_name = 'smartindent',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('smart autoindenting for C programs'),
type = 'boolean',
varname = 'p_si',
@@ -7751,9 +7785,8 @@ return {
NOTE: partly implemented, doesn't work yet for |gj| and |gk|.
]=],
full_name = 'smoothscroll',
- pv_name = 'p_sms',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_("scroll by screen lines when 'wrap' is set"),
type = 'boolean',
},
@@ -7778,7 +7811,7 @@ return {
to anything other than an empty string.
]=],
full_name = 'softtabstop',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('number of spaces that <Tab> uses while editing'),
type = 'number',
varname = 'p_sts',
@@ -7792,13 +7825,12 @@ return {
]=],
full_name = 'spell',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('spell checking'),
type = 'boolean',
},
{
abbreviation = 'spc',
- alloced = true,
cb = 'did_set_spellcapcheck',
defaults = { if_true = '[.?!]\\_[\\])\'"\\t ]\\+' },
desc = [=[
@@ -7814,14 +7846,13 @@ return {
]=],
full_name = 'spellcapcheck',
redraw = { 'current_buffer', 'highlight_only' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('pattern to locate end of a sentence'),
type = 'string',
varname = 'p_spc',
},
{
abbreviation = 'spf',
- alloced = true,
cb = 'did_set_spellfile',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -7851,7 +7882,7 @@ return {
expand = true,
full_name = 'spellfile',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
secure = true,
short_desc = N_('files where |zg| and |zw| store words'),
type = 'string',
@@ -7859,7 +7890,6 @@ return {
},
{
abbreviation = 'spl',
- alloced = true,
cb = 'did_set_spelllang',
defaults = { if_true = 'en' },
deny_duplicates = true,
@@ -7905,7 +7935,7 @@ return {
full_name = 'spelllang',
list = 'onecomma',
redraw = { 'current_buffer', 'highlight_only' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('language(s) to do spell checking for'),
type = 'string',
varname = 'p_spl',
@@ -7930,7 +7960,7 @@ return {
full_name = 'spelloptions',
list = 'onecomma',
redraw = { 'current_buffer', 'highlight_only' },
- scope = { 'buffer' },
+ scope = { 'buf' },
secure = true,
type = 'string',
varname = 'p_spo',
@@ -8093,7 +8123,6 @@ return {
},
{
abbreviation = 'stc',
- alloced = true,
cb = 'did_set_statuscolumn',
defaults = { if_true = '' },
desc = [=[
@@ -8152,14 +8181,13 @@ return {
]=],
full_name = 'statuscolumn',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
secure = true,
short_desc = N_('custom format for the status column'),
type = 'string',
},
{
abbreviation = 'stl',
- alloced = true,
cb = 'did_set_statusline',
defaults = { if_true = '' },
desc = [=[
@@ -8356,7 +8384,7 @@ return {
Examples:
Emulate standard status line with 'ruler' set >vim
- set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
+ set statusline=%<%f\ %h%w%m%r%=%-14.(%l,%c%V%)\ %P
< Similar, but add ASCII value of char under the cursor (like "ga") >vim
set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P
< Display byte count and byte value, modified flag in red. >vim
@@ -8377,7 +8405,7 @@ return {
full_name = 'statusline',
modelineexpr = true,
redraw = { 'statuslines' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('custom format for the status line'),
tags = { 'E540', 'E542' },
type = 'string',
@@ -8408,7 +8436,6 @@ return {
},
{
abbreviation = 'sua',
- alloced = true,
defaults = { if_true = '' },
deny_duplicates = true,
desc = [=[
@@ -8419,7 +8446,7 @@ return {
]=],
full_name = 'suffixesadd',
list = 'onecomma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('suffixes added when searching for a file'),
type = 'string',
varname = 'p_sua',
@@ -8450,7 +8477,7 @@ return {
]=],
full_name = 'swapfile',
redraw = { 'statuslines' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('whether to use a swapfile for a buffer'),
type = 'boolean',
varname = 'p_swf',
@@ -8510,14 +8537,13 @@ return {
]=],
full_name = 'synmaxcol',
redraw = { 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('maximum column to find syntax items'),
type = 'number',
varname = 'p_smc',
},
{
abbreviation = 'syn',
- alloced = true,
cb = 'did_set_filetype_or_syntax',
defaults = { if_true = '' },
desc = [=[
@@ -8548,7 +8574,7 @@ return {
full_name = 'syntax',
noglob = true,
normal_fname_chars = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('syntax to be loaded for current buffer'),
type = 'string',
varname = 'p_syn',
@@ -8671,7 +8697,7 @@ return {
]=],
full_name = 'tabstop',
redraw = { 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('number of spaces that <Tab> in file uses'),
type = 'number',
varname = 'p_ts',
@@ -8750,7 +8776,7 @@ return {
]=],
expand_cb = 'expand_set_tagcase',
full_name = 'tagcase',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('how to handle case when searching in tags files'),
type = 'string',
varname = 'p_tc',
@@ -8771,7 +8797,7 @@ return {
]=],
full_name = 'tagfunc',
func = true,
- scope = { 'buffer' },
+ scope = { 'buf' },
secure = true,
short_desc = N_('function used to perform tag searches'),
type = 'string',
@@ -8828,7 +8854,7 @@ return {
expand = true,
full_name = 'tags',
list = 'onecomma',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('list of file names used by the tag command'),
tags = { 'E433' },
type = 'string',
@@ -8874,11 +8900,11 @@ return {
{
abbreviation = 'tenc',
defaults = { if_true = '' },
- enable_if = false,
full_name = 'termencoding',
scope = { 'global' },
short_desc = N_('Terminal encoding'),
type = 'string',
+ immutable = true,
},
{
abbreviation = 'tgc',
@@ -8968,7 +8994,7 @@ return {
]=],
full_name = 'textwidth',
redraw = { 'current_buffer', 'highlight_only' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('maximum width of text that is being inserted'),
type = 'number',
varname = 'p_tw',
@@ -8997,14 +9023,13 @@ return {
full_name = 'thesaurus',
list = 'onecomma',
normal_dname_chars = true,
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('list of thesaurus files for keyword completion'),
type = 'string',
varname = 'p_tsr',
},
{
abbreviation = 'tsrfu',
- alloced = true,
cb = 'did_set_thesaurusfunc',
defaults = { if_true = '' },
desc = [=[
@@ -9018,7 +9043,7 @@ return {
]=],
full_name = 'thesaurusfunc',
func = true,
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
secure = true,
short_desc = N_('function used for thesaurus completion'),
type = 'string',
@@ -9129,7 +9154,9 @@ return {
window. This happens only when the 'title' option is on.
When this option contains printf-style '%' items, they will be
- expanded according to the rules used for 'statusline'.
+ expanded according to the rules used for 'statusline'. If it contains
+ an invalid '%' format, the value is used as-is and no error or warning
+ will be given when the value is set.
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >vim
@@ -9250,7 +9277,7 @@ return {
When 'undofile' is turned off the undo file is NOT deleted.
]=],
full_name = 'undofile',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('save undo information in a file'),
type = 'boolean',
varname = 'p_udf',
@@ -9279,7 +9306,7 @@ return {
Also see |clear-undo|.
]=],
full_name = 'undolevels',
- scope = { 'global', 'buffer' },
+ scope = { 'global', 'buf' },
short_desc = N_('maximum number of changes that can be undone'),
type = 'number',
varname = 'p_ul',
@@ -9397,7 +9424,8 @@ return {
<
]=],
short_desc=N_("Function used to define behavior of user-defined registers."),
- type='string', scope={'buffer'},
+ type='string',
+ scope={'buf'},
varname='p_urf',
defaults={if_true=""}
},
@@ -9424,7 +9452,7 @@ return {
]=],
full_name = 'varsofttabstop',
list = 'comma',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('list of numbers of spaces that <Tab> uses while editing'),
type = 'string',
varname = 'p_vsts',
@@ -9447,7 +9475,7 @@ return {
full_name = 'vartabstop',
list = 'comma',
redraw = { 'current_buffer' },
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('list of numbers of spaces that <Tab> in file uses'),
type = 'string',
varname = 'p_vts',
@@ -9593,7 +9621,7 @@ return {
full_name = 'virtualedit',
list = 'onecomma',
redraw = { 'curswant' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('when to use virtual editing'),
type = 'string',
varname = 'p_ve',
@@ -9666,7 +9694,7 @@ return {
abbreviation = 'wc',
cb = 'did_set_wildchar',
defaults = {
- if_true = imacros('TAB'),
+ if_true = macros('TAB', 'number'),
doc = '<Tab>',
},
desc = [=[
@@ -9920,7 +9948,6 @@ return {
},
{
abbreviation = 'wbr',
- alloced = true,
cb = 'did_set_winbar',
defaults = { if_true = '' },
desc = [=[
@@ -9941,7 +9968,7 @@ return {
full_name = 'winbar',
modelineexpr = true,
redraw = { 'statuslines' },
- scope = { 'global', 'window' },
+ scope = { 'global', 'win' },
short_desc = N_('custom format for the window bar'),
type = 'string',
varname = 'p_wbr',
@@ -9959,7 +9986,7 @@ return {
]=],
full_name = 'winblend',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('Controls transparency level for floating windows'),
type = 'number',
},
@@ -9998,8 +10025,7 @@ return {
command has a "!" modifier, it can force switching buffers.
]=],
full_name = 'winfixbuf',
- pv_name = 'p_wfb',
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('pin a window to a specific buffer'),
type = 'boolean',
},
@@ -10014,7 +10040,7 @@ return {
]=],
full_name = 'winfixheight',
redraw = { 'statuslines' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('keep window height when opening/closing windows'),
type = 'boolean',
},
@@ -10028,7 +10054,7 @@ return {
]=],
full_name = 'winfixwidth',
redraw = { 'statuslines' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('keep window width when opening/closing windows'),
type = 'boolean',
},
@@ -10063,7 +10089,6 @@ return {
},
{
abbreviation = 'winhl',
- alloced = true,
cb = 'did_set_winhighlight',
defaults = { if_true = '' },
deny_duplicates = true,
@@ -10090,7 +10115,7 @@ return {
full_name = 'winhighlight',
list = 'onecommacolon',
redraw = { 'current_window', 'highlight_only' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('Setup window-local highlights'),
type = 'string',
},
@@ -10181,7 +10206,7 @@ return {
]=],
full_name = 'wrap',
redraw = { 'current_window' },
- scope = { 'window' },
+ scope = { 'win' },
short_desc = N_('lines wrap and continue on the next line'),
type = 'boolean',
},
@@ -10198,7 +10223,7 @@ return {
See also 'formatoptions' and |ins-textwidth|.
]=],
full_name = 'wrapmargin',
- scope = { 'buffer' },
+ scope = { 'buf' },
short_desc = N_('chars from the right where wrapping starts'),
type = 'number',
varname = 'p_wm',
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index c8f19d7ccf..bfb26a0be6 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -72,8 +72,9 @@ static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhardlink", NULL };
static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error",
- "esc", "ex", "hangul", "lang", "mess", "showmatch", "operator",
- "register", "shell", "spell", "wildmode", NULL };
+ "esc", "ex", "hangul", "insertmode", "lang", "mess", "showmatch",
+ "operator", "register", "shell", "spell", "term", "wildmode",
+ NULL };
// Note: Keep this in sync with briopt_check()
static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL };
// Note: Keep this in sync with diffopt_changed()
@@ -83,7 +84,7 @@ static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase",
"indent-heuristic", "linematch:", "algorithm:", NULL };
static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL };
static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", "blank", NULL };
-static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
+static char *(p_ff_values[]) = { "unix", "dos", "mac", NULL };
static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
// Note: Keep this in sync with fill_culopt_flags()
@@ -234,6 +235,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_ep);
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
+ check_string_option(&buf->b_p_ffu);
check_string_option(&buf->b_p_tfu);
check_string_option(&buf->b_p_tc);
check_string_option(&buf->b_p_dict);
@@ -249,7 +251,6 @@ void check_buf_options(buf_T *buf)
/// Free the string allocated for an option.
/// Checks for the string being empty_string_option. This may happen if we're out of memory,
/// xstrdup() returned NULL, which was replaced by empty_string_option by check_options().
-/// Does NOT check for P_ALLOCED flag!
void free_string_option(char *p)
{
if (p != empty_string_option) {
@@ -282,15 +283,27 @@ static bool valid_filetype(const char *val)
/// Handle setting 'signcolumn' for value 'val'. Store minimum and maximum width.
///
+/// @param wcl when NULL: use "wp->w_p_scl"
+/// @param wp when NULL: only parse "scl"
+///
/// @return OK when the value is valid, FAIL otherwise
-int check_signcolumn(win_T *wp)
+int check_signcolumn(char *scl, win_T *wp)
{
- char *val = wp->w_p_scl;
+ char *val = empty_string_option;
+ if (scl != NULL) {
+ val = scl;
+ } else if (wp != NULL) {
+ val = wp->w_p_scl;
+ }
+
if (*val == NUL) {
return FAIL;
}
if (check_opt_strings(val, p_scl_values, false) == OK) {
+ if (wp == NULL) {
+ return OK;
+ }
if (!strncmp(val, "no", 2)) { // no
wp->w_minscwidth = wp->w_maxscwidth = SCL_NO;
} else if (!strncmp(val, "nu", 2) && (wp->w_p_nu || wp->w_p_rnu)) { // number
@@ -320,6 +333,9 @@ int check_signcolumn(win_T *wp)
if (min < 1 || max < 2 || min > 8 || min >= max) {
return FAIL;
}
+ if (wp == NULL) {
+ return OK;
+ }
wp->w_minscwidth = min;
wp->w_maxscwidth = max;
}
@@ -404,9 +420,9 @@ const char *check_stl_option(char *s)
/// often illegal in a file name. Be more permissive if "secure" is off.
bool check_illegal_path_names(char *val, uint32_t flags)
{
- return (((flags & P_NFNAME)
+ return (((flags & kOptFlagNFname)
&& strpbrk(val, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
- || ((flags & P_NDNAME)
+ || ((flags & kOptFlagNDname)
&& strpbrk(val, "*?[|;&<>\r\n") != NULL));
}
@@ -639,6 +655,9 @@ const char *did_set_backupcopy(optset_T *args)
if (opt_flags & OPT_LOCAL) {
bkc = buf->b_p_bkc;
flags = &buf->b_bkc_flags;
+ } else if (!(opt_flags & OPT_GLOBAL)) {
+ // When using :set, clear the local flags.
+ buf->b_bkc_flags = 0;
}
if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
@@ -716,11 +735,14 @@ const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED)
const char *did_set_breakindentopt(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
- if (briopt_check(win) == FAIL) {
+ char **varp = (char **)args->os_varp;
+
+ if (briopt_check(*varp, varp == &win->w_p_briopt ? win : NULL) == FAIL) {
return e_invarg;
}
+
// list setting requires a redraw
- if (win == curwin && win->w_briopt_list) {
+ if (varp == &win->w_p_briopt && win->w_briopt_list) {
redraw_all_later(UPD_NOT_VALID);
}
@@ -870,10 +892,11 @@ int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches)
}
/// The 'cinoptions' option is changed.
-const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_cinoptions(optset_T *args)
{
+ buf_T *buf = (buf_T *)args->os_buf;
// TODO(vim): recognize errors
- parse_cino(curbuf);
+ parse_cino(buf);
return NULL;
}
@@ -897,7 +920,8 @@ int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches)
const char *did_set_colorcolumn(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
- return check_colorcolumn(win);
+ char **varp = (char **)args->os_varp;
+ return check_colorcolumn(*varp, varp == &win->w_p_cc ? win : NULL);
}
/// The 'comments' option is changed.
@@ -1050,6 +1074,9 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
if (args->os_flags & OPT_LOCAL) {
cot = buf->b_p_cot;
flags = &buf->b_cot_flags;
+ } else if (!(args->os_flags & OPT_GLOBAL)) {
+ // When using :set, clear the local flags.
+ buf->b_cot_flags = 0;
}
if (check_opt_strings(cot, p_cot_values, true) != OK) {
@@ -1358,7 +1385,7 @@ const char *did_set_filetype_or_syntax(optset_T *args)
args->os_value_changed = strcmp(args->os_oldval.string.data, *varp) != 0;
- // Since we check the value, there is no need to set P_INSECURE,
+ // Since we check the value, there is no need to set kOptFlagInsecure,
// even when the value comes from a modeline.
args->os_value_checked = true;
@@ -1447,8 +1474,7 @@ const char *did_set_foldmethod(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
char **varp = (char **)args->os_varp;
- if (check_opt_strings(*varp, p_fdm_values, false) != OK
- || *win->w_p_fdm == NUL) {
+ if (check_opt_strings(*varp, p_fdm_values, false) != OK || **varp == NUL) {
return e_invarg;
}
foldUpdateAll(win);
@@ -1572,12 +1598,28 @@ int expand_set_inccommand(optexpand_T *args, int *numMatches, char ***matches)
matches);
}
+/// The 'iskeyword' option is changed.
+const char *did_set_iskeyword(optset_T *args)
+{
+ char **varp = (char **)args->os_varp;
+
+ if (varp == &p_isk) { // only check for global-value
+ if (check_isopt(*varp) == FAIL) {
+ return e_invarg;
+ }
+ } else { // fallthrough for local-value
+ return did_set_isopt(args);
+ }
+
+ return NULL;
+}
+
/// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is
/// changed.
const char *did_set_isopt(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
+ // 'isident', 'iskeyword', 'isprint' or 'isfname' option: refill g_chartab[]
// If the new option is invalid, use old value.
// 'lisp' option: refill g_chartab[] for '-' char
if (buf_init_chartab(buf, true) == FAIL) {
@@ -1624,7 +1666,7 @@ const char *did_set_keymap(optset_T *args)
secure = secure_save;
- // Since we check the value, there is no need to set P_INSECURE,
+ // Since we check the value, there is no need to set kOptFlagInsecure,
// even when the value comes from a modeline.
args->os_value_checked = true;
@@ -2079,8 +2121,9 @@ int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches)
const char *did_set_signcolumn(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
+ char **varp = (char **)args->os_varp;
const char *oldval = args->os_oldval.string.data;
- if (check_signcolumn(win) != OK) {
+ if (check_signcolumn(*varp, varp == &win->w_p_scl ? win : NULL) != OK) {
return e_invarg;
}
// When changing the 'signcolumn' to or from 'number', recompute the
@@ -2115,10 +2158,10 @@ const char *did_set_spellfile(optset_T *args)
// When there is a window for this buffer in which 'spell'
// is set load the wordlists.
- if ((!valid_spellfile(*varp))) {
+ if (!valid_spellfile(*varp)) {
return e_invarg;
}
- return did_set_spell_option(true);
+ return did_set_spell_option();
}
/// The 'spelllang' option is changed.
@@ -2131,15 +2174,22 @@ const char *did_set_spelllang(optset_T *args)
if (!valid_spelllang(*varp)) {
return e_invarg;
}
- return did_set_spell_option(false);
+ return did_set_spell_option();
}
/// The 'spelloptions' option is changed.
const char *did_set_spelloptions(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
- if (opt_strings_flags(win->w_s->b_p_spo, p_spo_values, &(win->w_s->b_p_spo_flags),
- true) != OK) {
+ int opt_flags = args->os_flags;
+ const char *val = args->os_newval.string.data;
+
+ if (!(opt_flags & OPT_LOCAL)
+ && opt_strings_flags(val, p_spo_values, &spo_flags, true) != OK) {
+ return e_invarg;
+ }
+ if (!(opt_flags & OPT_GLOBAL)
+ && opt_strings_flags(val, p_spo_values, &win->w_s->b_p_spo_flags, true) != OK) {
return e_invarg;
}
return NULL;
@@ -2541,7 +2591,8 @@ const char *did_set_winbar(optset_T *args)
const char *did_set_winhighlight(optset_T *args)
{
win_T *win = (win_T *)args->os_win;
- if (!parse_winhl_opt(win)) {
+ char **varp = (char **)args->os_varp;
+ if (!parse_winhl_opt(*varp, varp == &win->w_p_winhl ? win : NULL)) {
return e_invarg;
}
return NULL;
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 7c5293a8b8..2d17581bac 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -289,9 +289,10 @@ size_t input_enqueue(String keys)
unsigned new_size
= trans_special(&ptr, (size_t)(end - ptr), (char *)buf, FSK_KEYCODE, true, NULL);
- if (new_size) {
- new_size = handle_mouse_event(&ptr, buf, new_size);
- input_enqueue_raw((char *)buf, new_size);
+ if (new_size > 0) {
+ if ((new_size = handle_mouse_event(&ptr, buf, new_size)) > 0) {
+ input_enqueue_raw((char *)buf, new_size);
+ }
continue;
}
@@ -326,7 +327,7 @@ size_t input_enqueue(String keys)
return rv;
}
-static uint8_t check_multiclick(int code, int grid, int row, int col)
+static uint8_t check_multiclick(int code, int grid, int row, int col, bool *skip_event)
{
static int orig_num_clicks = 0;
static int orig_mouse_code = 0;
@@ -335,24 +336,29 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
static int orig_mouse_row = 0;
static uint64_t orig_mouse_time = 0; // time of previous mouse click
- if ((code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) || code == KE_MOUSEMOVE) {
+ if (code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) {
return 0;
}
- // For click events the number of clicks is updated.
- if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE
- || code == KE_X1MOUSE || code == KE_X2MOUSE) {
+ bool no_move = orig_mouse_grid == grid && orig_mouse_col == col && orig_mouse_row == row;
+
+ if (code == KE_MOUSEMOVE) {
+ if (no_move) {
+ *skip_event = true;
+ return 0;
+ }
+ } else if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE
+ || code == KE_X1MOUSE || code == KE_X2MOUSE) {
+ // For click events the number of clicks is updated.
uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
+ // Compute the time elapsed since the previous mouse click.
uint64_t timediff = mouse_time - orig_mouse_time;
+ // Convert 'mousetime' from ms to ns.
uint64_t mouset = (uint64_t)p_mouset * 1000000;
if (code == orig_mouse_code
+ && no_move
&& timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_grid == grid
- && orig_mouse_col == col
- && orig_mouse_row == row) {
+ && orig_num_clicks != 4) {
orig_num_clicks++;
} else {
orig_num_clicks = 1;
@@ -367,12 +373,14 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
orig_mouse_row = row;
uint8_t modifiers = 0;
- if (orig_num_clicks == 2) {
- modifiers |= MOD_MASK_2CLICK;
- } else if (orig_num_clicks == 3) {
- modifiers |= MOD_MASK_3CLICK;
- } else if (orig_num_clicks == 4) {
- modifiers |= MOD_MASK_4CLICK;
+ if (code != KE_MOUSEMOVE) {
+ if (orig_num_clicks == 2) {
+ modifiers |= MOD_MASK_2CLICK;
+ } else if (orig_num_clicks == 3) {
+ modifiers |= MOD_MASK_3CLICK;
+ } else if (orig_num_clicks == 4) {
+ modifiers |= MOD_MASK_4CLICK;
+ }
}
return modifiers;
}
@@ -399,7 +407,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
return bufsize;
}
- // a <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
+ // A <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
// global variables. This is ugly but its how the rest of the code expects to
// find mouse coordinates, and it would be too expensive to refactor this
// now.
@@ -421,8 +429,12 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
*ptr += advance;
}
+ bool skip_event = false;
uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
- mouse_row, mouse_col);
+ mouse_row, mouse_col, &skip_event);
+ if (skip_event) {
+ return 0;
+ }
if (modifiers) {
if (buf[1] != KS_MODIFIER) {
@@ -443,7 +455,11 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
void input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col)
{
- modifier |= check_multiclick(code, grid, row, col);
+ bool skip_event = false;
+ modifier |= check_multiclick(code, grid, row, col, &skip_event);
+ if (skip_event) {
+ return;
+ }
uint8_t buf[7];
uint8_t *p = buf;
if (modifier) {
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index efcdee9c8b..81b75dc4d3 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -885,9 +885,9 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
// Failed, probably 'shell' is not executable.
if (!silent) {
msg_puts(_("\nshell failed to start: "));
- msg_outtrans(os_strerror(status), 0);
+ msg_outtrans(os_strerror(status), 0, false);
msg_puts(": ");
- msg_outtrans(prog, 0);
+ msg_outtrans(prog, 0, false);
msg_putchar('\n');
}
multiqueue_free(events);
@@ -1102,7 +1102,7 @@ static void out_data_append_to_screen(const char *output, size_t *count, bool eo
const char *end = output + *count;
while (p < end) {
if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) {
- msg_putchar_attr((uint8_t)(*p), 0);
+ msg_putchar_hl((uint8_t)(*p), 0);
p++;
} else {
// Note: this is not 100% precise:
@@ -1118,7 +1118,7 @@ static void out_data_append_to_screen(const char *output, size_t *count, bool eo
goto end;
}
- msg_outtrans_len(p, i, 0);
+ msg_outtrans_len(p, i, 0, false);
p += i;
}
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 9cce504831..80890acb7d 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1538,6 +1538,13 @@ void simplify_filename(char *filename)
} while (vim_ispathsep(*p));
}
char *start = p; // remember start after "c:/" or "/" or "///"
+#ifdef UNIX
+ // Posix says that "//path" is unchanged but "///path" is "/path".
+ if (start > filename + 2) {
+ STRMOVE(filename + 1, p);
+ start = p = filename + 1;
+ }
+#endif
do {
// At this point "p" is pointing to the char following a single "/"
@@ -1827,7 +1834,7 @@ char *fix_fname(const char *fname)
fname = xstrdup(fname);
-# ifdef USE_FNAME_CASE
+# ifdef CASE_INSENSITIVE_FILENAME
path_fix_case((char *)fname); // set correct case for file name
# endif
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index ddcb819054..7df6a1a5d7 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -480,13 +480,19 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
for (int i = 0; i < ga->ga_len; i++) {
if (char_pos == ((uint32_t *)ga->ga_data)[i]) {
new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
break;
}
}
} else if (matched_start && ptr < text + leader_len) {
new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
+ new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
}
+ new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), new_attr);
+
if (user_hlattr > 0) {
new_attr = hl_combine_attr(new_attr, user_hlattr);
}
@@ -629,6 +635,7 @@ void pum_redraw(void)
const hlf_T *const hlfs = (idx == pum_selected) ? hlfsSel : hlfsNorm;
hlf_T hlf = hlfs[0]; // start with "word" highlight
int attr = win_hl_attr(curwin, (int)hlf);
+ attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
grid_line_start(&pum_grid, row);
@@ -650,15 +657,21 @@ void pum_redraw(void)
pum_align_order(order);
int basic_width = items_width_array[order[0]]; // first item width
bool last_isabbr = order[2] == CPT_ABBR;
+ int orig_attr = -1;
+
for (int j = 0; j < 3; j++) {
int item_type = order[j];
hlf = hlfs[item_type];
attr = win_hl_attr(curwin, (int)hlf);
- if (pum_array[idx].pum_user_hlattr > 0) {
- attr = hl_combine_attr(attr, pum_array[idx].pum_user_hlattr);
+ attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
+ orig_attr = attr;
+ int user_abbr_hlattr = pum_array[idx].pum_user_abbr_hlattr;
+ int user_kind_hlattr = pum_array[idx].pum_user_kind_hlattr;
+ if (item_type == CPT_ABBR && user_abbr_hlattr > 0) {
+ attr = hl_combine_attr(attr, user_abbr_hlattr);
}
- if (item_type == CPT_KIND && pum_array[idx].pum_user_kind_hlattr > 0) {
- attr = hl_combine_attr(attr, pum_array[idx].pum_user_kind_hlattr);
+ if (item_type == CPT_KIND && user_kind_hlattr > 0) {
+ attr = hl_combine_attr(attr, user_kind_hlattr);
}
int width = 0;
char *s = NULL;
@@ -684,8 +697,10 @@ void pum_redraw(void)
*p = saved;
}
- int user_hlattr = pum_array[idx].pum_user_hlattr;
- int *attrs = pum_compute_text_attrs(st, hlf, user_hlattr);
+ int *attrs = NULL;
+ if (item_type == CPT_ABBR) {
+ attrs = pum_compute_text_attrs(st, hlf, user_abbr_hlattr);
+ }
if (pum_rl) {
char *rt = reverse_text(st);
@@ -727,7 +742,9 @@ void pum_redraw(void)
grid_col += width;
}
- xfree(attrs);
+ if (attrs != NULL) {
+ XFREE_CLEAR(attrs);
+ }
if (*p != TAB) {
break;
@@ -769,19 +786,21 @@ void pum_redraw(void)
}
if (pum_rl) {
- grid_line_fill(col_off - basic_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr);
+ grid_line_fill(col_off - basic_width - n + 1, grid_col + 1,
+ schar_from_ascii(' '), orig_attr);
grid_col = col_off - basic_width - n;
} else {
- grid_line_fill(grid_col, col_off + basic_width + n, schar_from_ascii(' '), attr);
+ grid_line_fill(grid_col, col_off + basic_width + n,
+ schar_from_ascii(' '), orig_attr);
grid_col = col_off + basic_width + n;
}
totwidth = basic_width + n;
}
if (pum_rl) {
- grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), attr);
+ grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), orig_attr);
} else {
- grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), attr);
+ grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), orig_attr);
}
if (pum_scrollbar > 0) {
@@ -1341,14 +1360,15 @@ static void pum_select_mouse_pos(void)
if (mouse_grid == pum_grid.handle) {
pum_selected = mouse_row;
return;
- } else if (mouse_grid != pum_anchor_grid) {
+ } else if (mouse_grid != pum_anchor_grid || mouse_col < pum_grid.comp_col
+ || mouse_col >= pum_grid.comp_col + pum_grid.comp_width) {
pum_selected = -1;
return;
}
- int idx = mouse_row - pum_row;
+ int idx = mouse_row - pum_grid.comp_row;
- if (idx < 0 || idx >= pum_height) {
+ if (idx < 0 || idx >= pum_grid.comp_height) {
pum_selected = -1;
} else if (*pum_array[idx].pum_text != NUL) {
pum_selected = idx;
diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h
index f7eb0240ae..40bd42ee17 100644
--- a/src/nvim/popupmenu.h
+++ b/src/nvim/popupmenu.h
@@ -16,7 +16,7 @@ typedef struct {
char *pum_info; ///< extra info
int pum_score; ///< fuzzy match score
int pum_idx; ///< index of item before sorting by score
- int pum_user_hlattr; ///< highlight attribute to combine with
+ int pum_user_abbr_hlattr; ///< highlight attribute for abbr
int pum_user_kind_hlattr; ///< highlight attribute for kind
} pumitem_T;
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index 81207b4e39..1b4f4a2029 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -956,7 +956,7 @@ void time_init(const char *fname, const char *proc_name)
const size_t bufsize = 8192; // Big enough for the entire --startuptime report.
time_fd = fopen(fname, "a");
if (time_fd == NULL) {
- semsg(_(e_notopen), fname);
+ fprintf(stderr, _(e_notopen), fname);
return;
}
startuptime_buf = xmalloc(sizeof(char) * (bufsize + 1));
@@ -968,8 +968,7 @@ void time_init(const char *fname, const char *proc_name)
XFREE_CLEAR(startuptime_buf);
fclose(time_fd);
time_fd = NULL;
- ELOG("time_init: setvbuf failed: %d %s", r, uv_err_name(r));
- semsg("time_init: setvbuf failed: %d %s", r, uv_err_name(r));
+ fprintf(stderr, "time_init: setvbuf failed: %d %s", r, uv_err_name(r));
return;
}
fprintf(time_fd, "--- Startup times for process: %s ---\n", proc_name);
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index ddf2a7247f..6526b0d0bf 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2937,7 +2937,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf
msg_scroll = false;
}
msg_ext_set_kind("quickfix");
- msg_attr_keep(gap->ga_data, 0, true, false);
+ msg_hl_keep(gap->ga_data, 0, true, false);
msg_scroll = (int)i;
qfga_clear();
@@ -3144,10 +3144,10 @@ theend:
decr_quickfix_busy();
}
-// Highlight attributes used for displaying entries from the quickfix list.
-static int qfFileAttr;
-static int qfSepAttr;
-static int qfLineAttr;
+// Highlight ids used for displaying entries from the quickfix list.
+static int qfFile_hl_id;
+static int qfSep_hl_id;
+static int qfLine_hl_id;
/// Display information about a single entry from the quickfix/location list.
/// Used by ":clist/:llist" commands.
@@ -3195,10 +3195,10 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
}
msg_putchar('\n');
- msg_outtrans(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
+ msg_outtrans(IObuff, cursel ? HLF_QFL : qfFile_hl_id, false);
if (qfp->qf_lnum != 0) {
- msg_puts_attr(":", qfSepAttr);
+ msg_puts_hl(":", qfSep_hl_id, false);
}
garray_T *gap = qfga_get();
if (qfp->qf_lnum != 0) {
@@ -3206,14 +3206,14 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
}
ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr));
ga_append(gap, NUL);
- msg_puts_attr(gap->ga_data, qfLineAttr);
- msg_puts_attr(":", qfSepAttr);
+ msg_puts_hl(gap->ga_data, qfLine_hl_id, false);
+ msg_puts_hl(":", qfSep_hl_id, false);
if (qfp->qf_pattern != NULL) {
gap = qfga_get();
qf_fmt_text(gap, qfp->qf_pattern);
ga_append(gap, NUL);
msg_puts(gap->ga_data);
- msg_puts_attr(":", qfSepAttr);
+ msg_puts_hl(":", qfSep_hl_id, false);
}
msg_puts(" ");
@@ -3275,17 +3275,17 @@ void qf_list(exarg_T *eap)
// Get the attributes for the different quickfix highlight items. Note
// that this depends on syntax items defined in the qf.vim syntax file
- qfFileAttr = syn_name2attr("qfFileName");
- if (qfFileAttr == 0) {
- qfFileAttr = HL_ATTR(HLF_D);
+ qfFile_hl_id = syn_name2id("qfFileName");
+ if (qfFile_hl_id == 0) {
+ qfFile_hl_id = HLF_D;
}
- qfSepAttr = syn_name2attr("qfSeparator");
- if (qfSepAttr == 0) {
- qfSepAttr = HL_ATTR(HLF_D);
+ qfSep_hl_id = syn_name2id("qfSeparator");
+ if (qfSep_hl_id == 0) {
+ qfSep_hl_id = HLF_D;
}
- qfLineAttr = syn_name2attr("qfLineNr");
- if (qfLineAttr == 0) {
- qfLineAttr = HL_ATTR(HLF_N);
+ qfLine_hl_id = syn_name2id("qfLineNr");
+ if (qfLine_hl_id == 0) {
+ qfLine_hl_id = HLF_N;
}
if (qfl->qf_nonevalid) {
@@ -4396,7 +4396,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname)
}
msg_start();
msg_puts(":!");
- msg_outtrans(cmd, 0); // show what we are doing
+ msg_outtrans(cmd, 0, false); // show what we are doing
return cmd;
}
@@ -5243,9 +5243,9 @@ static void vgr_display_fname(char *fname)
msg_start();
char *p = msg_strtrunc(fname, true);
if (p == NULL) {
- msg_outtrans(fname, 0);
+ msg_outtrans(fname, 0, false);
} else {
- msg_outtrans(p, 0);
+ msg_outtrans(p, 0, false);
xfree(p);
}
msg_clr_eos();
@@ -6462,6 +6462,64 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, dict_T *d, bool first_entry, b
return status;
}
+/// Check if `entry` is closer to the target than `other_entry`.
+///
+/// Only returns true if `entry` is definitively closer. If it's further
+/// away, or there's not enough information to tell, return false.
+static bool entry_is_closer_to_target(qfline_T *entry, qfline_T *other_entry, int target_fnum,
+ int target_lnum, int target_col)
+{
+ // First, compare entries to target file.
+ if (!target_fnum) {
+ // Without a target file, we can't know which is closer.
+ return false;
+ }
+
+ bool is_target_file = entry->qf_fnum && entry->qf_fnum == target_fnum;
+ bool other_is_target_file = other_entry->qf_fnum && other_entry->qf_fnum == target_fnum;
+ if (!is_target_file && other_is_target_file) {
+ return false;
+ } else if (is_target_file && !other_is_target_file) {
+ return true;
+ }
+
+ // Both entries are pointing at the exact same file. Now compare line numbers.
+ if (!target_lnum) {
+ // Without a target line number, we can't know which is closer.
+ return false;
+ }
+
+ int line_distance = entry->qf_lnum
+ ? abs(entry->qf_lnum - target_lnum) : INT_MAX;
+ int other_line_distance = other_entry->qf_lnum
+ ? abs(other_entry->qf_lnum - target_lnum) : INT_MAX;
+ if (line_distance > other_line_distance) {
+ return false;
+ } else if (line_distance < other_line_distance) {
+ return true;
+ }
+
+ // Both entries are pointing at the exact same line number (or no line
+ // number at all). Now compare columns.
+ if (!target_col) {
+ // Without a target column, we can't know which is closer.
+ return false;
+ }
+
+ int column_distance = entry->qf_col
+ ? abs(entry->qf_col - target_col) : INT_MAX;
+ int other_column_distance = other_entry->qf_col
+ ? abs(other_entry->qf_col - target_col) : INT_MAX;
+ if (column_distance > other_column_distance) {
+ return false;
+ } else if (column_distance < other_column_distance) {
+ return true;
+ }
+
+ // It's a complete tie! The exact same file, line, and column.
+ return false;
+}
+
/// Add list of entries to quickfix/location list. Each list entry is
/// a dictionary with item information.
static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, int action)
@@ -6471,19 +6529,48 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title,
int retval = OK;
bool valid_entry = false;
+ // If there's an entry selected in the quickfix list, remember its location
+ // (file, line, column), so we can select the nearest entry in the updated
+ // quickfix list.
+ int prev_fnum = 0;
+ int prev_lnum = 0;
+ int prev_col = 0;
+ if (qfl->qf_ptr) {
+ prev_fnum = qfl->qf_ptr->qf_fnum;
+ prev_lnum = qfl->qf_ptr->qf_lnum;
+ prev_col = qfl->qf_ptr->qf_col;
+ }
+
+ bool select_first_entry = false;
+ bool select_nearest_entry = false;
+
if (action == ' ' || qf_idx == qi->qf_listcount) {
+ select_first_entry = true;
// make place for a new list
qf_new_list(qi, title);
qf_idx = qi->qf_curlist;
qfl = qf_get_list(qi, qf_idx);
- } else if (action == 'a' && !qf_list_empty(qfl)) {
- // Adding to existing list, use last entry.
- old_last = qfl->qf_last;
+ } else if (action == 'a') {
+ if (qf_list_empty(qfl)) {
+ // Appending to empty list, select first entry.
+ select_first_entry = true;
+ } else {
+ // Adding to existing list, use last entry.
+ old_last = qfl->qf_last;
+ }
} else if (action == 'r') {
+ select_first_entry = true;
+ qf_free_items(qfl);
+ qf_store_title(qfl, title);
+ } else if (action == 'u') {
+ select_nearest_entry = true;
qf_free_items(qfl);
qf_store_title(qfl, title);
}
+ qfline_T *entry_to_select = NULL;
+ int entry_to_select_index = 0;
+
TV_LIST_ITER_CONST(list, li, {
if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) {
continue; // Skip non-dict items.
@@ -6498,6 +6585,16 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title,
if (retval == QF_FAIL) {
break;
}
+
+ qfline_T *entry = qfl->qf_last;
+ if ((select_first_entry && entry_to_select == NULL)
+ || (select_nearest_entry
+ && (entry_to_select == NULL
+ || entry_is_closer_to_target(entry, entry_to_select, prev_fnum,
+ prev_lnum, prev_col)))) {
+ entry_to_select = entry;
+ entry_to_select_index = qfl->qf_count;
+ }
});
// Check if any valid error entries are added to the list.
@@ -6507,16 +6604,10 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title,
qfl->qf_nonevalid = true;
}
- // If not appending to the list, set the current error to the first entry
- if (action != 'a') {
- qfl->qf_ptr = qfl->qf_start;
- }
-
- // Update the current error index if not appending to the list or if the
- // list was empty before and it is not empty now.
- if ((action != 'a' || qfl->qf_index == 0)
- && !qf_list_empty(qfl)) {
- qfl->qf_index = 1;
+ // Set the current error.
+ if (entry_to_select) {
+ qfl->qf_ptr = entry_to_select;
+ qfl->qf_index = entry_to_select_index;
}
// Don't update the cursor in quickfix window when appending entries
@@ -6632,7 +6723,7 @@ static int qf_setprop_items_from_lines(qf_info_T *qi, int qf_idx, const dict_T *
return FAIL;
}
- if (action == 'r') {
+ if (action == 'r' || action == 'u') {
qf_free_items(&qi->qf_lists[qf_idx]);
}
if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat,
@@ -6789,10 +6880,11 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
}
}
-// Populate the quickfix list with the items supplied in the list
-// of dictionaries. "title" will be copied to w:quickfix_title
-// "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
-// When "what" is not NULL then only set some properties.
+/// Populate the quickfix list with the items supplied in the list
+/// of dictionaries. "title" will be copied to w:quickfix_title
+/// "action" is 'a' for add, 'r' for replace, 'u' for update. Otherwise
+/// create a new list.
+/// When "what" is not NULL then only set some properties.
int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what)
{
qf_info_T *qi = &ql_info;
@@ -7256,7 +7348,6 @@ void ex_helpgrep(exarg_T *eap)
bool updated = false;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *const save_cpo = p_cpo;
- const bool save_cpo_allocated = (get_option(kOptCpoptions)->flags & P_ALLOCED);
p_cpo = empty_string_option;
bool new_qi = false;
@@ -7296,9 +7387,7 @@ void ex_helpgrep(exarg_T *eap)
if (*p_cpo == NUL) {
set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0);
}
- if (save_cpo_allocated) {
- free_string_option(save_cpo);
- }
+ free_string_option(save_cpo);
}
if (updated) {
@@ -7433,7 +7522,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
return;
}
const char *const act = tv_get_string_chk(action_arg);
- if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
+ if ((*act == 'a' || *act == 'r' || *act == 'u' || *act == ' ' || *act == 'f')
&& act[1] == NUL) {
action = *act;
} else {
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 030bda4fa5..3f00b74e61 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -2348,7 +2348,7 @@ void ex_scriptnames(exarg_T *eap)
vim_snprintf(IObuff, IOSIZE, "%3d: %s", i, NameBuff);
if (!message_filtered(IObuff)) {
msg_putchar('\n');
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
line_breakcheck();
}
}
diff --git a/src/nvim/search.c b/src/nvim/search.c
index ff6e135df1..debc5697d1 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -113,7 +113,7 @@ static int last_idx = 0; // index in spats[] for RE_LAST
static uint8_t lastc[2] = { NUL, NUL }; // last character searched for
static Direction lastcdir = FORWARD; // last direction of character search
static bool last_t_cmd = true; // last search t_cmd
-static char lastc_bytes[MB_MAXBYTES + 1];
+static char lastc_bytes[MAX_SCHAR_SIZE + 1];
static int lastc_bytelen = 1; // >1 for multi-byte char
// copy of spats[], for keeping the search patterns while executing autocmds
@@ -380,6 +380,11 @@ char *last_search_pattern(void)
return spats[RE_SEARCH].pat;
}
+size_t last_search_pattern_len(void)
+{
+ return spats[RE_SEARCH].patlen;
+}
+
/// Return true when case should be ignored for search pattern "pat".
/// Uses the 'ignorecase' and 'smartcase' options.
int ignorecase(char *pat)
@@ -1299,7 +1304,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
memset(msgbuf + pat_len, ' ', (size_t)(r - msgbuf));
}
}
- msg_outtrans(msgbuf, 0);
+ msg_outtrans(msgbuf, 0, false);
msg_clr_eos();
msg_check();
@@ -1545,14 +1550,11 @@ int searchc(cmdarg_T *cap, bool t_cmd)
*lastc = (uint8_t)c;
set_csearch_direction(dir);
set_csearch_until(t_cmd);
- lastc_bytelen = utf_char2bytes(c, lastc_bytes);
- if (cap->ncharC1 != 0) {
- lastc_bytelen += utf_char2bytes(cap->ncharC1,
- lastc_bytes + lastc_bytelen);
- if (cap->ncharC2 != 0) {
- lastc_bytelen += utf_char2bytes(cap->ncharC2,
- lastc_bytes + lastc_bytelen);
- }
+ if (cap->nchar_len) {
+ lastc_bytelen = cap->nchar_len;
+ memcpy(lastc_bytes, cap->nchar_composing, (size_t)cap->nchar_len);
+ } else {
+ lastc_bytelen = utf_char2bytes(c, lastc_bytes);
}
}
} else { // repeat previous search
@@ -3729,7 +3731,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool
&& action == ACTION_SHOW_ALL && files[i].matched) {
msg_putchar('\n'); // cursor below last one
if (!got_int) { // don't display if 'q' typed at "--more--" message
- msg_home_replace_hl(new_fname);
+ msg_home_replace(new_fname);
msg_puts(_(" (includes previously listed match)"));
prev_fname = NULL;
}
@@ -3770,7 +3772,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool
if (new_fname != NULL) {
// using "new_fname" is more reliable, e.g., when
// 'includeexpr' is set.
- msg_outtrans(new_fname, HL_ATTR(HLF_D));
+ msg_outtrans(new_fname, HLF_D, false);
} else {
// Isolate the file name.
// Include the surrounding "" or <> if present.
@@ -3804,7 +3806,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool
}
char save_char = p[i];
p[i] = NUL;
- msg_outtrans(p, HL_ATTR(HLF_D));
+ msg_outtrans(p, HLF_D, false);
p[i] = save_char;
}
@@ -3856,7 +3858,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool
vim_snprintf(IObuff, IOSIZE,
_("Scanning included file: %s"),
new_fname);
- msg_trunc(IObuff, true, HL_ATTR(HLF_R));
+ msg_trunc(IObuff, true, HLF_R);
} else if (p_verbose >= 5) {
verbose_enter();
smsg(0, _("Searching included file %s"), new_fname);
@@ -4030,7 +4032,7 @@ search_line:
}
if (!got_int) { // don't display if 'q' typed
// at "--more--" message
- msg_home_replace_hl(curr_fname);
+ msg_home_replace(curr_fname);
}
prev_fname = curr_fname;
}
@@ -4231,7 +4233,7 @@ static void show_pat_in_path(char *line, int type, bool did_show, int action, FI
msg_puts(IObuff);
snprintf(IObuff, IOSIZE, "%4" PRIdLINENR, *lnum); // Show line nr.
// Highlight line numbers.
- msg_puts_attr(IObuff, HL_ATTR(HLF_N));
+ msg_puts_hl(IObuff, HLF_N, false);
msg_puts(" ");
}
msg_prt_line(line, false);
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 2b401fa106..2961b35a29 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -246,7 +246,7 @@ typedef struct {
struct reg { // yankreg_T
char name;
MotionType type;
- char **contents;
+ String *contents;
bool is_unnamed;
size_t contents_size;
size_t width;
@@ -658,7 +658,7 @@ static const void *shada_hist_iter(const void *const iter, const uint8_t history
.histtype = history_type,
.string = hist_he.hisstr,
.sep = (char)(history_type == HIST_SEARCH
- ? hist_he.hisstr[strlen(hist_he.hisstr) + 1]
+ ? hist_he.hisstr[hist_he.hisstrlen + 1]
: 0),
}
},
@@ -784,6 +784,7 @@ static inline void hms_to_he_array(const HistoryMergerState *const hms_p,
hist->timestamp = cur_entry->data.timestamp;
hist->hisnum = (int)(hist - hist_array) + 1;
hist->hisstr = cur_entry->data.data.history_item.string;
+ hist->hisstrlen = strlen(cur_entry->data.data.history_item.string);
hist->additional_data = cur_entry->data.additional_data;
hist++;
})
@@ -1490,7 +1491,7 @@ static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry
PACK_KEY(REG_KEY_CONTENTS);
mpack_array(&sbuf.ptr, (uint32_t)entry.data.reg.contents_size);
for (size_t i = 0; i < entry.data.reg.contents_size; i++) {
- mpack_bin(cstr_as_string(entry.data.reg.contents[i]), &sbuf);
+ mpack_bin(entry.data.reg.contents[i], &sbuf);
}
PACK_KEY(KEY_NAME_CHAR);
mpack_uint(&sbuf.ptr, (uint8_t)entry.data.reg.name);
@@ -2929,7 +2930,7 @@ static void shada_free_shada_entry(ShadaEntry *const entry)
break;
case kSDItemRegister:
for (size_t i = 0; i < entry->data.reg.contents_size; i++) {
- xfree(entry->data.reg.contents[i]);
+ api_free_string(entry->data.reg.contents[i]);
}
xfree(entry->data.reg.contents);
break;
@@ -3311,9 +3312,9 @@ shada_read_next_item_start:
goto shada_read_next_item_error;
}
entry->data.reg.contents_size = it.rc.size;
- entry->data.reg.contents = xmalloc(it.rc.size * sizeof(char *));
+ entry->data.reg.contents = xmalloc(it.rc.size * sizeof(String));
for (size_t j = 0; j < it.rc.size; j++) {
- entry->data.reg.contents[j] = xmemdupz(it.rc.items[j].data, it.rc.items[j].size);
+ entry->data.reg.contents[j] = copy_string(it.rc.items[j], NULL);
}
kv_destroy(it.rc);
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index b4ba7833e9..f8e7eeaca4 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -270,7 +270,7 @@ static void sign_list_placed(buf_T *rbuf, char *group)
while (buf != NULL && !got_int) {
if (buf_has_signs(buf)) {
vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
- msg_puts_attr(lbuf, HL_ATTR(HLF_D));
+ msg_puts_hl(lbuf, HLF_D, false);
msg_putchar('\n');
}
@@ -481,14 +481,14 @@ static void sign_list_defined(sign_T *sp)
smsg(0, "sign %s", sp->sn_name);
if (sp->sn_icon != NULL) {
msg_puts(" icon=");
- msg_outtrans(sp->sn_icon, 0);
+ msg_outtrans(sp->sn_icon, 0, false);
msg_puts(_(" (not supported)"));
}
if (sp->sn_text[0]) {
msg_puts(" text=");
char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
describe_sign_text(buf, sp->sn_text);
- msg_outtrans(buf, 0);
+ msg_outtrans(buf, 0, false);
}
if (sp->sn_priority > 0) {
char lbuf[MSG_BUF_LEN];
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 8ec28c7f61..a0fdd46b04 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -2091,7 +2091,7 @@ char *parse_spelllang(win_T *wp)
int_wordlist_spl(spf_name);
} else {
// One entry in 'spellfile'.
- copy_option_part(&spf, spf_name, MAXPATHL - 5, ",");
+ copy_option_part(&spf, spf_name, MAXPATHL - 4, ",");
strcat(spf_name, ".spl");
int c;
@@ -3664,30 +3664,26 @@ bool valid_spelllang(const char *val)
bool valid_spellfile(const char *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (const char *s = val; *s != NUL; s++) {
- if (!vim_is_fname_char((uint8_t)(*s))) {
+ char spf_name[MAXPATHL];
+ char *spf = (char *)val;
+ while (*spf != NUL) {
+ size_t l = copy_option_part(&spf, spf_name, MAXPATHL, ",");
+ if (l >= MAXPATHL - 4 || l < 4 || strcmp(spf_name + l - 4, ".add") != 0) {
return false;
}
+ for (char *s = spf_name; *s != NUL; s++) {
+ if (!vim_is_fname_char((uint8_t)(*s))) {
+ return false;
+ }
+ }
}
return true;
}
-const char *did_set_spell_option(bool is_spellfile)
+const char *did_set_spell_option(void)
{
const char *errmsg = NULL;
- if (is_spellfile) {
- int l = (int)strlen(curwin->w_s->b_p_spf);
- if (l > 0
- && (l < 4 || strcmp(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
- errmsg = e_invarg;
- }
- }
-
- if (errmsg != NULL) {
- return errmsg;
- }
-
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_spell) {
errmsg = parse_spelllang(wp);
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 07bb7dd69a..4e78067d46 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -430,7 +430,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
if (hltab[n].userhl == 0) {
curattr = attr;
} else if (hltab[n].userhl < 0) {
- curattr = syn_id2attr(-hltab[n].userhl);
+ curattr = hl_combine_attr(attr, syn_id2attr(-hltab[n].userhl));
} else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
curattr = highlight_stlnc[hltab[n].userhl - 1];
} else {
@@ -582,9 +582,11 @@ void win_redr_ruler(win_T *wp)
if (ui_has(kUIMessages) && !part_of_status) {
MAXSIZE_TEMP_ARRAY(content, 1);
- MAXSIZE_TEMP_ARRAY(chunk, 2);
+ MAXSIZE_TEMP_ARRAY(chunk, 3);
ADD_C(chunk, INTEGER_OBJ(attr));
ADD_C(chunk, CSTR_AS_OBJ(buffer));
+ ADD_C(chunk, INTEGER_OBJ(HLF_MSG));
+ assert(attr == HL_ATTR(HLF_MSG));
ADD_C(content, ARRAY_OBJ(chunk));
ui_call_msg_ruler(content);
did_show_ext_ruler = true;
@@ -758,7 +760,9 @@ void draw_tabline(void)
bool modified = false;
for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) {
- if (bufIsChanged(wp->w_buffer)) {
+ if (!wp->w_config.focusable) {
+ wincount--;
+ } else if (bufIsChanged(wp->w_buffer)) {
modified = true;
}
}
@@ -1630,7 +1634,7 @@ stcsign:
schar_T fold_buf[9];
fill_foldcolumn(wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM),
0, fdc, NULL, fold_buf);
- stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1);
+ stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLF : HLF_FC);
size_t buflen = 0;
// TODO(bfredl): this is very backwards. we must support schar_T
// being used directly in 'statuscolumn'
@@ -1651,7 +1655,7 @@ stcsign:
buf_tmp[signlen++] = ' ';
buf_tmp[signlen++] = ' ';
buf_tmp[signlen] = NUL;
- stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
+ stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLS : HLF_SC);
}
}
stl_items[curitem++].type = Highlight;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 118abbae6d..2de65391cc 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -2838,10 +2838,10 @@ void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) {
- int off;
+ int64_t off;
// length in characters
- for (off = (int)n; off < (int)slen && len > 0; len--) {
+ for (off = n; off < (int64_t)slen && len > 0; len--) {
off += utfc_ptr2len(p + off);
}
len = off - n;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9eeed3fbd2..03f20047a5 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -2928,9 +2928,9 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
msg_puts("\n");
if (curwin->w_s->b_syn_isk != empty_string_option) {
msg_puts("syntax iskeyword ");
- msg_outtrans(curwin->w_s->b_syn_isk, 0);
+ msg_outtrans(curwin->w_s->b_syn_isk, 0, false);
} else {
- msg_outtrans(_("syntax iskeyword not set"), 0);
+ msg_outtrans(_("syntax iskeyword not set"), 0, false);
}
} else {
if (STRNICMP(arg, "clear", 5) == 0) {
@@ -3346,13 +3346,12 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
KEYVALUE_ENTRY(HL_SKIPEMPTY, "skipempty"),
};
- const int attr = HL_ATTR(HLF_D); // highlight like directories
+ const int hl_id = HLF_D; // highlight like directories
// list the keywords for "id"
if (!syncing) {
- did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, false, attr);
- did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
- did_header, attr);
+ did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, false, hl_id);
+ did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic, did_header, hl_id);
}
// list the patterns for "id"
@@ -3368,46 +3367,46 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
did_header = true;
last_matchgroup = 0;
if (spp->sp_type == SPTYPE_MATCH) {
- put_pattern("match", ' ', spp, attr);
+ put_pattern("match", ' ', spp, hl_id);
msg_putchar(' ');
} else if (spp->sp_type == SPTYPE_START) {
while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) {
- put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
+ put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id);
}
if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) {
- put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
+ put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id);
}
while (idx < curwin->w_s->b_syn_patterns.ga_len
&& SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) {
- put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
+ put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], hl_id);
}
idx--;
msg_putchar(' ');
}
- syn_list_flags(namelist1, ARRAY_SIZE(namelist1), spp->sp_flags, attr);
+ syn_list_flags(namelist1, ARRAY_SIZE(namelist1), spp->sp_flags, hl_id);
if (spp->sp_cont_list != NULL) {
- put_id_list("contains", spp->sp_cont_list, attr);
+ put_id_list("contains", spp->sp_cont_list, hl_id);
}
if (spp->sp_syn.cont_in_list != NULL) {
- put_id_list("containedin", spp->sp_syn.cont_in_list, attr);
+ put_id_list("containedin", spp->sp_syn.cont_in_list, hl_id);
}
if (spp->sp_next_list != NULL) {
- put_id_list("nextgroup", spp->sp_next_list, attr);
- syn_list_flags(namelist2, ARRAY_SIZE(namelist2), spp->sp_flags, attr);
+ put_id_list("nextgroup", spp->sp_next_list, hl_id);
+ syn_list_flags(namelist2, ARRAY_SIZE(namelist2), spp->sp_flags, hl_id);
}
if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE)) {
if (spp->sp_flags & HL_SYNC_HERE) {
- msg_puts_attr("grouphere", attr);
+ msg_puts_hl("grouphere", hl_id, false);
} else {
- msg_puts_attr("groupthere", attr);
+ msg_puts_hl("groupthere", hl_id, false);
}
msg_putchar(' ');
if (spp->sp_sync_idx >= 0) {
msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s)
- [spp->sp_sync_idx].sp_syn.id - 1), 0);
+ [spp->sp_sync_idx].sp_syn.id - 1), 0, false);
} else {
msg_puts("NONE");
}
@@ -3418,17 +3417,17 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
// list the link, if there is one
if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) {
syn_list_header(did_header, 0, id, true);
- msg_puts_attr("links to", attr);
+ msg_puts_hl("links to", hl_id, false);
msg_putchar(' ');
- msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1), 0);
+ msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1), 0, false);
}
}
-static void syn_list_flags(keyvalue_T *nlist, size_t nr_entries, int flags, int attr)
+static void syn_list_flags(keyvalue_T *nlist, size_t nr_entries, int flags, int hl_id)
{
for (size_t i = 0; i < nr_entries; i++) {
if (flags & nlist[i].key) {
- msg_puts_attr(nlist[i].value, attr);
+ msg_puts_hl(nlist[i].value, hl_id, false);
msg_putchar(' ');
}
}
@@ -3441,7 +3440,7 @@ static void syn_list_cluster(int id)
// slight hack: roughly duplicate the guts of syn_list_header()
msg_putchar('\n');
- msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name, 0);
+ msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name, 0, false);
if (msg_col >= endcol) { // output at least one space
endcol = msg_col + 1;
@@ -3452,16 +3451,16 @@ static void syn_list_cluster(int id)
msg_advance(endcol);
if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) {
- put_id_list("cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, HL_ATTR(HLF_D));
+ put_id_list("cluster", SYN_CLSTR(curwin->w_s)[id].scl_list, HLF_D);
} else {
- msg_puts_attr("cluster", HL_ATTR(HLF_D));
+ msg_puts_hl("cluster", HLF_D, false);
msg_puts("=NONE");
}
}
-static void put_id_list(const char *const name, const int16_t *const list, const int attr)
+static void put_id_list(const char *const name, const int16_t *const list, const int hl_id)
{
- msg_puts_attr(name, attr);
+ msg_puts_hl(name, hl_id, false);
msg_putchar('=');
for (const int16_t *p = list; *p; p++) {
if (*p >= SYNID_ALLBUT && *p < SYNID_TOP) {
@@ -3478,9 +3477,9 @@ static void put_id_list(const char *const name, const int16_t *const list, const
int scl_id = *p - SYNID_CLUSTER;
msg_putchar('@');
- msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name, 0);
+ msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name, 0, false);
} else {
- msg_outtrans(highlight_group_name(*p - 1), 0);
+ msg_outtrans(highlight_group_name(*p - 1), 0, false);
}
if (p[1]) {
msg_putchar(',');
@@ -3489,7 +3488,8 @@ static void put_id_list(const char *const name, const int16_t *const list, const
msg_putchar(' ');
}
-static void put_pattern(const char *const s, const int c, const synpat_T *const spp, const int attr)
+static void put_pattern(const char *const s, const int c, const synpat_T *const spp,
+ const int hl_id)
{
static const char *const sepchars = "/+=-#@\"|'^&";
int i;
@@ -3497,18 +3497,18 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
// May have to write "matchgroup=group"
if (last_matchgroup != spp->sp_syn_match_id) {
last_matchgroup = spp->sp_syn_match_id;
- msg_puts_attr("matchgroup", attr);
+ msg_puts_hl("matchgroup", hl_id, false);
msg_putchar('=');
if (last_matchgroup == 0) {
- msg_outtrans("NONE", 0);
+ msg_outtrans("NONE", 0, false);
} else {
- msg_outtrans(highlight_group_name(last_matchgroup - 1), 0);
+ msg_outtrans(highlight_group_name(last_matchgroup - 1), 0, false);
}
msg_putchar(' ');
}
// Output the name of the pattern and an '=' or ' '.
- msg_puts_attr(s, attr);
+ msg_puts_hl(s, hl_id, false);
msg_putchar(c);
// output the pattern, in between a char that is not in the pattern
@@ -3519,7 +3519,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
}
}
msg_putchar(sepchars[i]);
- msg_outtrans(spp->sp_pattern, 0);
+ msg_outtrans(spp->sp_pattern, 0, false);
msg_putchar(sepchars[i]);
// output any pattern options
@@ -3558,7 +3558,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
///
/// @return true if the header has been printed.
static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_header,
- const int attr)
+ const int hl_id)
{
int prev_contained = 0;
const int16_t *prev_next_list = NULL;
@@ -3600,36 +3600,36 @@ static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_
}
did_header = true;
if (prev_contained != (kp->flags & HL_CONTAINED)) {
- msg_puts_attr("contained", attr);
+ msg_puts_hl("contained", hl_id, false);
msg_putchar(' ');
prev_contained = (kp->flags & HL_CONTAINED);
}
if (kp->k_syn.cont_in_list != prev_cont_in_list) {
- put_id_list("containedin", kp->k_syn.cont_in_list, attr);
+ put_id_list("containedin", kp->k_syn.cont_in_list, hl_id);
msg_putchar(' ');
prev_cont_in_list = kp->k_syn.cont_in_list;
}
if (kp->next_list != prev_next_list) {
- put_id_list("nextgroup", kp->next_list, attr);
+ put_id_list("nextgroup", kp->next_list, hl_id);
msg_putchar(' ');
prev_next_list = kp->next_list;
if (kp->flags & HL_SKIPNL) {
- msg_puts_attr("skipnl", attr);
+ msg_puts_hl("skipnl", hl_id, false);
msg_putchar(' ');
prev_skipnl = (kp->flags & HL_SKIPNL);
}
if (kp->flags & HL_SKIPWHITE) {
- msg_puts_attr("skipwhite", attr);
+ msg_puts_hl("skipwhite", hl_id, false);
msg_putchar(' ');
prev_skipwhite = (kp->flags & HL_SKIPWHITE);
}
if (kp->flags & HL_SKIPEMPTY) {
- msg_puts_attr("skipempty", attr);
+ msg_puts_hl("skipempty", hl_id, false);
msg_putchar(' ');
prev_skipempty = (kp->flags & HL_SKIPEMPTY);
}
}
- msg_outtrans(kp->keyword, 0);
+ msg_outtrans(kp->keyword, 0, false);
}
}
}
@@ -5649,7 +5649,7 @@ static void syntime_report(void)
msg_puts(profile_msg(p->average));
msg_puts(" ");
msg_advance(50);
- msg_outtrans(highlight_group_name(p->id - 1), 0);
+ msg_outtrans(highlight_group_name(p->id - 1), 0, false);
msg_puts(" ");
msg_advance(69);
@@ -5661,7 +5661,7 @@ static void syntime_report(void)
}
int patlen = (int)strlen(p->pattern);
len = MIN(len, patlen);
- msg_outtrans_len(p->pattern, len, 0);
+ msg_outtrans_len(p->pattern, len, 0, false);
msg_puts("\n");
}
ga_clear(&ga);
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 7a0c1cd810..3e0bb32391 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -736,7 +736,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
}
if ((num_matches > prev_num_matches || new_tag)
&& num_matches > 1) {
- msg(IObuff, ic ? HL_ATTR(HLF_W) : 0);
+ msg(IObuff, ic ? HLF_W : 0);
msg_scroll = true; // Don't overwrite this message.
} else {
give_warning(IObuff, ic);
@@ -815,10 +815,10 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
msg_didout = false; // overwrite previous message
}
msg_start();
- msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T));
+ msg_puts_hl(_(" # pri kind tag"), HLF_T, false);
msg_clr_eos();
taglen_advance(taglen);
- msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
+ msg_puts_hl(_("file\n"), HLF_T, false);
for (int i = 0; i < num_matches && !got_int; i++) {
parse_match(matches[i], &tagp);
@@ -836,10 +836,10 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
mt_names[matches[i][0] & MT_MASK]);
msg_puts(IObuff);
if (tagp.tagkind != NULL) {
- msg_outtrans_len(tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind), 0);
+ msg_outtrans_len(tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind), 0, false);
}
msg_advance(13);
- msg_outtrans_len(tagp.tagname, (int)(tagp.tagname_end - tagp.tagname), HL_ATTR(HLF_T));
+ msg_outtrans_len(tagp.tagname, (int)(tagp.tagname_end - tagp.tagname), HLF_T, false);
msg_putchar(' ');
taglen_advance(taglen);
@@ -847,7 +847,7 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
// it and put "..." in the middle
const char *p = tag_full_fname(&tagp);
if (p != NULL) {
- msg_outtrans(p, HL_ATTR(HLF_D));
+ msg_outtrans(p, HLF_D, false);
XFREE_CLEAR(p);
}
if (msg_col > 0) {
@@ -880,7 +880,7 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
continue;
}
// print all other extra fields
- int attr = HL_ATTR(HLF_CM);
+ int hl_id = HLF_CM;
while (*p && *p != '\r' && *p != '\n') {
if (msg_col + ptr2cells(p) >= Columns) {
msg_putchar('\n');
@@ -889,13 +889,13 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
}
msg_advance(15);
}
- p = msg_outtrans_one(p, attr);
+ p = msg_outtrans_one(p, hl_id, false);
if (*p == TAB) {
- msg_puts_attr(" ", attr);
+ msg_puts_hl(" ", hl_id, false);
break;
}
if (*p == ':') {
- attr = 0;
+ hl_id = 0;
}
}
}
@@ -947,7 +947,7 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha
msg_putchar(' ');
p++;
} else {
- p = msg_outtrans_one(p, 0);
+ p = msg_outtrans_one(p, 0, false);
}
// don't display the "$/;\"" and "$?;\""
@@ -1125,8 +1125,8 @@ void do_tags(exarg_T *eap)
tagstack[i].cur_match + 1,
tagstack[i].tagname,
tagstack[i].fmark.mark.lnum);
- msg_outtrans(IObuff, 0);
- msg_outtrans(name, tagstack[i].fmark.fnum == curbuf->b_fnum ? HL_ATTR(HLF_D) : 0);
+ msg_outtrans(IObuff, 0, false);
+ msg_outtrans(name, tagstack[i].fmark.fnum == curbuf->b_fnum ? HLF_D : 0, false);
xfree(name);
}
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index b916660024..5ff7f721ba 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -895,13 +895,13 @@ static bool is_filter_char(int c)
return !!(tpf_flags & flag);
}
-void terminal_paste(int count, char **y_array, size_t y_size)
+void terminal_paste(int count, String *y_array, size_t y_size)
{
if (y_size == 0) {
return;
}
vterm_keyboard_start_paste(curbuf->terminal->vt);
- size_t buff_len = strlen(y_array[0]);
+ size_t buff_len = y_array[0].size;
char *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) {
// feed the lines to the terminal
@@ -914,13 +914,13 @@ void terminal_paste(int count, char **y_array, size_t y_size)
terminal_send(curbuf->terminal, "\n", 1);
#endif
}
- size_t len = strlen(y_array[j]);
+ size_t len = y_array[j].size;
if (len > buff_len) {
buff = xrealloc(buff, len);
buff_len = len;
}
char *dst = buff;
- char *src = y_array[j];
+ char *src = y_array[j].data;
while (*src != NUL) {
len = (size_t)utf_ptr2len(src);
int c = utf_ptr2char(src);
@@ -1178,9 +1178,10 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
return 1;
}
+/// Called when the terminal wants to ring the system bell.
static int term_bell(void *data)
{
- ui_call_bell();
+ vim_beep(BO_TERM);
return 1;
}
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 9095d4e8c9..06b3aa0411 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -863,7 +863,7 @@ int fex_format(linenr_T lnum, long count, int c)
// Make a copy, the option could be changed while calling it.
char *fex = xstrdup(curbuf->b_p_fex);
- current_sctx = curbuf->b_p_script_ctx[BV_FEX].script_ctx;
+ current_sctx = curbuf->b_p_script_ctx[kBufOptFormatexpr].script_ctx;
// Evaluate the function.
if (use_sandbox) {
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index fa50a8252d..2839a665da 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -134,11 +134,12 @@ struct TUIData {
int resize_screen;
int reset_scroll_region;
int set_cursor_style, reset_cursor_style;
- int save_title, restore_title;
+ int save_title, restore_title, set_title;
int set_underline_style;
int set_underline_color;
int sync;
} unibi_ext;
+ char *set_title;
char *space_buf;
size_t space_buf_len;
bool stopped;
@@ -536,6 +537,7 @@ static void terminfo_stop(TUIData *tui)
abort();
}
unibi_destroy(tui->ut);
+ XFREE_CLEAR(tui->set_title);
}
static void tui_terminal_start(TUIData *tui)
@@ -1567,8 +1569,7 @@ void tui_suspend(TUIData *tui)
void tui_set_title(TUIData *tui, String title)
{
- if (!(unibi_get_str(tui->ut, unibi_to_status_line)
- && unibi_get_str(tui->ut, unibi_from_status_line))) {
+ if (!unibi_get_ext_str(tui->ut, (unsigned)tui->unibi_ext.set_title)) {
return;
}
if (title.size > 0) {
@@ -1577,9 +1578,9 @@ void tui_set_title(TUIData *tui, String title)
unibi_out_ext(tui, tui->unibi_ext.save_title);
tui->title_enabled = true;
}
- unibi_out(tui, unibi_to_status_line);
- out(tui, title.data, title.size);
- unibi_out(tui, unibi_from_status_line);
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ UNIBI_SET_STR_VAR(tui->params[1], title.data);
+ unibi_out_ext(tui, tui->unibi_ext.set_title);
} else if (tui->title_enabled) {
// Restore title/icon from the "stack". #4063
unibi_out_ext(tui, tui->unibi_ext.restore_title);
@@ -1803,12 +1804,17 @@ static void unibi_goto(TUIData *tui, int row, int col)
memset(&vars, 0, sizeof(vars)); \
tui->cork = true; \
retry: \
+ /* Copy parameters on every retry, as unibi_format() may modify them. */ \
memcpy(params, tui->params, sizeof(params)); \
unibi_format(vars, vars + 26, str, params, out, tui, pad, tui); \
if (tui->overflow) { \
tui->bufpos = orig_pos; \
- flush_buf(tui); \
- goto retry; \
+ /* If orig_pos is 0, there's nothing to flush and retrying won't work. */ \
+ /* TODO(zeertzjq): should this situation still be handled? */ \
+ if (orig_pos > 0) { \
+ flush_buf(tui); \
+ goto retry; \
+ } \
} \
tui->cork = false; \
} \
@@ -1840,6 +1846,7 @@ static void out(void *ctx, const char *str, size_t len)
}
flush_buf(tui);
}
+ // TODO(zeertzjq): handle string longer than buffer size? #30794
memcpy(tui->buf + tui->bufpos, str, len);
tui->bufpos += len;
@@ -2378,6 +2385,19 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
tui->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t");
tui->unibi_ext.restore_title = (int)unibi_add_ext_str(ut, "ext.restore_title", "\x1b[23;0t");
+ const char *tsl = unibi_get_str(ut, unibi_to_status_line);
+ const char *fsl = unibi_get_str(ut, unibi_from_status_line);
+ if (tsl != NULL && fsl != NULL) {
+ // Add a single extended capability for the whole sequence to set title,
+ // as it is usually an OSC sequence that cannot be cut in half.
+ // Use %p2 for the title string, as to_status_line may take an argument.
+ size_t set_title_len = strlen(tsl) + strlen("%p2%s") + strlen(fsl);
+ char *set_title = xmallocz(set_title_len);
+ snprintf(set_title, set_title_len + 1, "%s%s%s", tsl, "%p2%s", fsl);
+ tui->unibi_ext.set_title = (int)unibi_add_ext_str(ut, "ext.set_title", set_title);
+ tui->set_title = set_title;
+ }
+
/// Terminals usually ignore unrecognized private modes, and there is no
/// known ambiguity with these. So we just set them unconditionally.
tui->unibi_ext.enable_lr_margin =
diff --git a/src/nvim/types_defs.h b/src/nvim/types_defs.h
index 2dd2b01adf..bec0950653 100644
--- a/src/nvim/types_defs.h
+++ b/src/nvim/types_defs.h
@@ -12,6 +12,10 @@ typedef int32_t sattr_T;
// must be at least as big as the biggest of schar_T, sattr_T, colnr_T
typedef int32_t sscratch_T;
+// Includes final NUL. MAX_MCO is no longer used, but at least 4*(MAX_MCO+1)+1=29
+// ensures we can fit all composed chars which did fit before.
+#define MAX_SCHAR_SIZE 32
+
// Opaque handle used by API clients to refer to various objects in vim
typedef int handle_T;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 365aa5c74f..7c81110ae9 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -44,6 +44,7 @@
typedef struct {
LuaRef cb;
+ uint8_t errors;
bool ext_widgets[kUIGlobalCount];
} UIEventCallback;
@@ -212,21 +213,20 @@ void ui_refresh(void)
cursor_row = cursor_col = 0;
pending_cursor_update = true;
+ bool had_message = ui_ext[kUIMessages];
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ ui_ext[i] = ext_widgets[i] | ui_cb_ext[i];
if (i < kUIGlobalCount) {
- ext_widgets[i] |= ui_cb_ext[i];
+ ui_call_option_set(cstr_as_string(ui_ext_names[i]), BOOLEAN_OBJ(ui_ext[i]));
}
- // Set 'cmdheight' to zero for all tabpages when ext_messages becomes active.
- if (i == kUIMessages && !ui_ext[i] && ext_widgets[i]) {
- set_option_value(kOptCmdheight, NUMBER_OPTVAL(0), 0);
- command_height();
- FOR_ALL_TABS(tp) {
- tp->tp_ch_used = 0;
- }
- }
- ui_ext[i] = ext_widgets[i];
- if (i < kUIGlobalCount) {
- ui_call_option_set(cstr_as_string(ui_ext_names[i]), BOOLEAN_OBJ(ext_widgets[i]));
+ }
+
+ // Reset 'cmdheight' for all tabpages when ext_messages toggles.
+ if (had_message != ui_ext[kUIMessages]) {
+ set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0);
+ command_height();
+ FOR_ALL_TABS(tp) {
+ tp->tp_ch_used = had_message;
}
}
@@ -358,8 +358,8 @@ void vim_beep(unsigned val)
// 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(_("Beep!"), HL_ATTR(HLF_W));
+ msg_source(HLF_W);
+ msg(_("Beep!"), HLF_W);
}
}
@@ -713,13 +713,22 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *err)
}
}
-void ui_call_event(char *name, Array args)
+void ui_call_event(char *name, bool fast, Array args)
{
- UIEventCallback *event_cb;
bool handled = false;
- map_foreach_value(&ui_event_cbs, event_cb, {
+ UIEventCallback *event_cb;
+
+ // Prompt messages should be shown immediately so must be safe
+ if (strcmp(name, "msg_show") == 0) {
+ char *kind = args.items[0].data.string.data;
+ fast = !kind || (strncmp(kind, "confirm", 7) != 0 && strcmp(kind, "return_prompt") != 0);
+ }
+
+ map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, {
Error err = ERROR_INIT;
- Object res = nlua_call_ref(event_cb->cb, name, args, kRetNilBool, NULL, &err);
+ uint32_t ns_id = ui_event_ns_id;
+ Object res = nlua_call_ref_ctx(fast, event_cb->cb, name, args, kRetNilBool, NULL, &err);
+ ui_event_ns_id = 0;
// TODO(bfredl/luukvbaal): should this be documented or reconsidered?
// Why does truthy return from Lua callback mean remote UI should not receive
// the event.
@@ -728,6 +737,7 @@ void ui_call_event(char *name, Array args)
}
if (ERROR_SET(&err)) {
ELOG("Error executing UI event callback: %s", err.msg);
+ ui_remove_cb(ns_id, true);
}
api_clear_error(&err);
})
@@ -780,12 +790,16 @@ void ui_add_cb(uint32_t ns_id, LuaRef cb, bool *ext_widgets)
ui_refresh();
}
-void ui_remove_cb(uint32_t ns_id)
+void ui_remove_cb(uint32_t ns_id, bool checkerr)
{
- if (map_has(uint32_t, &ui_event_cbs, ns_id)) {
- UIEventCallback *item = pmap_del(uint32_t)(&ui_event_cbs, ns_id, NULL);
+ UIEventCallback *item = pmap_get(uint32_t)(&ui_event_cbs, ns_id);
+ if (item && (!checkerr || ++item->errors > 10)) {
+ pmap_del(uint32_t)(&ui_event_cbs, ns_id, NULL);
free_ui_event_callback(item);
+ ui_cb_update_ext();
+ ui_refresh();
+ if (checkerr) {
+ msg_schedule_semsg("Excessive errors in vim.ui_attach() callback from ns: %d.", ns_id);
+ }
}
- ui_cb_update_ext();
- ui_refresh();
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 8718c7b506..4aeb8ffda9 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -18,4 +18,6 @@ EXTERN Array noargs INIT(= ARRAY_DICT_INIT);
#endif
// uncrustify:on
-EXTERN MultiQueue *resize_events;
+// vim.ui_attach() namespace of currently executed callback.
+EXTERN uint32_t ui_event_ns_id INIT( = 0);
+EXTERN MultiQueue *resize_events INIT( = NULL);
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 6e89894e0b..e28e8d4da7 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -275,7 +275,7 @@ ScreenGrid *ui_comp_mouse_focus(int row, int col)
{
for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) {
ScreenGrid *grid = kv_A(layers, i);
- if (grid->focusable
+ if (grid->mouse_enabled
&& row >= grid->comp_row && row < grid->comp_row + grid->rows
&& col >= grid->comp_col && col < grid->comp_col + grid->cols) {
return grid;
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 15c8e0b283..0f8857a6bd 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2595,13 +2595,12 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
check_pos(curbuf, &VIsual);
}
- 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"),
- uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
- msgbuf);
+ smsg_hl_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"),
+ u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
+ _(msgstr),
+ did_undo ? _("before") : _("after"),
+ uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
+ msgbuf);
}
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
@@ -2713,8 +2712,7 @@ void ex_undolist(exarg_T *eap)
sort_strings(ga.ga_data, ga.ga_len);
msg_start();
- msg_puts_attr(_("number changes when saved"),
- HL_ATTR(HLF_T));
+ msg_puts_hl(_("number changes when saved"), HLF_T, false);
for (int i = 0; i < ga.ga_len && !got_int; i++) {
msg_putchar('\n');
if (got_int) {
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index d32e0ee319..d27899b10f 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -91,6 +91,7 @@ static const char *command_complete[] = {
[EXPAND_PACKADD] = "packadd",
[EXPAND_RUNTIME] = "runtime",
[EXPAND_SHELLCMD] = "shellcmd",
+ [EXPAND_SHELLCMDLINE] = "shellcmdline",
[EXPAND_SIGN] = "sign",
[EXPAND_TAGS] = "tag",
[EXPAND_TAGS_LISTFILES] = "tag_listfiles",
@@ -285,8 +286,7 @@ const char *set_context_in_user_cmdarg(const char *cmd FUNC_ATTR_UNUSED, const c
}
if (argt & EX_XFILE) {
- // EX_XFILE: file names are handled above.
- xp->xp_context = context;
+ // EX_XFILE: file names are handled before this call.
return NULL;
}
@@ -464,8 +464,7 @@ static void uc_list(char *name, size_t name_len)
// Put out the title first time
if (!found) {
- msg_puts_title(_("\n Name Args Address "
- "Complete Definition"));
+ msg_puts_title(_("\n Name Args Address Complete Definition"));
}
found = true;
msg_putchar('\n');
@@ -495,7 +494,7 @@ static void uc_list(char *name, size_t name_len)
msg_putchar(' ');
}
- msg_outtrans(cmd->uc_name, HL_ATTR(HLF_D));
+ msg_outtrans(cmd->uc_name, HLF_D, false);
len = strlen(cmd->uc_name) + 4;
do {
@@ -582,11 +581,11 @@ static void uc_list(char *name, size_t name_len)
} while ((int64_t)len < 25 - over);
IObuff[len] = NUL;
- msg_outtrans(IObuff, 0);
+ msg_outtrans(IObuff, 0, false);
if (cmd->uc_luaref != LUA_NOREF) {
char *fn = nlua_funcref_str(cmd->uc_luaref, NULL);
- msg_puts_attr(fn, HL_ATTR(HLF_8));
+ msg_puts_hl(fn, HLF_8, false);
xfree(fn);
// put the description on a new line
if (*cmd->uc_rep != NUL) {
@@ -675,7 +674,8 @@ int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt,
*complp = i;
if (i == EXPAND_BUFFERS) {
*argt |= EX_BUFNAME;
- } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES) {
+ } else if (i == EXPAND_DIRECTORIES || i == EXPAND_FILES
+ || i == EXPAND_SHELLCMDLINE) {
*argt |= EX_XFILE;
}
break;
diff --git a/src/nvim/vim_defs.h b/src/nvim/vim_defs.h
index f6b348e009..72aaa77533 100644
--- a/src/nvim/vim_defs.h
+++ b/src/nvim/vim_defs.h
@@ -2,7 +2,6 @@
// Some defines from the old feature.h
#define SESSION_FILE "Session.vim"
-#define MAX_MSG_HIST_LEN 200
#define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim"
#define RUNTIME_DIRNAME "runtime"
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index 2f43f8b32b..5fc014a50c 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -41,6 +41,15 @@ M.vars = {
included here, because it will be executed anyway.
]=],
},
+ cmdbang = {
+ type = 'integer',
+ desc = [=[
+ Set like v:cmdarg for a file read/write command. When a "!"
+ was used the value is 1, otherwise it is 0. Note that this
+ can only be used in autocommands. For user commands |<bang>|
+ can be used.
+ ]=],
+ },
collate = {
type = 'string',
desc = [=[
@@ -53,15 +62,6 @@ M.vars = {
See |multi-lang|.
]=],
},
- cmdbang = {
- type = 'integer',
- desc = [=[
- Set like v:cmdarg for a file read/write command. When a "!"
- was used the value is 1, otherwise it is 0. Note that this
- can only be used in autocommands. For user commands |<bang>|
- can be used.
- ]=],
- },
completed_item = {
desc = [=[
Dictionary containing the |complete-items| for the most
@@ -118,15 +118,6 @@ M.vars = {
VimLeave autocommands will not be executed.
]=],
},
- exiting = {
- desc = [=[
- Exit code, or |v:null| before invoking the |VimLeavePre|
- and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
- Example: >vim
- :au VimLeave * echo "Exit value is " .. v:exiting
- <
- ]=],
- },
echospace = {
type = 'integer',
desc = [=[
@@ -180,13 +171,14 @@ M.vars = {
an aborting condition (e.g. |c_Esc| or
|c_CTRL-C| for |CmdlineLeave|).
chan |channel-id|
+ info Dict of arbitrary event data.
cmdlevel Level of cmdline.
cmdtype Type of cmdline, |cmdline-char|.
cwd Current working directory.
inclusive Motion is |inclusive|, else exclusive.
scope Event-specific scope name.
operator Current |operator|. Also set for Ex
- commands (unlike |v:operator|). For
+ commands (unlike |v:operator|). For
example if |TextYankPost| is triggered
by the |:yank| Ex command then
`v:event.operator` is "y".
@@ -243,18 +235,13 @@ M.vars = {
or |expr7| when used with numeric operators). Read-only.
]=],
},
- fcs_reason = {
- type = 'string',
+ exiting = {
desc = [=[
- The reason why the |FileChangedShell| event was triggered.
- Can be used in an autocommand to decide what to do and/or what
- to set v:fcs_choice to. Possible values:
- deleted file no longer exists
- conflict file contents, mode or timestamp was
- changed and buffer is modified
- changed file contents has changed
- mode mode of file changed
- time only file timestamp changed
+ Exit code, or |v:null| before invoking the |VimLeavePre|
+ and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
+ Example: >vim
+ :au VimLeave * echo "Exit value is " .. v:exiting
+ <
]=],
},
fcs_choice = {
@@ -280,6 +267,20 @@ M.vars = {
Vim behaves like it is empty, there is no warning message.
]=],
},
+ fcs_reason = {
+ type = 'string',
+ desc = [=[
+ The reason why the |FileChangedShell| event was triggered.
+ Can be used in an autocommand to decide what to do and/or what
+ to set v:fcs_choice to. Possible values:
+ deleted file no longer exists
+ conflict file contents, mode or timestamp was
+ changed and buffer is modified
+ changed file contents has changed
+ mode mode of file changed
+ time only file timestamp changed
+ ]=],
+ },
fname = {
type = 'string',
desc = [=[
@@ -287,6 +288,13 @@ M.vars = {
detected. Empty otherwise.
]=],
},
+ fname_diff = {
+ type = 'string',
+ desc = [=[
+ The name of the diff (patch) file. Only valid while
+ evaluating 'patchexpr'.
+ ]=],
+ },
fname_in = {
type = 'string',
desc = [=[
@@ -298,6 +306,13 @@ M.vars = {
And set to the swap file name for |SwapExists|.
]=],
},
+ fname_new = {
+ type = 'string',
+ desc = [=[
+ The name of the new version of the file. Only valid while
+ evaluating 'diffexpr'.
+ ]=],
+ },
fname_out = {
type = 'string',
desc = [=[
@@ -313,20 +328,6 @@ M.vars = {
file and different from v:fname_in.
]=],
},
- fname_new = {
- type = 'string',
- desc = [=[
- The name of the new version of the file. Only valid while
- evaluating 'diffexpr'.
- ]=],
- },
- fname_diff = {
- type = 'string',
- desc = [=[
- The name of the diff (patch) file. Only valid while
- evaluating 'patchexpr'.
- ]=],
- },
folddashes = {
type = 'string',
desc = [=[
@@ -335,17 +336,17 @@ M.vars = {
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
- foldlevel = {
+ foldend = {
type = 'integer',
desc = [=[
- Used for 'foldtext': foldlevel of closed fold.
+ Used for 'foldtext': last line of closed fold.
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
- foldend = {
+ foldlevel = {
type = 'integer',
desc = [=[
- Used for 'foldtext': last line of closed fold.
+ Used for 'foldtext': foldlevel of closed fold.
Read-only in the |sandbox|. |fold-foldtext|
]=],
},
@@ -435,19 +436,12 @@ M.vars = {
2147483647 on all systems.
]=],
},
- mouse_win = {
- type = 'integer',
- desc = [=[
- Window number for a mouse click obtained with |getchar()|.
- First window has number 1, like with |winnr()|. The value is
- zero when there was no mouse button click.
- ]=],
- },
- mouse_winid = {
+ mouse_col = {
type = 'integer',
desc = [=[
- |window-ID| for a mouse click obtained with |getchar()|.
- The value is zero when there was no mouse button click.
+ Column number for a mouse click obtained with |getchar()|.
+ This is the screen column number, like with |virtcol()|. The
+ value is zero when there was no mouse button click.
]=],
},
mouse_lnum = {
@@ -458,12 +452,19 @@ M.vars = {
value is zero when there was no mouse button click.
]=],
},
- mouse_col = {
+ mouse_win = {
type = 'integer',
desc = [=[
- Column number for a mouse click obtained with |getchar()|.
- This is the screen column number, like with |virtcol()|. The
- value is zero when there was no mouse button click.
+ Window number for a mouse click obtained with |getchar()|.
+ First window has number 1, like with |winnr()|. The value is
+ zero when there was no mouse button click.
+ ]=],
+ },
+ mouse_winid = {
+ type = 'integer',
+ desc = [=[
+ |window-ID| for a mouse click obtained with |getchar()|.
+ The value is zero when there was no mouse button click.
]=],
},
msgpack_types = {
@@ -516,6 +517,35 @@ M.vars = {
than String this will cause trouble.
]=],
},
+ operator = {
+ type = 'string',
+ desc = [=[
+ The last operator given in Normal mode. This is a single
+ character except for commands starting with <g> or <z>,
+ in which case it is two characters. Best used alongside
+ |v:prevcount| and |v:register|. Useful if you want to cancel
+ Operator-pending mode and then use the operator, e.g.: >vim
+ :omap O <Esc>:call MyMotion(v:operator)<CR>
+ <
+ The value remains set until another operator is entered, thus
+ don't expect it to be empty.
+ v:operator is not set for |:delete|, |:yank| or other Ex
+ commands.
+ Read-only.
+ ]=],
+ },
+ option_command = {
+ type = 'string',
+ desc = [=[
+ Command used to set the option. Valid while executing an
+ |OptionSet| autocommand.
+ value option was set via ~
+ "setlocal" |:setlocal| or `:let l:xxx`
+ "setglobal" |:setglobal| or `:let g:xxx`
+ "set" |:set| or |:let|
+ "modeline" |modeline|
+ ]=],
+ },
option_new = {
desc = [=[
New value of the option. Valid while executing an |OptionSet|
@@ -530,15 +560,15 @@ M.vars = {
global old value.
]=],
},
- option_oldlocal = {
+ option_oldglobal = {
desc = [=[
- Old local value of the option. Valid while executing an
+ Old global value of the option. Valid while executing an
|OptionSet| autocommand.
]=],
},
- option_oldglobal = {
+ option_oldlocal = {
desc = [=[
- Old global value of the option. Valid while executing an
+ Old local value of the option. Valid while executing an
|OptionSet| autocommand.
]=],
},
@@ -549,35 +579,6 @@ M.vars = {
|OptionSet| autocommand. Can be either "global" or "local"
]=],
},
- option_command = {
- type = 'string',
- desc = [=[
- Command used to set the option. Valid while executing an
- |OptionSet| autocommand.
- value option was set via ~
- "setlocal" |:setlocal| or `:let l:xxx`
- "setglobal" |:setglobal| or `:let g:xxx`
- "set" |:set| or |:let|
- "modeline" |modeline|
- ]=],
- },
- operator = {
- type = 'string',
- desc = [=[
- The last operator given in Normal mode. This is a single
- character except for commands starting with <g> or <z>,
- in which case it is two characters. Best used alongside
- |v:prevcount| and |v:register|. Useful if you want to cancel
- Operator-pending mode and then use the operator, e.g.: >vim
- :omap O <Esc>:call MyMotion(v:operator)<CR>
- <
- The value remains set until another operator is entered, thus
- don't expect it to be empty.
- v:operator is not set for |:delete|, |:yank| or other Ex
- commands.
- Read-only.
- ]=],
- },
prevcount = {
type = 'integer',
desc = [=[
@@ -641,6 +642,17 @@ M.vars = {
hit-enter prompt.
]=],
},
+ searchforward = {
+ type = 'integer',
+ desc = [=[
+ Search direction: 1 after a forward search, 0 after a
+ backward search. It is reset to forward when directly setting
+ the last search pattern, see |quote/|.
+ Note that the value is restored when returning from a
+ function. |function-search-undo|.
+ Read-write.
+ ]=],
+ },
servername = {
type = 'string',
desc = [=[
@@ -664,17 +676,6 @@ M.vars = {
Note the contents of $NVIM may change in the future.
]=],
},
- searchforward = {
- type = 'integer',
- desc = [=[
- Search direction: 1 after a forward search, 0 after a
- backward search. It is reset to forward when directly setting
- the last search pattern, see |quote/|.
- Note that the value is restored when returning from a
- function. |function-search-undo|.
- Read-write.
- ]=],
- },
shell_error = {
type = 'integer',
desc = [=[
@@ -709,14 +710,6 @@ M.vars = {
<
]=],
},
- swapname = {
- type = 'string',
- desc = [=[
- Name of the swapfile found.
- Only valid during |SwapExists| event.
- Read-only.
- ]=],
- },
swapchoice = {
type = 'string',
desc = [=[
@@ -743,6 +736,14 @@ M.vars = {
For ":edit +cmd file" the value is ":cmd\r".
]=],
},
+ swapname = {
+ type = 'string',
+ desc = [=[
+ Name of the swapfile found.
+ Only valid during |SwapExists| event.
+ Read-only.
+ ]=],
+ },
t_blob = {
type = 'integer',
tags = { 'v:t_TYPE' },
@@ -776,22 +777,22 @@ M.vars = {
type = 'integer',
desc = 'Value of |String| type. Read-only. See: |type()|',
},
- termresponse = {
+ termrequest = {
type = 'string',
desc = [=[
The value of the most recent OSC or DCS control sequence
- received by Nvim from the terminal. This can be read in a
- |TermResponse| event handler after querying the terminal using
- another escape sequence.
+ sent from a process running in the embedded |terminal|.
+ This can be read in a |TermRequest| event handler to respond
+ to queries from embedded applications.
]=],
},
- termrequest = {
+ termresponse = {
type = 'string',
desc = [=[
The value of the most recent OSC or DCS control sequence
- sent from a process running in the embedded |terminal|.
- This can be read in a |TermRequest| event handler to respond
- to queries from embedded applications.
+ received by Nvim from the terminal. This can be read in a
+ |TermResponse| event handler after querying the terminal using
+ another escape sequence.
]=],
},
testing = {
@@ -849,20 +850,20 @@ M.vars = {
<
]=],
},
- virtnum = {
+ vim_did_enter = {
type = 'integer',
desc = [=[
- Virtual line number for the 'statuscolumn' expression.
- Negative when drawing the status column for virtual lines, zero
- when drawing an actual buffer line, and positive when drawing
- the wrapped part of a buffer line.
+ 0 during startup, 1 just before |VimEnter|.
Read-only.
]=],
},
- vim_did_enter = {
+ virtnum = {
type = 'integer',
desc = [=[
- 0 during startup, 1 just before |VimEnter|.
+ Virtual line number for the 'statuscolumn' expression.
+ Negative when drawing the status column for virtual lines, zero
+ when drawing an actual buffer line, and positive when drawing
+ the wrapped part of a buffer line.
Read-only.
]=],
},
diff --git a/src/nvim/window.c b/src/nvim/window.c
index d3280a3478..c3f3e075f1 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -857,7 +857,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
String anchor = cstr_as_string(float_anchor_str[c.anchor]);
if (!c.hide) {
ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
- grid->handle, row, col, c.focusable,
+ grid->handle, row, col, c.mouse,
wp->w_grid_alloc.zindex);
} else {
ui_call_win_hide(wp->w_grid_alloc.handle);
@@ -889,7 +889,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
wp->w_height_outer, wp->w_width_outer, valid, false);
ui_check_cursor_grid(wp->w_grid_alloc.handle);
- wp->w_grid_alloc.focusable = wp->w_config.focusable;
+ wp->w_grid_alloc.mouse_enabled = wp->w_config.mouse;
if (!valid) {
wp->w_grid_alloc.valid = false;
redraw_later(wp, UPD_NOT_VALID);
@@ -4044,6 +4044,7 @@ void win_alloc_aucmd_win(int idx)
fconfig.width = Columns;
fconfig.height = 5;
fconfig.focusable = false;
+ fconfig.mouse = false;
aucmd_win[idx].auc_win = win_new_float(NULL, true, fconfig, &err);
aucmd_win[idx].auc_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win[idx].auc_win);
@@ -6754,8 +6755,8 @@ void win_comp_scroll(win_T *wp)
if (wp->w_p_scr != old_w_p_scr) {
// Used by "verbose set scroll".
- wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT;
- wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_lnum = 0;
+ wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_sid = SID_WINLAYOUT;
+ wp->w_p_script_ctx[kWinOptScroll].script_ctx.sc_lnum = 0;
}
}
@@ -6813,11 +6814,13 @@ void command_height(void)
// Recompute window positions.
win_comp_pos();
- // clear the lines added to cmdline
- if (full_screen) {
- grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0);
+ if (!need_wait_return) {
+ // clear the lines added to cmdline
+ if (full_screen) {
+ grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0);
+ }
+ msg_row = cmdline_row;
}
- msg_row = cmdline_row;
redraw_cmdline = true;
return;
}
@@ -7370,18 +7373,37 @@ static int int_cmp(const void *pa, const void *pb)
return a == b ? 0 : a < b ? -1 : 1;
}
-/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
+/// Check "cc" as 'colorcolumn' and update the members of "wp".
+/// This is called when 'colorcolumn' or 'textwidth' is changed.
+///
+/// @param cc when NULL: use "wp->w_p_cc"
+/// @param wp when NULL: only parse "cc"
///
/// @return error message, NULL if it's OK.
-const char *check_colorcolumn(win_T *wp)
+const char *check_colorcolumn(char *cc, win_T *wp)
{
- if (wp->w_buffer == NULL) {
+ if (wp != NULL && wp->w_buffer == NULL) {
return NULL; // buffer was closed
}
+ char *s = empty_string_option;
+ if (cc != NULL) {
+ s = cc;
+ } else if (wp != NULL) {
+ s = wp->w_p_cc;
+ }
+
+ OptInt tw;
+ if (wp != NULL) {
+ tw = wp->w_buffer->b_p_tw;
+ } else {
+ // buffer-local value not set, assume zero
+ tw = 0;
+ }
+
unsigned count = 0;
int color_cols[256];
- for (char *s = wp->w_p_cc; *s != NUL && count < 255;) {
+ while (*s != NUL && count < 255) {
int col;
if (*s == '-' || *s == '+') {
// -N and +N: add to 'textwidth'
@@ -7391,16 +7413,12 @@ const char *check_colorcolumn(win_T *wp)
return e_invarg;
}
col = col * getdigits_int(&s, true, 0);
- if (wp->w_buffer->b_p_tw == 0) {
+ if (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)
- || (col < 0
- && 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;
+ assert((col >= 0 && tw <= INT_MAX - col && tw + col >= INT_MIN)
+ || (col < 0 && tw >= INT_MIN - col && tw + col <= INT_MAX));
+ col += (int)tw;
if (col < 0) {
goto skip;
}
@@ -7422,6 +7440,10 @@ skip:
}
}
+ if (wp == NULL) {
+ return NULL; // only parse "cc"
+ }
+
xfree(wp->w_p_cc_cols);
if (count == 0) {
wp->w_p_cc_cols = NULL;
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
index fdb65ad614..b8a51d686d 100644
--- a/src/nvim/winfloat.c
+++ b/src/nvim/winfloat.c
@@ -137,7 +137,7 @@ void win_set_minimal_style(win_T *wp)
? xstrdup("EndOfBuffer:")
: concat_str(old, ",EndOfBuffer:"));
free_string_option(old);
- parse_winhl_opt(wp);
+ parse_winhl_opt(NULL, wp);
// signcolumn: use 'auto'
if (wp->w_p_scl[0] != 'a' || strlen(wp->w_p_scl) >= 8) {
@@ -389,6 +389,7 @@ win_T *win_float_create(bool enter, bool new_buf)
config.row = curwin->w_wrow;
config.relative = kFloatRelativeEditor;
config.focusable = false;
+ config.mouse = false;
config.anchor = 0; // NW
config.noautocmd = true;
config.hide = true;
@@ -411,8 +412,8 @@ win_T *win_float_create(bool enter, bool new_buf)
return handle_error_and_cleanup(wp, &err);
}
buf->b_p_bl = false; // unlist
- set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, kOptReqBuf,
- buf);
+ set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
win_set_buf(wp, buf, &err);
if (ERROR_SET(&err)) {
return handle_error_and_cleanup(wp, &err);
diff --git a/src/nvim/yankmap.c b/src/nvim/yankmap.c
index 591bcffe33..12b70fed65 100644
--- a/src/nvim/yankmap.c
+++ b/src/nvim/yankmap.c
@@ -20,7 +20,7 @@ yankreg_T* yankmap_get(yankmap_T* yankmap, int reg)
if (ret) {
if (is_new) {
- *ret = xcalloc(sizeof(yankreg_T), 1);
+ *ret = xcalloc(1, sizeof(yankreg_T));
}
/* Add the back-reference */
diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg
index 50cee638af..91a72fa318 100644
--- a/src/uncrustify.cfg
+++ b/src/uncrustify.cfg
@@ -1,4 +1,4 @@
-# Uncrustify-0.79.0_f
+# Uncrustify-0.80.1_f
#
# General options
@@ -422,6 +422,10 @@ sp_invariant_paren = ignore # ignore/add/remove/force
sp_after_invariant_paren = ignore # ignore/add/remove/force
# Add or remove space before empty statement ';' on 'if', 'for' and 'while'.
+# examples:
+# if (b) <here> ;
+# for (a=1; a<10; a++) <here> ;
+# while (*p++ = ' ') <here> ;
sp_special_semi = ignore # ignore/add/remove/force
# Add or remove space before ';'.
@@ -1103,6 +1107,10 @@ sp_after_emb_cmt = force # ignore/add/remove/force
# Default: 1
sp_num_after_emb_cmt = 1 # unsigned number
+# Embedded comment spacing options have higher priority (== override)
+# than other spacing options (comma, parenthesis, braces, ...)
+sp_emb_cmt_priority = false # true/false
+
# (Java) Add or remove space between an annotation and the open parenthesis.
sp_annotation_paren = ignore # ignore/add/remove/force
@@ -1127,6 +1135,9 @@ force_tab_after_define = false # true/false
# Add or remove space between two strings.
sp_string_string = force # ignore/add/remove/force
+# Add or remove space 'struct' and a type.
+sp_struct_type = ignore # ignore/add/remove/force
+
#
# Indenting options
#
diff --git a/src/vterm/vterm.c b/src/vterm/vterm.c
index 870a61566e..e8c87929e2 100644
--- a/src/vterm/vterm.c
+++ b/src/vterm/vterm.c
@@ -1,8 +1,9 @@
#include "vterm_internal.h"
+#include "auto/config.h"
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
#include <string.h>
/*****************
@@ -429,3 +430,509 @@ void vterm_check_version(int major, int minor)
// Happy
}
+
+// For unit tests.
+#ifndef NDEBUG
+
+int parser_text(const char bytes[], size_t len, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "text ");
+ int i;
+ for(i = 0; i < len; i++) {
+ unsigned char b = bytes[i];
+ if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) {
+ break;
+ }
+ fprintf(f, i ? ",%x" : "%x", b);
+ }
+ fprintf(f, "\n");
+ fclose(f);
+
+ return i;
+}
+
+static void printchars(const char *s, size_t len, FILE *f)
+{
+ while(len--) {
+ fprintf(f, "%c", (s++)[0]);
+ }
+}
+
+int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "csi %02x", command);
+
+ if(leader && leader[0]) {
+ fprintf(f, " L=");
+ for(int i = 0; leader[i]; i++) {
+ fprintf(f, "%02x", leader[i]);
+ }
+ }
+
+ for(int i = 0; i < argcount; i++) {
+ char sep = i ? ',' : ' ';
+
+ if(args[i] == CSI_ARG_MISSING) {
+ fprintf(f, "%c*", sep);
+ } else {
+ fprintf(f, "%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : "");
+ }
+ }
+
+ if(intermed && intermed[0]) {
+ fprintf(f, " I=");
+ for(int i = 0; intermed[i]; i++) {
+ fprintf(f, "%02x", intermed[i]);
+ }
+ }
+
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+int parser_osc(int command, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "osc ");
+
+ if(frag.initial) {
+ if(command == -1) {
+ fprintf(f, "[");
+ } else {
+ fprintf(f, "[%d;", command);
+ }
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if(frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "dcs ");
+
+ if(frag.initial) {
+ fprintf(f, "[");
+ for(int i = 0; i < commandlen; i++) {
+ fprintf(f, "%c", command[i]);
+ }
+ }
+
+ printchars(frag.str, frag.len,f);
+
+ if(frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_apc(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "apc ");
+
+ if(frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if(frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_pm(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "pm ");
+
+ if(frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len,f);
+
+ if(frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_sos(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sos ");
+
+ if(frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len,f);
+
+ if(frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "selection-set mask=%04X ", mask);
+ if(frag.initial) {
+ fprintf(f, "[");
+}
+ printchars(frag.str, frag.len, f);
+ if(frag.final) {
+ fprintf(f, "]");
+}
+ fprintf(f,"\n");
+
+ fclose(f);
+ return 1;
+}
+
+int selection_query(VTermSelectionMask mask, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f,"selection-query mask=%04X\n", mask);
+
+ fclose(f);
+ return 1;
+}
+
+bool want_state_putglyph;
+int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ if(!want_state_putglyph) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "putglyph ");
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
+ fprintf(f, i ? ",%x" : "%x", info->chars[i]);
+ }
+ fprintf(f, " %d %d,%d", info->width, pos.row, pos.col);
+ if(info->protected_cell) {
+ fprintf(f, " prot");
+ }
+ if(info->dwl) {
+ fprintf(f, " dwl");
+ }
+ if(info->dhl) {
+ fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" );
+ }
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+bool want_state_movecursor;
+VTermPos state_pos;
+int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ state_pos = pos;
+
+ if(want_state_movecursor) {
+ fprintf(f,"movecursor %d,%d\n", pos.row, pos.col);
+ }
+
+ fclose(f);
+ return 1;
+}
+
+bool want_state_scrollrect;
+int state_scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ if(!want_state_scrollrect) {
+ return 0;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ fprintf(f,"scrollrect %d..%d,%d..%d => %+d,%+d\n",
+ rect.start_row, rect.end_row, rect.start_col, rect.end_col,
+ downward, rightward);
+
+ fclose(f);
+ return 1;
+}
+
+bool want_state_moverect;
+int state_moverect(VTermRect dest, VTermRect src, void *user)
+{
+ if(!want_state_moverect) {
+ return 0;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f,"moverect %d..%d,%d..%d -> %d..%d,%d..%d\n",
+ src.start_row, src.end_row, src.start_col, src.end_col,
+ dest.start_row, dest.end_row, dest.start_col, dest.end_col);
+
+ fclose(f);
+ return 1;
+}
+
+void print_color(const VTermColor *col)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ if (VTERM_COLOR_IS_RGB(col)) {
+ fprintf(f,"rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue);
+ }
+ else if (VTERM_COLOR_IS_INDEXED(col)) {
+ fprintf(f,"idx(%d", col->indexed.idx);
+ }
+ else {
+ fprintf(f,"invalid(%d", col->type);
+ }
+ if (VTERM_COLOR_IS_DEFAULT_FG(col)) {
+ fprintf(f,",is_default_fg");
+ }
+ if (VTERM_COLOR_IS_DEFAULT_BG(col)) {
+ fprintf(f,",is_default_bg");
+ }
+ fprintf(f,")");
+ fclose(f);
+}
+
+bool want_state_settermprop;
+int state_settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ if(!want_state_settermprop) {
+ return 1;
+ }
+
+ int errcode = 0;
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ VTermValueType type = vterm_get_prop_type(prop);
+ switch(type) {
+ case VTERM_VALUETYPE_BOOL:
+ fprintf(f,"settermprop %d %s\n", prop, val->boolean ? "true" : "false");
+ errcode = 1;
+ goto end;
+ case VTERM_VALUETYPE_INT:
+ fprintf(f,"settermprop %d %d\n", prop, val->number);
+ errcode = 1;
+ goto end;
+ case VTERM_VALUETYPE_STRING:
+ fprintf(f,"settermprop %d %s\"%.*s\"%s\n", prop,
+ val->string.initial ? "[" : "", (int)val->string.len, val->string.str, val->string.final ? "]" : "");
+ errcode=0;
+ goto end;
+ case VTERM_VALUETYPE_COLOR:
+ fprintf(f,"settermprop %d ", prop);
+ print_color(&val->color);
+ fprintf(f,"\n");
+ errcode=1;
+ goto end;
+ case VTERM_N_VALUETYPES:
+ goto end;
+ }
+
+end:
+ fclose(f);
+ return errcode;
+}
+
+bool want_state_erase;
+int state_erase(VTermRect rect, int selective, void *user)
+{
+ if(!want_state_erase) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ fprintf(f,"erase %d..%d,%d..%d%s\n",
+ rect.start_row, rect.end_row, rect.start_col, rect.end_col,
+ selective ? " selective" : "");
+
+ fclose(f);
+ return 1;
+}
+
+struct {
+ int bold;
+ int underline;
+ int italic;
+ int blink;
+ int reverse;
+ int conceal;
+ int strike;
+ int font;
+ int small;
+ int baseline;
+ VTermColor foreground;
+ VTermColor background;
+} state_pen;
+
+int state_setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ switch(attr) {
+ case VTERM_ATTR_BOLD:
+ state_pen.bold = val->boolean;
+ break;
+ case VTERM_ATTR_UNDERLINE:
+ state_pen.underline = val->number;
+ break;
+ case VTERM_ATTR_ITALIC:
+ state_pen.italic = val->boolean;
+ break;
+ case VTERM_ATTR_BLINK:
+ state_pen.blink = val->boolean;
+ break;
+ case VTERM_ATTR_REVERSE:
+ state_pen.reverse = val->boolean;
+ break;
+ case VTERM_ATTR_CONCEAL:
+ state_pen.conceal = val->boolean;
+ break;
+ case VTERM_ATTR_STRIKE:
+ state_pen.strike = val->boolean;
+ break;
+ case VTERM_ATTR_FONT:
+ state_pen.font = val->number;
+ break;
+ case VTERM_ATTR_SMALL:
+ state_pen.small = val->boolean;
+ break;
+ case VTERM_ATTR_BASELINE:
+ state_pen.baseline = val->number;
+ break;
+ case VTERM_ATTR_FOREGROUND:
+ state_pen.foreground = val->color;
+ break;
+ case VTERM_ATTR_BACKGROUND:
+ state_pen.background = val->color;
+ break;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+bool want_state_scrollback;
+int state_sb_clear(void *user) {
+ if(!want_state_scrollback) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f,"sb_clear\n");
+ fclose(f);
+
+ return 0;
+}
+
+bool want_screen_scrollback;
+int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
+{
+ if(!want_screen_scrollback) {
+ return 1;
+ }
+
+ int eol = cols;
+ while(eol && !cells[eol-1].chars[0]) {
+ eol--;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_pushline %d =", cols);
+ for(int c = 0; c < eol; c++) {
+ fprintf(f, " %02X", cells[c].chars[0]);
+ }
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
+{
+ if(!want_screen_scrollback) {
+ return 0;
+ }
+
+ // All lines of scrollback contain "ABCDE"
+ for(int col = 0; col < cols; col++) {
+ if(col < 5) {
+ cells[col].chars[0] = 'A' + col;
+ } else {
+ cells[col].chars[0] = 0;
+ }
+
+ cells[col].width = 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f,"sb_popline %d\n", cols);
+ fclose(f);
+ return 1;
+}
+
+int screen_sb_clear(void *user)
+{
+ if(!want_screen_scrollback) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_clear\n");
+ fclose(f);
+ return 0;
+}
+
+void term_output(const char *s, size_t len, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "output ");
+ for(int i = 0; i < len; i++) {
+ fprintf(f, "%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n");
+ }
+ fclose(f);
+}
+
+#endif
diff --git a/src/vterm/vterm.h b/src/vterm/vterm.h
index 929418c63a..df6878f744 100644
--- a/src/vterm/vterm.h
+++ b/src/vterm/vterm.h
@@ -9,6 +9,7 @@ extern "C" {
#include <stdlib.h>
#include <stdbool.h>
+#include "nvim/macros_defs.h"
#include "vterm_keycodes.h"
#define VTERM_VERSION_MAJOR 0
@@ -21,7 +22,7 @@ extern "C" {
/* Any cell can contain at most one basic printing character and 5 combining
* characters. This number could be changed but will be ABI-incompatible if
* you do */
-#define VTERM_MAX_CHARS_PER_CELL 6
+enum{ VTERM_MAX_CHARS_PER_CELL=6};
typedef struct VTerm VTerm;
typedef struct VTermState VTermState;
@@ -634,6 +635,40 @@ void vterm_copy_cells(VTermRect dest,
void (*copycell)(VTermPos dest, VTermPos src, void *user),
void *user);
+#ifndef NDEBUG
+int parser_text(const char bytes[], size_t len, void *user);
+int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+int parser_osc(int command, VTermStringFragment frag, void *user);
+int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+int parser_apc(VTermStringFragment frag, void *user);
+int parser_pm(VTermStringFragment frag, void *user);
+int parser_sos(VTermStringFragment frag, void *user);
+int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+int selection_query(VTermSelectionMask mask, void *user);
+int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user);
+int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
+int state_scrollrect(VTermRect rect, int downward, int rightward, void *user);
+int state_moverect(VTermRect dest, VTermRect src, void *user);
+int state_settermprop(VTermProp prop, VTermValue *val, void *user);
+int state_erase(VTermRect rect, int selective, void *user);
+int state_setpenattr(VTermAttr attr, VTermValue *val, void *user);
+int state_sb_clear(void *user);
+void print_color(const VTermColor *col);
+int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user);
+int screen_sb_popline(int cols, VTermScreenCell *cells, void *user);
+int screen_sb_clear(void *user);
+void term_output(const char *s, size_t len, void *user);
+EXTERN VTermPos state_pos;
+EXTERN bool want_state_putglyph INIT (=false);
+EXTERN bool want_state_movecursor INIT(= false);
+EXTERN bool want_state_erase INIT(= false);
+EXTERN bool want_state_scrollrect INIT(= false);
+EXTERN bool want_state_moverect INIT(= false);
+EXTERN bool want_state_settermprop INIT(= false);
+EXTERN bool want_state_scrollback INIT(= false);
+EXTERN bool want_screen_scrollback INIT(= false);
+#endif
+
#ifdef __cplusplus
}
#endif