diff options
Diffstat (limited to 'src')
148 files changed, 2106 insertions, 2022 deletions
diff --git a/src/clint.py b/src/clint.py index 41058469b1..051f0e91e5 100755 --- a/src/clint.py +++ b/src/clint.py @@ -1995,7 +1995,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcpy, xmemcpyz or snprintf instead of %s' % match.group(1)) - match = Search(r'\b(STRNCAT|strncat|strcat|vim_strcat)\b', line) + match = Search(r'\b(STRNCAT|strncat|vim_strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcat or snprintf instead of %s' % match.group(1)) diff --git a/src/man/nvim.1 b/src/man/nvim.1 index 4dc099f98c..9b7680d011 100644 --- a/src/man/nvim.1 +++ b/src/man/nvim.1 @@ -387,10 +387,10 @@ features like .El .Sh FILES .Bl -tag -width "~/.config/nvim/init.vim" -.It Pa ~/.config/nvim/init.vim +.It Pa ~/.config/nvim/init.lua User-local .Nm -configuration file. +Lua configuration file. .It Pa ~/.config/nvim User-local .Nm diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7e64808709..a0678dc3e4 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1183,12 +1183,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, return rv; } -/// call a function with buffer as temporary current buffer +/// Call a function with buffer as temporary current buffer. /// /// This temporarily switches current buffer to "buffer". -/// If the current window already shows "buffer", the window is not switched +/// If the current window already shows "buffer", the window is not switched. /// If a window inside the current tabpage (including a float) already shows the -/// buffer One of these windows will be set as current window temporarily. +/// buffer, then one of these windows will be set as current window temporarily. /// Otherwise a temporary scratch window (called the "autocmd window" for /// historical reasons) will be used. /// @@ -1375,7 +1375,7 @@ static inline void init_line_array(lua_State *lstate, Array *a, size_t size, Are /// @param s String to push /// @param len Size of string /// @param idx 0-based index to place s (only used for Lua) -/// @param replace_nl Replace newlines ('\n') with null ('\0') +/// @param replace_nl Replace newlines ('\n') with null (NUL) static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx, bool replace_nl, Arena *arena) { @@ -1384,7 +1384,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, if (s && replace_nl && strchr(s, '\n')) { // TODO(bfredl): could manage scratch space in the arena, for the NUL case char *tmp = xmemdupz(s, len); - strchrsub(tmp, '\n', '\0'); + strchrsub(tmp, '\n', NUL); lua_pushlstring(lstate, tmp, len); xfree(tmp); } else { @@ -1397,7 +1397,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, str = CBUF_TO_ARENA_STR(arena, s, len); if (replace_nl) { // Vim represents NULs as NLs, but this may confuse clients. - strchrsub(str.data, '\n', '\0'); + strchrsub(str.data, '\n', NUL); } } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 779e216c74..5ad439af9c 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -193,7 +193,7 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err } else { nargs[0] = '0'; } - nargs[1] = '\0'; + nargs[1] = NUL; PUT_KEY(result, cmd, nargs, CSTR_TO_ARENA_OBJ(arena, nargs)); char *addr; @@ -391,7 +391,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena case kObjectTypeBoolean: data_str = arena_alloc(arena, 2, false); data_str[0] = elem.data.boolean ? '1' : '0'; - data_str[1] = '\0'; + data_str[1] = NUL; ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeBuffer: diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index af3bfe2c03..a1af354577 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -170,7 +170,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID }; extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true, - false, false, false, false, NULL); + false, false, false, NULL); return src_id; } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 85cce45560..ab6ff5ff1f 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -18,6 +18,7 @@ #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" +#include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" #include "nvim/map_defs.h" @@ -41,6 +42,7 @@ void api_extmark_free_all_mem(void) xfree(name.data); }) map_destroy(String, &namespace_ids); + set_destroy(uint32_t, &namespace_localscope); } /// Creates a new namespace or gets an existing one. [namespace]() @@ -179,10 +181,6 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); } - if (mt_scoped(start)) { - PUT_C(dict, "scoped", BOOLEAN_OBJ(true)); - } - decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); ADD_C(rv, DICTIONARY_OBJ(dict)); @@ -489,8 +487,6 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// used together with virt_text. /// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control /// sequence is used to generate a clickable hyperlink to this URL. -/// - scoped: boolean (EXPERIMENTAL) enables "scoping" for the extmark. See -/// |nvim__win_add_ns()| /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -749,11 +745,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { - if (opts->scoped) { - api_set_error(err, kErrorTypeException, "not yet implemented"); - goto error; - } - int r = (int)line; int c = (int)col; if (line2 == -1) { @@ -834,7 +825,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, decor, decor_flags, right_gravity, opts->end_right_gravity, !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), - opts->invalidate, opts->scoped, err); + opts->invalidate, err); if (ERROR_SET(err)) { decor_free(decor); return 0; @@ -960,7 +951,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In decor.data.hl.hl_id = hl_id; extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - decor, MT_FLAG_DECOR_HL, true, false, false, false, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL); return ns_id; } @@ -1038,7 +1029,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// ``` /// - on_win: called when starting to redraw a specific window. /// ``` -/// ["win", winid, bufnr, topline, botline] +/// ["win", winid, bufnr, toprow, botrow] /// ``` /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) @@ -1217,77 +1208,119 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error /// EXPERIMENTAL: this API will change in the future. /// -/// Scopes a namespace to the a window, so extmarks in the namespace will be active only in the -/// given window. +/// Set some properties for namespace /// -/// @param window Window handle, or 0 for current window /// @param ns_id Namespace -/// @return true if the namespace was added, else false -Boolean nvim__win_add_ns(Window window, Integer ns_id, Error *err) +/// @param opts Optional parameters to set: +/// - wins: a list of windows to be scoped in +/// +void nvim__ns_set(Integer ns_id, Dict(ns_opts) *opts, Error *err) { - win_T *win = find_window_by_handle(window, err); - if (!win) { - return false; - } - VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { - return false; + return; }); - set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + bool set_scoped = true; - if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { - changed_window_setting(win); - } + if (HAS_KEY(opts, ns_opts, wins)) { + if (opts->wins.size == 0) { + set_scoped = false; + } - return true; -} + Set(ptr_t) windows = SET_INIT; + for (size_t i = 0; i < opts->wins.size; i++) { + Integer win = opts->wins.items[i].data.integer; -/// EXPERIMENTAL: this API will change in the future. -/// -/// Gets the namespace scopes for a given window. -/// -/// @param window Window handle, or 0 for current window -/// @return a list of namespaces ids -ArrayOf(Integer) nvim__win_get_ns(Window window, Arena *arena, Error *err) -{ - win_T *win = find_window_by_handle(window, err); - if (!win) { - return (Array)ARRAY_DICT_INIT; + win_T *wp = find_window_by_handle((Window)win, err); + if (!wp) { + return; + } + + set_put(ptr_t, &windows, wp); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(ptr_t, &windows, wp) && !set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + set_put(uint32_t, &wp->w_ns_set, (uint32_t)ns_id); + + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id) && !set_has(ptr_t, &windows, wp)) { + set_del(uint32_t, &wp->w_ns_set, (uint32_t)ns_id); + + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } + + set_destroy(ptr_t, &windows); } - Array rv = arena_array(arena, set_size(&win->w_ns_set)); - uint32_t i; - set_foreach(&win->w_ns_set, i, { - ADD_C(rv, INTEGER_OBJ((Integer)(i))); - }); + if (set_scoped && !set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + set_put(uint32_t, &namespace_localscope, (uint32_t)ns_id); - return rv; + // When a namespace becomes scoped, any window which contains + // elements associated with namespace needs to be redrawn + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } else if (!set_scoped && set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + set_del(uint32_t, &namespace_localscope, (uint32_t)ns_id); + + // When a namespace becomes unscoped, any window which does not + // contain elements associated with namespace needs to be redrawn + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } } /// EXPERIMENTAL: this API will change in the future. /// -/// Unscopes a namespace (un-binds it from the given scope). +/// Get the properties for namespace /// -/// @param window Window handle, or 0 for current window -/// @param ns_id the namespace to remove -/// @return true if the namespace was removed, else false -Boolean nvim__win_del_ns(Window window, Integer ns_id, Error *err) +/// @param ns_id Namespace +/// @return Map defining the namespace properties, see |nvim__ns_set()| +Dict(ns_opts) nvim__ns_get(Integer ns_id, Arena *arena, Error *err) { - win_T *win = find_window_by_handle(window, err); - if (!win) { - return false; + Dict(ns_opts) opts = KEYDICT_INIT; + + Array windows = ARRAY_DICT_INIT; + + PUT_KEY(opts, ns_opts, wins, windows); + + VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { + return opts; + }); + + if (!set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + return opts; } - if (!set_has(uint32_t, &win->w_ns_set, (uint32_t)ns_id)) { - return false; + size_t count = 0; + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + count++; + } } - set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + windows = arena_array(arena, count); - if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { - changed_window_setting(win); + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + ADD(windows, INTEGER_OBJ(wp->handle)); + } } - return true; + PUT_KEY(opts, ns_opts, wins, windows); + + return opts; } diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h index 124feaabfb..af2d51c95c 100644 --- a/src/nvim/api/extmark.h +++ b/src/nvim/api/extmark.h @@ -4,14 +4,29 @@ #include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/buffer_defs.h" #include "nvim/decoration_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/types_defs.h" EXTERN Map(String, int) namespace_ids INIT( = MAP_INIT); +/// Non-global namespaces. A locally-scoped namespace may be "orphaned" if all +/// window(s) it was scoped to, are destroyed. Such orphans are tracked here to +/// avoid being mistaken as "global scope". +EXTERN Set(uint32_t) namespace_localscope INIT( = SET_INIT); EXTERN handle_T next_namespace_id INIT( = 1); +/// Returns true if the namespace is global or scoped in the given window. +static inline bool ns_in_win(uint32_t ns_id, win_T *wp) +{ + if (!set_has(uint32_t, &namespace_localscope, ns_id)) { + return true; + } + + return set_has(uint32_t, &wp->w_ns_set, ns_id); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/extmark.h.generated.h" #endif diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 00d8aa8428..cc2ef981b5 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -387,3 +387,8 @@ typedef struct { Window win; Buffer buf; } Dict(redraw); + +typedef struct { + OptionalKeys is_set__ns_opts_; + Array wins; +} Dict(ns_opts); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index d9bc0ccc92..5adaff8449 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -54,6 +54,10 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * } if (HAS_KEY_X(opts, buf)) { + VALIDATE(!(HAS_KEY_X(opts, scope) && *scope == OPT_GLOBAL), "%s", + "cannot use both global 'scope' and 'buf'", { + return FAIL; + }); *scope = OPT_LOCAL; *req_scope = kOptReqBuf; *from = find_buffer_by_handle(opts->buf, err); @@ -68,11 +72,6 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * return FAIL; }); - VALIDATE((!HAS_KEY_X(opts, scope) || !HAS_KEY_X(opts, buf)), "%s", - "cannot use both 'scope' and 'buf'", { - return FAIL; - }); - VALIDATE((!HAS_KEY_X(opts, win) || !HAS_KEY_X(opts, buf)), "%s", "cannot use both 'buf' and 'win'", { return FAIL; diff --git a/src/nvim/api/private/validate.c b/src/nvim/api/private/validate.c index e198c671eb..9fd7d3bfa6 100644 --- a/src/nvim/api/private/validate.c +++ b/src/nvim/api/private/validate.c @@ -17,7 +17,7 @@ void api_err_invalid(Error *err, const char *name, const char *val_s, int64_t va char *has_space = strchr(name, ' '); // No value. - if (val_s && val_s[0] == '\0') { + if (val_s && val_s[0] == NUL) { api_set_error(err, errtype, has_space ? "Invalid %s" : "Invalid '%s'", name); return; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 52ab18cbff..26dc223948 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -28,6 +28,7 @@ #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -313,7 +314,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) keys_esc = keys.data; } if (lowlevel) { - input_enqueue_raw(cstr_as_string(keys_esc)); + input_enqueue_raw(keys_esc, strlen(keys_esc)); } else { ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), insert ? 0 : typebuf.tb_len, !typed, false); diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 477cbe2428..124c26d175 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -109,7 +109,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error * // redir usually (except :echon) prepends a newline. if (s.data[0] == '\n') { memmove(s.data, s.data + 1, s.size - 1); - s.data[s.size - 1] = '\0'; + s.data[s.size - 1] = NUL; s.size = s.size - 1; } return s; // Caller will free the memory. diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 70235d8db6..f0b90d8512 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -17,6 +17,7 @@ #include "nvim/decoration.h" #include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/window.h" #include "nvim/extmark_defs.h" #include "nvim/globals.h" diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 54a19513db..92dc9dc7e3 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -13,6 +13,7 @@ #include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" #include "nvim/gettext_defs.h" diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index 4d493c9d03..700bfc1655 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -13,6 +13,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index c5d81d4cd2..ebed3e6f03 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -15,6 +15,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" @@ -710,7 +711,7 @@ char *au_event_disable(char *what) if (*what == ',' && *p_ei == NUL) { STRCPY(new_ei, what + 1); } else { - STRCAT(new_ei, what); + strcat(new_ei, what); } set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE); xfree(new_ei); @@ -823,11 +824,11 @@ void do_autocmd(exarg_T *eap, char *arg_in, int forceit) continue; } - invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6); - invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8); + invalid_flags |= arg_autocmd_flag_get(&once, &cmd, S_LEN("++once")); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, S_LEN("++nested")); // Check the deprecated "nested" flag. - invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, S_LEN("nested")); } if (invalid_flags) { @@ -1244,7 +1245,7 @@ void ex_doautoall(exarg_T *eap) bool check_nomodeline(char **argp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - if (strncmp(*argp, "<nomodeline>", 12) == 0) { + if (strncmp(*argp, S_LEN("<nomodeline>")) == 0) { *argp = skipwhite(*argp + 12); return false; } @@ -2358,7 +2359,7 @@ theend: bool aupat_is_buflocal(const char *pat, int patlen) FUNC_ATTR_PURE { - return patlen >= 8 && strncmp(pat, "<buffer", 7) == 0 && (pat)[patlen - 1] == '>'; + return patlen >= 8 && strncmp(pat, S_LEN("<buffer")) == 0 && (pat)[patlen - 1] == '>'; } int aupat_get_buflocal_nr(const char *pat, int patlen) diff --git a/src/nvim/base64.c b/src/nvim/base64.c index a645c64fe3..99d3c5a33e 100644 --- a/src/nvim/base64.c +++ b/src/nvim/base64.c @@ -4,6 +4,7 @@ #include <string.h> #include "auto/config.h" // IWYU pragma: keep +#include "nvim/ascii_defs.h" #include "nvim/base64.h" #include "nvim/memory.h" @@ -125,7 +126,7 @@ char *base64_encode(const char *src, size_t src_len) dest[out_i] = '='; } - dest[out_len] = '\0'; + dest[out_len] = NUL; return dest; } @@ -141,6 +142,7 @@ char *base64_encode(const char *src, size_t src_len) /// @param [out] out_lenp Returns the length of the decoded string /// @return Decoded string char *base64_decode(const char *src, size_t src_len, size_t *out_lenp) + FUNC_ATTR_NONNULL_ALL { assert(src != NULL); assert(out_lenp != NULL); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 39d0d24d47..3f4e7047f9 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -43,6 +43,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" @@ -2076,6 +2077,7 @@ void free_buf_options(buf_T *buf, bool free_p_ff) clear_string_option(&buf->b_p_lop); clear_string_option(&buf->b_p_cinsd); clear_string_option(&buf->b_p_cinw); + clear_string_option(&buf->b_p_cot); clear_string_option(&buf->b_p_cpt); clear_string_option(&buf->b_p_cfu); callback_free(&buf->b_cfu_cb); @@ -3848,8 +3850,8 @@ static int chk_modeline(linenr_T lnum, int flags) int prev = -1; for (s = ml_get(lnum); *s != NUL; s++) { if (prev == -1 || ascii_isspace(prev)) { - if ((prev != -1 && strncmp(s, "ex:", 3) == 0) - || strncmp(s, "vi:", 3) == 0) { + if ((prev != -1 && strncmp(s, S_LEN("ex:")) == 0) + || strncmp(s, S_LEN("vi:")) == 0) { break; } // Accept both "vim" and "Vim". @@ -3865,7 +3867,7 @@ static int chk_modeline(linenr_T lnum, int flags) if (*e == ':' && (s[0] != 'V' - || strncmp(skipwhite(e + 1), "set", 3) == 0) + || strncmp(skipwhite(e + 1), S_LEN("set")) == 0) && (s[3] == ':' || (VIM_VERSION_100 >= vers && isdigit((uint8_t)s[3])) || (VIM_VERSION_100 < vers && s[3] == '<') @@ -3914,8 +3916,8 @@ static int chk_modeline(linenr_T lnum, int flags) // "vi:set opt opt opt: foo" -- foo not interpreted // "vi:opt opt opt: foo" -- foo interpreted // Accept "se" for compatibility with Elvis. - if (strncmp(s, "set ", 4) == 0 - || strncmp(s, "se ", 3) == 0) { + if (strncmp(s, S_LEN("set ")) == 0 + || strncmp(s, S_LEN("se ")) == 0) { if (*e != ':') { // no terminating ':'? break; } @@ -4207,7 +4209,7 @@ int buf_open_scratch(handle_T bufnr, char *bufname) bool buf_is_empty(buf_T *buf) { - return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == '\0'; + return buf->b_ml.ml_line_count == 1 && *ml_get_buf(buf, 1) == NUL; } /// Increment b:changedtick value diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 512247047c..221a86a907 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -533,6 +533,8 @@ struct file_buffer { char *b_p_cinsd; ///< 'cinscopedecls' char *b_p_com; ///< 'comments' char *b_p_cms; ///< 'commentstring' + char *b_p_cot; ///< 'completeopt' local value + unsigned b_cot_flags; ///< flags for 'completeopt' char *b_p_cpt; ///< 'complete' #ifdef BACKSLASH_IN_FILENAME char *b_p_csl; ///< 'completeslash' diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c index 27de03954a..5522ab1ca3 100644 --- a/src/nvim/bufwrite.c +++ b/src/nvim/bufwrite.c @@ -18,6 +18,7 @@ #include "nvim/bufwrite.h" #include "nvim/change.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds.h" @@ -1065,7 +1066,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en bool whole = (start == 1 && end == buf->b_ml.ml_line_count); bool write_undo_file = false; context_sha256_T sha_ctx; - unsigned bkc = get_bkc_value(buf); + unsigned bkc = get_bkc_flags(buf); if (fname == NULL || *fname == NUL) { // safety check return FAIL; diff --git a/src/nvim/change.c b/src/nvim/change.c index 05772d39e9..6c979df1fe 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1723,12 +1723,12 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Below, set_indent(newindent, SIN_INSERT) will insert the // whitespace needed before the comment char. for (int i = 0; i < padding; i++) { - STRCAT(leader, " "); + strcat(leader, " "); less_cols--; newcol++; } } - STRCAT(leader, p_extra); + strcat(leader, p_extra); p_extra = leader; did_ai = true; // So truncating blanks works with comments less_cols -= lead_len; diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 41635747f8..e3df12abbe 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -13,6 +13,7 @@ #include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" @@ -38,8 +39,6 @@ #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" #include "nvim/path.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/terminal.h" #include "nvim/types_defs.h" @@ -126,19 +125,19 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) *error = e_invstream; return false; } - stream_may_close(&chan->stream.socket); + rstream_may_close(&chan->stream.socket); break; case kChannelStreamProc: proc = &chan->stream.proc; if (part == kChannelPartStdin || close_main) { - stream_may_close(&proc->in); + wstream_may_close(&proc->in); } if (part == kChannelPartStdout || close_main) { - stream_may_close(&proc->out); + rstream_may_close(&proc->out); } if (part == kChannelPartStderr || part == kChannelPartAll) { - stream_may_close(&proc->err); + rstream_may_close(&proc->err); } if (proc->type == kProcessTypePty && part == kChannelPartAll) { pty_process_close_master(&chan->stream.pty); @@ -148,10 +147,10 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) case kChannelStreamStdio: if (part == kChannelPartStdin || close_main) { - stream_may_close(&chan->stream.stdio.in); + rstream_may_close(&chan->stream.stdio.in); } if (part == kChannelPartStdout || close_main) { - stream_may_close(&chan->stream.stdio.out); + wstream_may_close(&chan->stream.stdio.out); } if (part == kChannelPartStderr) { *error = e_invstream; @@ -431,7 +430,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s wstream_init(&proc->in, 0); } if (has_out) { - rstream_init(&proc->out, 0); + rstream_init(&proc->out); } if (rpc) { @@ -446,7 +445,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s if (has_err) { callback_reader_start(&chan->on_stderr, "stderr"); - rstream_init(&proc->err, 0); + rstream_init(&proc->err); rstream_start(&proc->err, on_job_stderr, chan); } @@ -480,10 +479,10 @@ uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader return 0; } - channel->stream.socket.internal_close_cb = close_cb; - channel->stream.socket.internal_data = channel; - wstream_init(&channel->stream.socket, 0); - rstream_init(&channel->stream.socket, 0); + channel->stream.socket.s.internal_close_cb = close_cb; + channel->stream.socket.s.internal_data = channel; + wstream_init(&channel->stream.socket.s, 0); + rstream_init(&channel->stream.socket); if (rpc) { rpc_start(channel); @@ -505,10 +504,10 @@ void channel_from_connection(SocketWatcher *watcher) { Channel *channel = channel_alloc(kChannelStreamSocket); socket_watcher_accept(watcher, &channel->stream.socket); - channel->stream.socket.internal_close_cb = close_cb; - channel->stream.socket.internal_data = channel; - wstream_init(&channel->stream.socket, 0); - rstream_init(&channel->stream.socket, 0); + channel->stream.socket.s.internal_close_cb = close_cb; + channel->stream.socket.s.internal_data = channel; + wstream_init(&channel->stream.socket.s, 0); + rstream_init(&channel->stream.socket); rpc_start(channel); channel_create_event(channel, watcher->addr); } @@ -553,7 +552,7 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err dup2(STDERR_FILENO, STDIN_FILENO); } #endif - rstream_init_fd(&main_loop, &channel->stream.stdio.in, stdin_dup_fd, 0); + rstream_init_fd(&main_loop, &channel->stream.stdio.in, stdin_dup_fd); wstream_init_fd(&main_loop, &channel->stream.stdio.out, stdout_dup_fd, 0); if (rpc) { @@ -640,58 +639,45 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len) list_T *const l = tv_list_alloc(kListLenMayKnow); // Empty buffer should be represented by [''], encode_list_write() thinks // empty list is fine for the case. - tv_list_append_string(l, "", 0); + tv_list_append_string(l, S_LEN("")); if (len > 0) { encode_list_write(l, buf, len); } return l; } -void on_channel_data(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) +size_t on_channel_data(RStream *stream, const char *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, eof, &chan->on_data); + return on_channel_output(stream, chan, buf, count, eof, &chan->on_data); } -void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) +size_t on_job_stderr(RStream *stream, const char *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, eof, &chan->on_stderr); + return on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr); } -static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, bool eof, - CallbackReader *reader) +static size_t on_channel_output(RStream *stream, Channel *chan, const char *buf, size_t count, + bool eof, CallbackReader *reader) { - size_t count; - char *output = rbuffer_read_ptr(buf, &count); - if (chan->term) { - if (!eof) { - char *p = output; - char *end = output + count; + if (count) { + const char *p = buf; + const char *end = buf + count; while (p < end) { // Don't pass incomplete UTF-8 sequences to libvterm. #16245 // Composing chars can be passed separately, so utf_ptr2len_len() is enough. int clen = utf_ptr2len_len(p, (int)(end - p)); if (clen > end - p) { - count = (size_t)(p - output); + count = (size_t)(p - buf); break; } p += clen; } } - terminal_receive(chan->term, output, count); - } - - if (count) { - rbuffer_consumed(buf, count); - } - // Move remaining data to start of buffer, so the buffer can never wrap around. - rbuffer_reset(buf); - - if (callback_reader_set(*reader)) { - ga_concat_len(&reader->buffer, output, count); + terminal_receive(chan->term, buf, count); } if (eof) { @@ -699,8 +685,11 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, bool } if (callback_reader_set(*reader)) { + ga_concat_len(&reader->buffer, buf, count); schedule_channel_event(chan); } + + return count; } /// schedule the necessary callbacks to be invoked as a deferred event @@ -864,7 +853,7 @@ static void term_resize(uint16_t width, uint16_t height, void *data) static inline void term_delayed_free(void **argv) { Channel *chan = argv[0]; - if (chan->stream.proc.in.pending_reqs || chan->stream.proc.out.pending_reqs) { + if (chan->stream.proc.in.pending_reqs || chan->stream.proc.out.s.pending_reqs) { multiqueue_put(chan->events, term_delayed_free, chan); return; } diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 35d369e513..72480db0d5 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -30,7 +30,7 @@ struct Channel { Process proc; LibuvProcess uv; PtyProcess pty; - Stream socket; + RStream socket; StdioPair stdio; StderrState err; InternalState internal; @@ -73,7 +73,7 @@ static inline Stream *channel_instream(Channel *chan) return &chan->stream.proc.in; case kChannelStreamSocket: - return &chan->stream.socket; + return &chan->stream.socket.s; case kChannelStreamStdio: return &chan->stream.stdio.out; @@ -85,10 +85,10 @@ static inline Stream *channel_instream(Channel *chan) abort(); } -static inline Stream *channel_outstream(Channel *chan) +static inline RStream *channel_outstream(Channel *chan) REAL_FATTR_NONNULL_ALL; -static inline Stream *channel_outstream(Channel *chan) +static inline RStream *channel_outstream(Channel *chan) { switch (chan->streamtype) { case kChannelStreamProc: diff --git a/src/nvim/channel_defs.h b/src/nvim/channel_defs.h index d4f1895420..2df6edea7a 100644 --- a/src/nvim/channel_defs.h +++ b/src/nvim/channel_defs.h @@ -30,7 +30,7 @@ typedef enum { } ChannelStdinMode; typedef struct { - Stream in; + RStream in; Stream out; } StdioPair; diff --git a/src/nvim/charset.c b/src/nvim/charset.c index c611d4cfd6..430f6b15fe 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1470,7 +1470,7 @@ start: *dst++ = *p++; } } - *dst = '\0'; + *dst = NUL; } } @@ -1492,6 +1492,6 @@ char *backslash_halve_save(const char *p) *dst++ = *p++; } } - *dst = '\0'; + *dst = NUL; return res; } diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 808df44941..fdb452aee4 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -19,6 +19,7 @@ #include "nvim/cmdexpand.h" #include "nvim/cmdhist.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" @@ -2246,7 +2247,7 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff) // Does command allow "++argopt" argument? if (ea.argt & EX_ARGOPT) { - while (*arg != NUL && strncmp(arg, "++", 2) == 0) { + while (*arg != NUL && strncmp(arg, S_LEN("++")) == 0) { p = arg + 2; while (*p && !ascii_isspace(*p)) { MB_PTR_ADV(p); @@ -2773,7 +2774,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM // When expanding a function name starting with s:, match the <SNR>nr_ // prefix. char *tofree = NULL; - if (xp->xp_context == EXPAND_USER_FUNC && strncmp(pat, "^s:", 3) == 0) { + if (xp->xp_context == EXPAND_USER_FUNC && strncmp(pat, S_LEN("^s:")) == 0) { const size_t len = strlen(pat) + 20; tofree = xmalloc(len); @@ -3074,7 +3075,7 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T typval_T args[4]; const sctx_T save_current_sctx = current_sctx; - if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) { + if (xp->xp_arg == NULL || xp->xp_arg[0] == NUL || xp->xp_line == NULL) { return NULL; } @@ -3256,7 +3257,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir copy_option_part(&path, buf, MAXPATHL, ","); if (strlen(buf) + strlen(file) + 2 < MAXPATHL) { add_pathsep(buf); - STRCAT(buf, file); + strcat(buf, file); char **p; int num_p = 0; @@ -3563,7 +3564,7 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (xpc.xp_context == EXPAND_USER_DEFINED) { // Must be "custom,funcname" pattern - if (strncmp(type, "custom,", 7) != 0) { + if (strncmp(type, S_LEN("custom,")) != 0) { semsg(_(e_invarg2), type); return; } @@ -3573,7 +3574,7 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (xpc.xp_context == EXPAND_USER_LIST) { // Must be "customlist,funcname" pattern - if (strncmp(type, "customlist,", 11) != 0) { + if (strncmp(type, S_LEN("customlist,")) != 0) { semsg(_(e_invarg2), type); return; } diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 983ab8b59b..ab05ae1cfc 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -11,6 +11,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/context.c b/src/nvim/context.c index 95e2618f62..b8eecfbb16 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -261,7 +261,7 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly) HASHTAB_ITER(func_tbl_get(), hi, { const char *const name = hi->hi_key; - bool islambda = (strncmp(name, "<lambda>", 8) == 0); + bool islambda = (strncmp(name, S_LEN("<lambda>")) == 0); bool isscript = ((uint8_t)name[0] == K_SPECIAL); if (!islambda && (!scriptonly || isscript)) { @@ -299,7 +299,7 @@ static inline void ctx_restore_funcs(Context *ctx) static inline Array sbuf_to_array(msgpack_sbuffer sbuf, Arena *arena) { list_T *const list = tv_list_alloc(kListLenMayKnow); - tv_list_append_string(list, "", 0); + tv_list_append_string(list, S_LEN("")); if (sbuf.size > 0) { encode_list_write(list, sbuf.data, sbuf.size); } diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index 7d87b61ce5..ffbeee7f7a 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -13,6 +13,7 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -520,18 +521,18 @@ static int dbg_parsearg(char *arg, garray_T *gap) struct debuggy *bp = &DEBUGGY(gap, gap->ga_len); // Find "func" or "file". - if (strncmp(p, "func", 4) == 0) { + if (strncmp(p, S_LEN("func")) == 0) { bp->dbg_type = DBG_FUNC; - } else if (strncmp(p, "file", 4) == 0) { + } else if (strncmp(p, S_LEN("file")) == 0) { bp->dbg_type = DBG_FILE; - } else if (gap != &prof_ga && strncmp(p, "here", 4) == 0) { + } else if (gap != &prof_ga && strncmp(p, S_LEN("here")) == 0) { if (curbuf->b_ffname == NULL) { emsg(_(e_noname)); return FAIL; } bp->dbg_type = DBG_FILE; here = true; - } else if (gap != &prof_ga && strncmp(p, "expr", 4) == 0) { + } else if (gap != &prof_ga && strncmp(p, S_LEN("expr")) == 0) { bp->dbg_type = DBG_EXPR; } else { semsg(_(e_invarg2), p); @@ -558,7 +559,7 @@ static int dbg_parsearg(char *arg, garray_T *gap) } if (bp->dbg_type == DBG_FUNC) { - bp->dbg_name = xstrdup(strncmp(p, "g:", 2) == 0 ? p + 2 : p); + bp->dbg_name = xstrdup(strncmp(p, S_LEN("g:")) == 0 ? p + 2 : p); } else if (here) { bp->dbg_name = xstrdup(curbuf->b_ffname); } else if (bp->dbg_type == DBG_EXPR) { diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 303d0318b5..0cf02d96da 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -15,6 +15,7 @@ #include "nvim/drawscreen.h" #include "nvim/extmark.h" #include "nvim/fold.h" +#include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/grid_defs.h" #include "nvim/highlight.h" @@ -88,7 +89,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start extmark_set(buf, (uint32_t)src_id, NULL, (int)lnum - 1, hl_start, (int)lnum - 1 + end_off, hl_end, - decor, MT_FLAG_DECOR_HL, true, false, true, false, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, true, false, NULL); } } @@ -184,6 +185,21 @@ void buf_put_decor(buf_T *buf, DecorInline decor, int row, int row2) } } +/// When displaying signs in the 'number' column, if the width of the number +/// column is less than 2, then force recomputing the width after placing or +/// unplacing the first sign in "buf". +static void may_force_numberwidth_recompute(buf_T *buf, bool unplace) +{ + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf + && wp->w_minscwidth == SCL_NUM + && (wp->w_p_nu || wp->w_p_rnu) + && (unplace || wp->w_nrwidth_width < 2)) { + wp->w_nrwidth_line_count = 0; + } + } +} + static int sign_add_id = 0; void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2) { @@ -191,6 +207,7 @@ void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2) sh->sign_add_id = sign_add_id++; if (sh->text[0]) { buf_signcols_count_range(buf, row1, row2, 1, kFalse); + may_force_numberwidth_recompute(buf, false); } } } @@ -218,6 +235,7 @@ void buf_remove_decor_sh(buf_T *buf, int row1, int row2, DecorSignHighlight *sh) if (buf_meta_total(buf, kMTMetaSignText)) { buf_signcols_count_range(buf, row1, row2, -1, kFalse); } else { + may_force_numberwidth_recompute(buf, true); buf->b_signcols.resized = true; buf->b_signcols.max = buf->b_signcols.count[0] = 0; } @@ -580,7 +598,7 @@ int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *s break; } - if (!mt_scoped_in_win(mark, wp)) { + if (!ns_in_win(mark.ns, wp)) { goto next_mark; } @@ -729,7 +747,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], break; } if (!mt_end(mark) && !mt_invalid(mark) && mt_decor_sign(mark) - && mt_scoped_in_win(mark, wp)) { + && ns_in_win(mark.ns, wp)) { DecorSignHighlight *sh = decor_find_sign(mt_decor(mark)); num_text += (sh->text[0] != NUL); kv_push(signs, ((SignItem){ sh, mark.id })); @@ -909,7 +927,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo while (true) { MTKey mark = marktree_itr_current(itr); DecorVirtText *vt = mt_decor_virt(mark); - if (mt_scoped_in_win(mark, wp)) { + if (ns_in_win(mark.ns, wp)) { while (vt) { if (vt->flags & kVTIsLines) { bool above = vt->flags & kVTLinesAbove; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index ea846b46ec..750f0c3525 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -26,6 +26,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" @@ -1019,7 +1020,7 @@ static int check_external_diff(diffio_T *diffio) if (fd == NULL) { io_error = true; } else { - if (fwrite("line1\n", 6, 1, fd) != 1) { + if (fwrite(S_LEN("line1\n"), 1, fd) != 1) { io_error = true; } fclose(fd); @@ -1028,7 +1029,7 @@ static int check_external_diff(diffio_T *diffio) if (fd == NULL) { io_error = true; } else { - if (fwrite("line2\n", 6, 1, fd) != 1) { + if (fwrite(S_LEN("line2\n"), 1, fd) != 1) { io_error = true; } fclose(fd); @@ -1049,8 +1050,8 @@ static int check_external_diff(diffio_T *diffio) break; } - if (strncmp(linebuf, "1c1", 3) == 0 - || strncmp(linebuf, "@@ -1 +1 @@", 11) == 0) { + if (strncmp(linebuf, S_LEN("1c1")) == 0 + || strncmp(linebuf, S_LEN("@@ -1 +1 @@")) == 0) { ok = kTrue; } } @@ -1272,10 +1273,10 @@ void ex_diffpatch(exarg_T *eap) // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); - STRCAT(buf, ".orig"); + strcat(buf, ".orig"); os_remove(buf); STRCPY(buf, tmp_new); - STRCAT(buf, ".rej"); + strcat(buf, ".rej"); os_remove(buf); // Only continue if the output file was created. @@ -1287,7 +1288,7 @@ void ex_diffpatch(exarg_T *eap) } else { if (curbuf->b_fname != NULL) { newname = xstrnsave(curbuf->b_fname, strlen(curbuf->b_fname) + 4); - STRCAT(newname, ".new"); + strcat(newname, ".new"); } // don't use a new tab page, each tab page has its own diffs @@ -1582,13 +1583,13 @@ static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) // @@ -1,3 +1,5 @@ if (isdigit((uint8_t)(*line))) { *diffstyle = DIFF_ED; - } else if ((strncmp(line, "@@ ", 3) == 0)) { + } else if ((strncmp(line, S_LEN("@@ ")) == 0)) { *diffstyle = DIFF_UNIFIED; - } else if ((strncmp(line, "--- ", 4) == 0) + } else if ((strncmp(line, S_LEN("--- ")) == 0) && (vim_fgets(line, LBUFLEN, fd) == 0) - && (strncmp(line, "+++ ", 4) == 0) + && (strncmp(line, S_LEN("+++ ")) == 0) && (vim_fgets(line, LBUFLEN, fd) == 0) - && (strncmp(line, "@@ ", 3) == 0)) { + && (strncmp(line, S_LEN("@@ ")) == 0)) { *diffstyle = DIFF_UNIFIED; } else { // Format not recognized yet, skip over this line. Cygwin diff @@ -1606,7 +1607,7 @@ static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) } } else { assert(*diffstyle == DIFF_UNIFIED); - if (strncmp(line, "@@ ", 3) != 0) { + if (strncmp(line, S_LEN("@@ ")) != 0) { continue; // not the start of a diff block } if (parse_diff_unified(line, hunk) == FAIL) { @@ -2472,70 +2473,70 @@ int diffopt_changed(void) char *p = p_dip; while (*p != NUL) { // Note: Keep this in sync with p_dip_values - if (strncmp(p, "filler", 6) == 0) { + if (strncmp(p, S_LEN("filler")) == 0) { p += 6; diff_flags_new |= DIFF_FILLER; - } else if ((strncmp(p, "context:", 8) == 0) && ascii_isdigit(p[8])) { + } else if ((strncmp(p, S_LEN("context:")) == 0) && ascii_isdigit(p[8])) { p += 8; diff_context_new = getdigits_int(&p, false, diff_context_new); - } else if (strncmp(p, "iblank", 6) == 0) { + } else if (strncmp(p, S_LEN("iblank")) == 0) { p += 6; diff_flags_new |= DIFF_IBLANK; - } else if (strncmp(p, "icase", 5) == 0) { + } else if (strncmp(p, S_LEN("icase")) == 0) { p += 5; diff_flags_new |= DIFF_ICASE; - } else if (strncmp(p, "iwhiteall", 9) == 0) { + } else if (strncmp(p, S_LEN("iwhiteall")) == 0) { p += 9; diff_flags_new |= DIFF_IWHITEALL; - } else if (strncmp(p, "iwhiteeol", 9) == 0) { + } else if (strncmp(p, S_LEN("iwhiteeol")) == 0) { p += 9; diff_flags_new |= DIFF_IWHITEEOL; - } else if (strncmp(p, "iwhite", 6) == 0) { + } else if (strncmp(p, S_LEN("iwhite")) == 0) { p += 6; diff_flags_new |= DIFF_IWHITE; - } else if (strncmp(p, "horizontal", 10) == 0) { + } else if (strncmp(p, S_LEN("horizontal")) == 0) { p += 10; diff_flags_new |= DIFF_HORIZONTAL; - } else if (strncmp(p, "vertical", 8) == 0) { + } else if (strncmp(p, S_LEN("vertical")) == 0) { p += 8; diff_flags_new |= DIFF_VERTICAL; - } else if ((strncmp(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) { + } else if ((strncmp(p, S_LEN("foldcolumn:")) == 0) && ascii_isdigit(p[11])) { p += 11; diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new); - } else if (strncmp(p, "hiddenoff", 9) == 0) { + } else if (strncmp(p, S_LEN("hiddenoff")) == 0) { p += 9; diff_flags_new |= DIFF_HIDDEN_OFF; - } else if (strncmp(p, "closeoff", 8) == 0) { + } else if (strncmp(p, S_LEN("closeoff")) == 0) { p += 8; diff_flags_new |= DIFF_CLOSE_OFF; - } else if (strncmp(p, "followwrap", 10) == 0) { + } else if (strncmp(p, S_LEN("followwrap")) == 0) { p += 10; diff_flags_new |= DIFF_FOLLOWWRAP; - } else if (strncmp(p, "indent-heuristic", 16) == 0) { + } else if (strncmp(p, S_LEN("indent-heuristic")) == 0) { p += 16; diff_indent_heuristic = XDF_INDENT_HEURISTIC; - } else if (strncmp(p, "internal", 8) == 0) { + } else if (strncmp(p, S_LEN("internal")) == 0) { p += 8; diff_flags_new |= DIFF_INTERNAL; - } else if (strncmp(p, "algorithm:", 10) == 0) { + } else if (strncmp(p, S_LEN("algorithm:")) == 0) { // Note: Keep this in sync with p_dip_algorithm_values. p += 10; - if (strncmp(p, "myers", 5) == 0) { + if (strncmp(p, S_LEN("myers")) == 0) { p += 5; diff_algorithm_new = 0; - } else if (strncmp(p, "minimal", 7) == 0) { + } else if (strncmp(p, S_LEN("minimal")) == 0) { p += 7; diff_algorithm_new = XDF_NEED_MINIMAL; - } else if (strncmp(p, "patience", 8) == 0) { + } else if (strncmp(p, S_LEN("patience")) == 0) { p += 8; diff_algorithm_new = XDF_PATIENCE_DIFF; - } else if (strncmp(p, "histogram", 9) == 0) { + } else if (strncmp(p, S_LEN("histogram")) == 0) { p += 9; diff_algorithm_new = XDF_HISTOGRAM_DIFF; } else { return FAIL; } - } else if ((strncmp(p, "linematch:", 10) == 0) && ascii_isdigit(p[10])) { + } else if ((strncmp(p, S_LEN("linematch:")) == 0) && ascii_isdigit(p[10])) { p += 10; linematch_lines_new = getdigits_int(&p, false, linematch_lines_new); diff_flags_new |= DIFF_LINEMATCH; diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index a358a1723a..26fb77df30 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -12,6 +12,7 @@ #include "nvim/charset.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 4d534d78a2..4247705896 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -465,6 +465,7 @@ static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, i int fill = nrcol ? number_width(wp) + 1 : SIGN_WIDTH; draw_col_fill(wlv, schar_from_ascii(' '), fill, attr); int sign_pos = wlv->off - SIGN_WIDTH - (int)nrcol; + assert(sign_pos >= 0); linebuf_char[sign_pos] = sattr.text[0]; linebuf_char[sign_pos + 1] = sattr.text[1]; } else { @@ -1421,7 +1422,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s line = ml_get_buf(wp->w_buffer, lnum); ptr = line + linecol; - if (len == 0 || (int)wp->w_cursor.col > ptr - line) { + if (len == 0 || wp->w_cursor.col > linecol) { // no bad word found at line start, don't check until end of a // word spell_hlf = HLF_COUNT; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 039bbd219c..88e1f302da 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -931,13 +931,7 @@ int showmode(void) msg_ext_clear(true); } - // Don't make non-flushed message part of the showmode and reset global - // variables before flushing to to avoid recursiveness. - bool draw_mode = redraw_mode; - bool clear_cmd = clear_cmdline; - redraw_cmdline = false; - redraw_mode = false; - clear_cmdline = false; + // Don't make non-flushed message part of the showmode. msg_ext_ui_flush(); msg_grid_validate(); @@ -960,8 +954,8 @@ int showmode(void) msg_check_for_delay(false); // if the cmdline is more than one line high, erase top lines - bool need_clear = clear_cmd; - if (clear_cmd && cmdline_row < Rows - 1) { + bool need_clear = clear_cmdline; + if (clear_cmdline && cmdline_row < Rows - 1) { msg_clr_cmdline(); // will reset clear_cmdline } @@ -1083,7 +1077,7 @@ int showmode(void) } mode_displayed = true; - if (need_clear || clear_cmd || draw_mode) { + if (need_clear || clear_cmdline || redraw_mode) { msg_clr_eos(); } msg_didout = false; // overwrite this message @@ -1092,10 +1086,10 @@ int showmode(void) msg_no_more = false; lines_left = save_lines_left; need_wait_return = nwr_save; // never ask for hit-return for this - } else if (clear_cmd && msg_silent == 0) { + } else if (clear_cmdline && msg_silent == 0) { // Clear the whole command line. Will reset "clear_cmdline". msg_clr_cmdline(); - } else if (draw_mode) { + } else if (redraw_mode) { msg_pos_mode(); msg_clr_eos(); } @@ -1118,6 +1112,10 @@ int showmode(void) grid_line_flush(); } + redraw_cmdline = false; + redraw_mode = false; + clear_cmdline = false; + return length; } @@ -1548,6 +1546,7 @@ static void win_update(win_T *wp) // Force redraw when width of 'number' or 'relativenumber' column changes. if (wp->w_nrwidth != nrwidth_new) { type = UPD_NOT_VALID; + changed_line_abv_curs_win(wp); wp->w_nrwidth = nrwidth_new; } else { // Set mod_top to the first line that needs displaying because of diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 220b92d099..889f445c3d 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -18,6 +18,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" @@ -473,7 +474,8 @@ static int insert_check(VimState *state) if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, - curbuf->b_p_vts_array) + curbuf->b_p_vts_array, + false) && curwin->w_wrow == curwin->w_height_inner - 1 - get_scrolloff_value(curwin) && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { @@ -3093,7 +3095,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { p = get_cursor_line_ptr(); if (skipwhite(p) == p + curwin->w_cursor.col - 4 - && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) { + && strncmp(p + curwin->w_cursor.col - 4, S_LEN("else")) == 0) { return true; } } @@ -4419,18 +4421,30 @@ static bool ins_tab(void) // Delete following spaces. int i = cursor->col - fpos.col; if (i > 0) { - STRMOVE(ptr, ptr + i); + if (!(State & VREPLACE_FLAG)) { + char *newp = xmalloc((size_t)(curbuf->b_ml.ml_line_len - i)); + ptrdiff_t col = ptr - curbuf->b_ml.ml_line_ptr; + if (col > 0) { + memmove(newp, ptr - col, (size_t)col); + } + memmove(newp + col, ptr + i, (size_t)(curbuf->b_ml.ml_line_len - col - i)); + if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { + xfree(curbuf->b_ml.ml_line_ptr); + } + curbuf->b_ml.ml_line_ptr = newp; + curbuf->b_ml.ml_line_len -= i; + curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; + inserted_bytes(fpos.lnum, change_col, + cursor->col - change_col, fpos.col - change_col); + } else { + STRMOVE(ptr, ptr + i); + } // correct replace stack. if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { for (temp = i; --temp >= 0;) { replace_join(repl_off); } } - if (!(State & VREPLACE_FLAG)) { - curbuf->b_ml.ml_line_len -= i; - inserted_bytes(fpos.lnum, change_col, - cursor->col - change_col, fpos.col - change_col); - } } cursor->col -= i; diff --git a/src/nvim/errors.h b/src/nvim/errors.h new file mode 100644 index 0000000000..39095db952 --- /dev/null +++ b/src/nvim/errors.h @@ -0,0 +1,193 @@ +#pragma once + +#include "nvim/gettext_defs.h" +#include "nvim/macros_defs.h" + +// +// Shared error messages. Excludes errors only used once and debugging messages. +// +// uncrustify:off +EXTERN const char e_abort[] INIT(= N_("E470: Command aborted")); +EXTERN const char e_afterinit[] INIT(= N_("E905: Cannot set this option after startup")); +EXTERN const char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job")); +EXTERN const char e_argreq[] INIT(= N_("E471: Argument required")); +EXTERN const char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); +EXTERN const char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits")); +EXTERN const char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search")); +EXTERN const char e_invalid_buffer_name_str[] INIT(= N_("E158: Invalid buffer name: %s")); +EXTERN const char e_command_too_recursive[] INIT(= N_("E169: Command too recursive")); +EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded")); +EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif")); +EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry")); +EXTERN const char e_endwhile[] INIT(= N_("E170: Missing :endwhile")); +EXTERN const char e_endfor[] INIT(= N_("E170: Missing :endfor")); +EXTERN const char e_while[] INIT(= N_("E588: :endwhile without :while")); +EXTERN const char e_for[] INIT(= N_("E588: :endfor without :for")); +EXTERN const char e_exists[] INIT(= N_("E13: File exists (add ! to override)")); +EXTERN const char e_failed[] INIT(= N_("E472: Command failed")); +EXTERN const char e_internal[] INIT(= N_("E473: Internal error")); +EXTERN const char e_intern2[] INIT(= N_("E685: Internal error: %s")); +EXTERN const char e_interr[] INIT(= N_("Interrupted")); +EXTERN const char e_invarg[] INIT(= N_("E474: Invalid argument")); +EXTERN const char e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); +EXTERN const char e_invargval[] INIT(= N_("E475: Invalid value for argument %s")); +EXTERN const char e_invargNval[] INIT(= N_("E475: Invalid value for argument %s: %s")); +EXTERN const char e_duparg2[] INIT(= N_("E983: Duplicate argument: %s")); +EXTERN const char e_invexpr2[] INIT(= N_("E15: Invalid expression: \"%s\"")); +EXTERN const char e_invrange[] INIT(= N_("E16: Invalid range")); +EXTERN const char e_invcmd[] INIT(= N_("E476: Invalid command")); +EXTERN const char e_isadir2[] INIT(= N_("E17: \"%s\" is a directory")); +EXTERN const char e_no_spell[] INIT(= N_("E756: Spell checking is not possible")); +EXTERN const char e_invchan[] INIT(= N_("E900: Invalid channel id")); +EXTERN const char e_invchanjob[] INIT(= N_("E900: Invalid channel id: not a job")); +EXTERN const char e_jobtblfull[] INIT(= N_("E901: Job table is full")); +EXTERN const char e_jobspawn[] INIT(= N_("E903: Process failed to start: %s: \"%s\"")); +EXTERN const char e_channotpty[] INIT(= N_("E904: channel is not a pty")); +EXTERN const char e_stdiochan2[] INIT(= N_("E905: Couldn't open stdio channel: %s")); +EXTERN const char e_invstream[] INIT(= N_("E906: invalid stream for channel")); +EXTERN const char e_invstreamrpc[] INIT(= N_("E906: invalid stream for rpc channel, use 'rpc'")); +EXTERN const char e_streamkey[] INIT(= N_("E5210: dict key '%s' already set for buffered stream in channel %" PRIu64)); +EXTERN const char e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\"")); +EXTERN const char e_fsync[] INIT(= N_("E667: Fsync failed: %s")); +EXTERN const char e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s")); +EXTERN const char e_markinval[] INIT(= N_("E19: Mark has invalid line number")); +EXTERN const char e_marknotset[] INIT(= N_("E20: Mark not set")); +EXTERN const char e_modifiable[] INIT(= N_("E21: Cannot make changes, 'modifiable' is off")); +EXTERN const char e_nesting[] INIT(= N_("E22: Scripts nested too deep")); +EXTERN const char e_noalt[] INIT(= N_("E23: No alternate file")); +EXTERN const char e_noabbr[] INIT(= N_("E24: No such abbreviation")); +EXTERN const char e_nobang[] INIT(= N_("E477: No ! allowed")); +EXTERN const char e_nogroup[] INIT(= N_("E28: No such highlight group name: %s")); +EXTERN const char e_noinstext[] INIT(= N_("E29: No inserted text yet")); +EXTERN const char e_nolastcmd[] INIT(= N_("E30: No previous command line")); +EXTERN const char e_nomap[] INIT(= N_("E31: No such mapping")); +EXTERN const char e_nomatch[] INIT(= N_("E479: No match")); +EXTERN const char e_nomatch2[] INIT(= N_("E480: No match: %s")); +EXTERN const char e_noname[] INIT(= N_("E32: No file name")); +EXTERN const char e_nopresub[] INIT(= N_("E33: No previous substitute regular expression")); +EXTERN const char e_noprev[] INIT(= N_("E34: No previous command")); +EXTERN const char e_noprevre[] INIT(= N_("E35: No previous regular expression")); +EXTERN const char e_norange[] INIT(= N_("E481: No range allowed")); +EXTERN const char e_noroom[] INIT(= N_("E36: Not enough room")); +EXTERN const char e_notmp[] INIT(= N_("E483: Can't get temp file name")); +EXTERN const char e_notopen[] INIT(= N_("E484: Can't open file %s")); +EXTERN const char e_notopen_2[] INIT(= N_("E484: Can't open file %s: %s")); +EXTERN const char e_notread[] INIT(= N_("E485: Can't read file %s")); +EXTERN const char e_null[] INIT(= N_("E38: Null argument")); +EXTERN const char e_number_exp[] INIT(= N_("E39: Number expected")); +EXTERN const char e_openerrf[] INIT(= N_("E40: Can't open errorfile %s")); +EXTERN const char e_outofmem[] INIT(= N_("E41: Out of memory!")); +EXTERN const char e_patnotf[] INIT(= N_("Pattern not found")); +EXTERN const char e_patnotf2[] INIT(= N_("E486: Pattern not found: %s")); +EXTERN const char e_positive[] INIT(= N_("E487: Argument must be positive")); +EXTERN const char e_prev_dir[] INIT(= N_("E459: Cannot go back to previous directory")); + +EXTERN const char e_no_errors[] INIT(= N_("E42: No Errors")); +EXTERN const char e_loclist[] INIT(= N_("E776: No location list")); +EXTERN const char e_re_damg[] INIT(= N_("E43: Damaged match string")); +EXTERN const char e_re_corr[] INIT(= N_("E44: Corrupted regexp program")); +EXTERN const char e_readonly[] INIT(= N_("E45: 'readonly' option is set (add ! to override)")); +EXTERN const char e_letwrong[] INIT(= N_("E734: Wrong variable type for %s=")); +EXTERN const char e_illvar[] INIT(= N_("E461: Illegal variable name: %s")); +EXTERN const char e_cannot_mod[] INIT(= N_("E995: Cannot modify existing variable")); +EXTERN const char e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%.*s\"")); +EXTERN const char e_stringreq[] INIT(= N_("E928: String required")); +EXTERN const char e_dictreq[] INIT(= N_("E715: Dictionary required")); +EXTERN const char e_blobidx[] INIT(= N_("E979: Blob index out of range: %" PRId64)); +EXTERN const char e_invalblob[] INIT(= N_("E978: Invalid operation for Blob")); +EXTERN const char e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); +EXTERN const char e_toofewarg[] INIT(= N_("E119: Not enough arguments for function: %s")); +EXTERN const char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: \"%s\"")); +EXTERN const char e_dictkey_len[] INIT(= N_("E716: Key not present in Dictionary: \"%.*s\"")); +EXTERN const char e_listreq[] INIT(= N_("E714: List required")); +EXTERN const char e_listblobreq[] INIT(= N_("E897: List or Blob required")); +EXTERN const char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary")); +EXTERN const char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List, Dictionary or Blob")); +EXTERN const char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); +EXTERN const char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox")); +EXTERN const char e_secure[] INIT(= N_("E523: Not allowed here")); +EXTERN const char e_textlock[] INIT(= N_("E565: Not allowed to change text or change window")); +EXTERN const char e_screenmode[] INIT(= N_("E359: Screen mode setting not supported")); +EXTERN const char e_scroll[] INIT(= N_("E49: Invalid scroll size")); +EXTERN const char e_shellempty[] INIT(= N_("E91: 'shell' option is empty")); +EXTERN const char e_signdata[] INIT(= N_("E255: Couldn't read in sign data!")); +EXTERN const char e_swapclose[] INIT(= N_("E72: Close error on swap file")); +EXTERN const char e_toocompl[] INIT(= N_("E74: Command too complex")); +EXTERN const char e_longname[] INIT(= N_("E75: Name too long")); +EXTERN const char e_toomsbra[] INIT(= N_("E76: Too many [")); +EXTERN const char e_toomany[] INIT(= N_("E77: Too many file names")); +EXTERN const char e_trailing[] INIT(= N_("E488: Trailing characters")); +EXTERN const char e_trailing_arg[] INIT(= N_("E488: Trailing characters: %s")); +EXTERN const char e_umark[] INIT(= N_("E78: Unknown mark")); +EXTERN const char e_wildexpand[] INIT(= N_("E79: Cannot expand wildcards")); +EXTERN const char e_winheight[] INIT(= N_("E591: 'winheight' cannot be smaller than 'winminheight'")); +EXTERN const char e_winwidth[] INIT(= N_("E592: 'winwidth' cannot be smaller than 'winminwidth'")); +EXTERN const char e_write[] INIT(= N_("E80: Error while writing")); +EXTERN const char e_zerocount[] INIT(= N_("E939: Positive count required")); +EXTERN const char e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context")); +EXTERN const char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); +EXTERN const char e_empty_buffer[] INIT(= N_("E749: Empty buffer")); +EXTERN const char e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist")); + +EXTERN const char e_str_not_inside_function[] INIT(= N_("E193: %s not inside a function")); + +EXTERN const char e_invalpat[] INIT(= N_("E682: Invalid search pattern or delimiter")); +EXTERN const char e_bufloaded[] INIT(= N_("E139: File is loaded in another buffer")); +EXTERN const char e_notset[] INIT(= N_("E764: Option '%s' is not set")); +EXTERN const char e_invalidreg[] INIT(= N_("E850: Invalid register name")); +EXTERN const char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); +EXTERN const char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); +EXTERN const char e_menu_only_exists_in_another_mode[] +INIT(= N_("E328: Menu only exists in another mode")); +EXTERN const char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window")); +EXTERN const char e_listarg[] INIT(= N_("E686: Argument of %s must be a List")); +EXTERN const char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); +EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long")); +EXTERN const char e_using_float_as_string[] INIT(= N_("E806: Using a Float as a String")); +EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); +EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d")); +EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s")); +EXTERN const char e_auabort[] INIT(= N_("E855: Autocommands caused command to abort")); + +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_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_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")); + +EXTERN const char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range")); + +EXTERN const char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invalid character in group name")); + +EXTERN const char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); + +EXTERN const char e_invalid_column_number_nr[] INIT( = N_("E964: Invalid column number: %ld")); +EXTERN const char e_invalid_line_number_nr[] INIT(= N_("E966: Invalid line number: %ld")); + +EXTERN const char e_stray_closing_curly_str[] +INIT(= N_("E1278: Stray '}' without a matching '{': %s")); +EXTERN const char e_missing_close_curly_str[] +INIT(= N_("E1279: Missing '}': %s")); + +EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s")); + +EXTERN const char e_undobang_cannot_redo_or_move_branch[] +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_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); + +EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s")); + +EXTERN const char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); +EXTERN const char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); + +EXTERN const char line_msg[] INIT(= N_(" line ")); +// uncrustify:on diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 48a58228ae..4cff5e1582 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -24,6 +24,7 @@ #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" @@ -1218,7 +1219,7 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett int len = (int)strlen(func); partial_T *pt = NULL; - if (len >= 6 && !memcmp(func, "v:lua.", 6)) { + if (len >= 6 && !memcmp(func, S_LEN("v:lua."))) { func += 6; len = check_luafunc_name(func, false); if (len == 0) { @@ -2160,7 +2161,7 @@ void del_menutrans_vars(void) { hash_lock(&globvarht); HASHTAB_ITER(&globvarht, hi, { - if (strncmp(hi->hi_key, "menutrans_", 10) == 0) { + if (strncmp(hi->hi_key, S_LEN("menutrans_")) == 0) { delete_var(&globvarht, hi); } }); @@ -3273,7 +3274,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan check_vars(s, (size_t)len); // If evaluate is false rettv->v_type was not set, but it's needed // in handle_subscript() to parse v:lua, so set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, "v:lua.", 6)) { + if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, S_LEN("v:lua."))) { rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = vvlua_partial; rettv->vval.v_partial->pt_refcount++; @@ -3482,7 +3483,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; - if (strnequal(name, "v:lua.", 6)) { + if (strnequal(name, S_LEN("v:lua."))) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later @@ -5617,7 +5618,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) int dict_idx = 0; int arg_idx = 0; list_T *list = NULL; - if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { + if (strncmp(s, S_LEN("s:")) == 0 || strncmp(s, S_LEN("<SID>")) == 0) { // Expand s: and <SID> into <SNR>nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being @@ -6191,7 +6192,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co case kCallbackFuncref: name = callback->data.funcref; int len = (int)strlen(name); - if (len >= 6 && !memcmp(name, "v:lua.", 6)) { + if (len >= 6 && !memcmp(name, S_LEN("v:lua."))) { name += 6; len = check_luafunc_name(name, false); if (len == 0) { @@ -6459,7 +6460,7 @@ bool write_list(FileDescriptor *const fp, const list_T *const list, const bool b } } if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { - const ptrdiff_t written = file_write(fp, "\n", 1); + const ptrdiff_t written = file_write(fp, S_LEN("\n")); if (written < 0) { error = (int)written; goto write_list_error; @@ -7153,8 +7154,8 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex 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); + strcat(retval, temp_result); + strcat(retval, expr_end + 1); } xfree(temp_result); @@ -8909,7 +8910,7 @@ bool eval_has_provider(const char *feat, bool throw_if_fast) char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); - strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. + strchrsub(name, '_', NUL); // Chop any "_xx" suffix. char buf[256]; typval_T tv; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index ceaba11f41..f5706f9553 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -19,7 +19,7 @@ --- @field returns_desc? string --- @field signature? string --- @field desc? string ---- @field params {[1]:string, [2]:string, [3]:string}[] +--- @field params [string, string, string][] --- @field lua? false Do not render type information --- @field tags? string[] Extra tags --- @field data? string Used by gen_eval.lua @@ -3448,7 +3448,7 @@ M.funcs = { < ]=], name = 'getchar', - params = {}, + params = { { 'expr', '0|1' } }, returns = 'integer', signature = 'getchar([{expr}])', }, @@ -3537,7 +3537,7 @@ M.funcs = { result is converted to a string. ]=], name = 'getcharstr', - params = {}, + params = { { 'expr', '0|1' } }, returns = 'string', signature = 'getcharstr([{expr}])', }, @@ -4047,8 +4047,9 @@ M.funcs = { ]=], name = 'getmarklist', - params = { { 'buf', 'any' } }, + params = { { 'buf', 'integer?' } }, signature = 'getmarklist([{buf}])', + returns = 'vim.fn.getmarklist.ret.item[]', }, getmatches = { args = { 0, 1 }, @@ -6080,10 +6081,21 @@ M.funcs = { display isn't updated, e.g. in silent Ex mode) w$ last line visible in current window (this is one less than "w0" if no lines are visible) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. + v When not in Visual mode, returns the cursor + position. In Visual mode, returns the other end + of the Visual area. A good way to think about + this is that in Visual mode "v" and "." complement + each other. While "." refers to the cursor + position, "v" refers to where |v_o| would move the + cursor. As a result, you can use "v" and "." + together to work on all of a selection in + characterwise visual mode. If the cursor is at + the end of a characterwise visual area, "v" refers + to the start of the same visual area. And if the + cursor is at the start of a characterwise visual + area, "v" refers to the end of the same visual + area. "v" differs from |'<| and |'>| in that it's + updated right away. Note that a mark in another file can be used. The line number then applies to another buffer. To get the column number use |col()|. To get both use @@ -6481,7 +6493,8 @@ M.funcs = { echo printf("Operator-pending mode bit: 0x%x", op_bit) ]], name = 'maplist', - params = {}, + params = { { 'abbr', '0|1' } }, + returns = 'table[]', signature = 'maplist([{abbr}])', }, mapnew = { @@ -9142,7 +9155,16 @@ M.funcs = { < ]=], name = 'searchpair', - params = {}, + params = { + { 'start', 'any' }, + { 'middle', 'any' }, + { 'end', 'any' }, + { 'flags', 'string' }, + { 'skip', 'any' }, + { 'stopline', 'any' }, + { 'timeout', 'integer' }, + }, + returns = 'integer', signature = 'searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]])', }, searchpairpos = { @@ -9159,7 +9181,16 @@ M.funcs = { See |match-parens| for a bigger and more useful example. ]=], name = 'searchpairpos', - params = {}, + params = { + { 'start', 'any' }, + { 'middle', 'any' }, + { 'end', 'any' }, + { 'flags', 'string' }, + { 'skip', 'any' }, + { 'stopline', 'any' }, + { 'timeout', 'integer' }, + }, + returns = '[integer, integer]', signature = 'searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]])', }, searchpos = { @@ -11672,7 +11703,7 @@ M.funcs = { ]=], name = 'synconcealed', params = { { 'lnum', 'integer' }, { 'col', 'integer' } }, - returns = '{[1]: integer, [2]: string, [3]: integer}', + returns = '[integer, string, integer]', signature = 'synconcealed({lnum}, {col})', }, synstack = { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index d35ac4eb7b..e216dbdaa6 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -855,7 +855,7 @@ char *encode_tv2string(typval_T *tv, size_t *len) if (len != NULL) { *len = (size_t)ga.ga_len; } - ga_append(&ga, '\0'); + ga_append(&ga, NUL); return (char *)ga.ga_data; } @@ -883,7 +883,7 @@ char *encode_tv2echo(typval_T *tv, size_t *len) if (len != NULL) { *len = (size_t)ga.ga_len; } - ga_append(&ga, '\0'); + ga_append(&ga, NUL); return (char *)ga.ga_data; } @@ -908,7 +908,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) if (len != NULL) { *len = (size_t)ga.ga_len; } - ga_append(&ga, '\0'); + ga_append(&ga, NUL); return (char *)ga.ga_data; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 1b8c057d7c..3255e78d09 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -1,6 +1,7 @@ #include <inttypes.h> #include <stdlib.h> +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/executor.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8b22c7a797..9a1ed7dea9 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -38,6 +38,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/buffer.h" #include "nvim/eval/decode.h" @@ -4317,7 +4318,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en // Set $NVIM (in the child process) to v:servername. #3118 char *nvim_addr = get_vim_var_str(VV_SEND_SERVER); - if (nvim_addr[0] != '\0') { + if (nvim_addr[0] != NUL) { dictitem_T *dv = tv_dict_find(env, S_LEN("NVIM")); if (dv) { tv_dict_item_remove(env, dv); @@ -5094,7 +5095,7 @@ static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, // return a list with the submatches for (int i = 1; i < NSUBEXP; i++) { if (rmp->endp[i] == NULL) { - tv_list_append_string(sml, "", 0); + tv_list_append_string(sml, S_LEN("")); } else { tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]); } @@ -8301,7 +8302,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (col < 0) { return; // type error; errmsg already given } - rettv->vval.v_number = get_sw_value_col(curbuf, col); + rettv->vval.v_number = get_sw_value_col(curbuf, col, false); return; } rettv->vval.v_number = get_sw_value(curbuf); @@ -9173,13 +9174,13 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true); // Trim slash. if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) { - IObuff[len - 1] = '\0'; + IObuff[len - 1] = NUL; } if (len == 1 && IObuff[0] == '/') { // Avoid ambiguity in the URI when CWD is root directory. IObuff[1] = '.'; - IObuff[2] = '\0'; + IObuff[2] = NUL; } // Terminal URI: "term://$CWD//$PID:$CMD" diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index eb8c89c36e..13b31ab30f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -10,6 +10,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" @@ -1823,7 +1824,7 @@ char *callback_to_string(Callback *cb, Arena *arena) snprintf(msg, msglen, "<vim partial: %s>", cb->data.partial->pt_name); break; default: - *msg = '\0'; + *msg = NUL; break; } return msg; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 39bd63462c..122a1ac8ab 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -15,6 +15,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" @@ -264,7 +265,7 @@ static void set_ufunc_name(ufunc_T *fp, char *name) if ((uint8_t)name[0] == K_SPECIAL) { fp->uf_name_exp = xmalloc(strlen(name) + 3); STRCPY(fp->uf_name_exp, "<SNR>"); - STRCAT(fp->uf_name_exp, fp->uf_name + 3); + strcat(fp->uf_name_exp, fp->uf_name + 3); } } @@ -2061,7 +2062,7 @@ char *get_scriptlocal_funcname(char *funcname) const int off = *funcname == 's' ? 2 : 5; char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1); STRCPY(newname, sid_buf); - STRCAT(newname, funcname + off); + strcat(newname, funcname + off); return newname; } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 1c15274acc..7b93a291c4 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -15,6 +15,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index 68de40f983..86495f1cb6 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -10,6 +10,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/cursor.h" +#include "nvim/errors.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -271,7 +272,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) // if count is not specified, default to 1 count = 1; } - if (endp != NULL && *endp != '\0') { + if (endp != NULL && *endp != NUL) { if (strequal(endp, "j")) { twin = win_vert_neighbor(tp, twin, false, count); } else if (strequal(endp, "k")) { diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index 9b7d8708be..41690ead88 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -6,7 +6,6 @@ #include <uv.h> #include "nvim/eval/typval_defs.h" -#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" enum { EVENT_HANDLER_MAX_ARGC = 10, }; @@ -55,14 +54,17 @@ struct wbuffer { }; typedef struct stream Stream; -/// Type of function called when the Stream buffer is filled with data +typedef struct rstream RStream; +/// Type of function called when the RStream buffer is filled with data /// /// @param stream The Stream instance -/// @param buf The associated RBuffer instance +/// @param read_data data that was read /// @param count Number of bytes that was read. /// @param data User-defined data /// @param eof If the stream reached EOF. -typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof); +/// @return number of bytes which were consumed +typedef size_t (*stream_read_cb)(RStream *stream, const char *read_data, size_t count, void *data, + bool eof); /// Type of function called when the Stream has information about a write /// request. @@ -71,11 +73,11 @@ typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, void /// @param data User-defined data /// @param status 0 on success, anything else indicates failure typedef void (*stream_write_cb)(Stream *stream, void *data, int status); + typedef void (*stream_close_cb)(Stream *stream, void *data); struct stream { bool closed; - bool did_eof; union { uv_pipe_t pipe; uv_tcp_t tcp; @@ -85,20 +87,32 @@ struct stream { #endif } uv; uv_stream_t *uvstream; - uv_buf_t uvbuf; - RBuffer *buffer; uv_file fd; - stream_read_cb read_cb; - stream_write_cb write_cb; void *cb_data; stream_close_cb close_cb, internal_close_cb; void *close_cb_data, *internal_data; - size_t fpos; + size_t pending_reqs; + MultiQueue *events; + + // only used for writing: + stream_write_cb write_cb; size_t curmem; size_t maxmem; - size_t pending_reqs; +}; + +struct rstream { + Stream s; + bool did_eof; + bool want_read; + bool pending_read; + bool paused_full; + char *buffer; // ARENA_BLOCK_SIZE + char *read_pos; + char *write_pos; + uv_buf_t uvbuf; + stream_read_cb read_cb; size_t num_bytes; - MultiQueue *events; + int64_t fpos; }; #define ADDRESS_MAX_SIZE 256 @@ -147,7 +161,8 @@ struct process { char **argv; const char *exepath; dict_T *env; - Stream in, out, err; + Stream in; + RStream out, err; /// Exit handler. If set, user must call process_free(). process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index f77d686c10..0dead1f9b4 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -70,19 +70,19 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvstdio[0].data.stream = (uv_stream_t *)(&proc->in.uv.pipe); } - if (!proc->out.closed) { + if (!proc->out.s.closed) { uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; #ifdef MSWIN // pipe must be readable for IOCP to work on Windows. uvproc->uvstdio[1].flags |= proc->overlapped ? (UV_READABLE_PIPE | UV_OVERLAPPED_PIPE) : 0; #endif - uvproc->uvstdio[1].data.stream = (uv_stream_t *)(&proc->out.uv.pipe); + uvproc->uvstdio[1].data.stream = (uv_stream_t *)(&proc->out.s.uv.pipe); } - if (!proc->err.closed) { + if (!proc->err.s.closed) { uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - uvproc->uvstdio[2].data.stream = (uv_stream_t *)(&proc->err.uv.pipe); + uvproc->uvstdio[2].data.stream = (uv_stream_t *)(&proc->err.s.uv.pipe); } else if (proc->fwd_err) { uvproc->uvstdio[2].flags = UV_INHERIT_FD; uvproc->uvstdio[2].data.fd = STDERR_FILENO; diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 7460e92766..70fc31ba21 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -8,7 +8,9 @@ #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" +#include "nvim/event/rstream.h" #include "nvim/event/stream.h" +#include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/main.h" @@ -16,7 +18,6 @@ #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" #include "nvim/os/time.h" -#include "nvim/rbuffer_defs.h" #include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -51,15 +52,15 @@ int process_spawn(Process *proc, bool in, bool out, bool err) } if (out) { - uv_pipe_init(&proc->loop->uv, &proc->out.uv.pipe, 0); + uv_pipe_init(&proc->loop->uv, &proc->out.s.uv.pipe, 0); } else { - proc->out.closed = true; + proc->out.s.closed = true; } if (err) { - uv_pipe_init(&proc->loop->uv, &proc->err.uv.pipe, 0); + uv_pipe_init(&proc->loop->uv, &proc->err.s.uv.pipe, 0); } else { - proc->err.closed = true; + proc->err.s.closed = true; } #ifdef USE_GCOV @@ -82,10 +83,10 @@ int process_spawn(Process *proc, bool in, bool out, bool err) uv_close((uv_handle_t *)&proc->in.uv.pipe, NULL); } if (out) { - uv_close((uv_handle_t *)&proc->out.uv.pipe, NULL); + uv_close((uv_handle_t *)&proc->out.s.uv.pipe, NULL); } if (err) { - uv_close((uv_handle_t *)&proc->err.uv.pipe, NULL); + uv_close((uv_handle_t *)&proc->err.s.uv.pipe, NULL); } if (proc->type == kProcessTypeUv) { @@ -106,16 +107,16 @@ int process_spawn(Process *proc, bool in, bool out, bool err) } if (out) { - stream_init(NULL, &proc->out, -1, (uv_stream_t *)&proc->out.uv.pipe); - proc->out.internal_data = proc; - proc->out.internal_close_cb = on_process_stream_close; + stream_init(NULL, &proc->out.s, -1, (uv_stream_t *)&proc->out.s.uv.pipe); + proc->out.s.internal_data = proc; + proc->out.s.internal_close_cb = on_process_stream_close; proc->refcount++; } if (err) { - stream_init(NULL, &proc->err, -1, (uv_stream_t *)&proc->err.uv.pipe); - proc->err.internal_data = proc; - proc->err.internal_close_cb = on_process_stream_close; + stream_init(NULL, &proc->err.s, -1, (uv_stream_t *)&proc->err.s.uv.pipe); + proc->err.s.internal_data = proc; + proc->err.s.internal_close_cb = on_process_stream_close; proc->refcount++; } @@ -148,9 +149,9 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL { - stream_may_close(&proc->in); - stream_may_close(&proc->out); - stream_may_close(&proc->err); + wstream_may_close(&proc->in); + rstream_may_close(&proc->out); + rstream_may_close(&proc->err); } /// Synchronously wait for a process to finish @@ -337,10 +338,10 @@ static void process_close(Process *proc) /// /// @param proc Process, for which an output stream should be flushed. /// @param stream Stream to flush. -static void flush_stream(Process *proc, Stream *stream) +static void flush_stream(Process *proc, RStream *stream) FUNC_ATTR_NONNULL_ARG(1) { - if (!stream || stream->closed) { + if (!stream || stream->s.closed) { return; } @@ -350,23 +351,23 @@ static void flush_stream(Process *proc, Stream *stream) // keeps sending data, we only accept as much data as the system buffer size. // Otherwise this would block cleanup/teardown. int system_buffer_size = 0; - int err = uv_recv_buffer_size((uv_handle_t *)&stream->uv.pipe, + int err = uv_recv_buffer_size((uv_handle_t *)&stream->s.uv.pipe, &system_buffer_size); if (err) { - system_buffer_size = (int)rbuffer_capacity(stream->buffer); + system_buffer_size = ARENA_BLOCK_SIZE; } size_t max_bytes = stream->num_bytes + (size_t)system_buffer_size; // Read remaining data. - while (!stream->closed && stream->num_bytes < max_bytes) { + while (!stream->s.closed && stream->num_bytes < max_bytes) { // Remember number of bytes before polling size_t num_bytes = stream->num_bytes; // Poll for data and process the generated events. loop_poll_events(proc->loop, 0); - if (stream->events) { - multiqueue_process_events(stream->events); + if (stream->s.events) { + multiqueue_process_events(stream->s.events); } // Stream can be closed if it is empty. @@ -374,7 +375,7 @@ static void flush_stream(Process *proc, Stream *stream) if (stream->read_cb && !stream->did_eof) { // Stream callback could miss EOF handling if a child keeps the stream // open. But only send EOF if we haven't already. - stream->read_cb(stream, stream->buffer, 0, stream->cb_data, true); + stream->read_cb(stream, stream->buffer, 0, stream->s.cb_data, true); } break; } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 421a470244..74b52cbbb1 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,8 +21,8 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data) .argv = NULL, .exepath = NULL, .in = { .closed = false }, - .out = { .closed = false }, - .err = { .closed = false }, + .out = { .s.closed = false }, + .err = { .s.closed = false }, .cb = NULL, .closed = false, .internal_close_cb = NULL, diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 6b4ab472e4..71290d0c0d 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -11,75 +11,81 @@ #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/os/os_defs.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/rstream.c.generated.h" #endif -void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize) +void rstream_init_fd(Loop *loop, RStream *stream, int fd) FUNC_ATTR_NONNULL_ARG(1, 2) { - stream_init(loop, stream, fd, NULL); - rstream_init(stream, bufsize); + stream_init(loop, &stream->s, fd, NULL); + rstream_init(stream); } -void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize) +void rstream_init_stream(RStream *stream, uv_stream_t *uvstream) FUNC_ATTR_NONNULL_ARG(1, 2) { - stream_init(NULL, stream, -1, uvstream); - rstream_init(stream, bufsize); + stream_init(NULL, &stream->s, -1, uvstream); + rstream_init(stream); } -void rstream_init(Stream *stream, size_t bufsize) +void rstream_init(RStream *stream) FUNC_ATTR_NONNULL_ARG(1) { - stream->buffer = rbuffer_new(bufsize); - stream->buffer->data = stream; - stream->buffer->full_cb = on_rbuffer_full; - stream->buffer->nonfull_cb = on_rbuffer_nonfull; + stream->fpos = 0; + stream->read_cb = NULL; + stream->num_bytes = 0; + stream->buffer = alloc_block(); + stream->read_pos = stream->write_pos = stream->buffer; +} + +void rstream_start_inner(RStream *stream) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (stream->s.uvstream) { + uv_read_start(stream->s.uvstream, alloc_cb, read_cb); + } else { + uv_idle_start(&stream->s.uv.idle, fread_idle_cb); + } } /// Starts watching for events from a `Stream` instance. /// /// @param stream The `Stream` instance -void rstream_start(Stream *stream, stream_read_cb cb, void *data) +void rstream_start(RStream *stream, stream_read_cb cb, void *data) FUNC_ATTR_NONNULL_ARG(1) { stream->read_cb = cb; - stream->cb_data = data; - if (stream->uvstream) { - uv_read_start(stream->uvstream, alloc_cb, read_cb); - } else { - uv_idle_start(&stream->uv.idle, fread_idle_cb); + stream->s.cb_data = data; + stream->want_read = true; + if (!stream->paused_full) { + rstream_start_inner(stream); } } /// Stops watching for events from a `Stream` instance. /// /// @param stream The `Stream` instance -void rstream_stop(Stream *stream) +void rstream_stop_inner(RStream *stream) FUNC_ATTR_NONNULL_ALL { - if (stream->uvstream) { - uv_read_stop(stream->uvstream); + if (stream->s.uvstream) { + uv_read_stop(stream->s.uvstream); } else { - uv_idle_stop(&stream->uv.idle); + uv_idle_stop(&stream->s.uv.idle); } } -static void on_rbuffer_full(RBuffer *buf, void *data) -{ - rstream_stop(data); -} - -static void on_rbuffer_nonfull(RBuffer *buf, void *data) +/// Stops watching for events from a `Stream` instance. +/// +/// @param stream The `Stream` instance +void rstream_stop(RStream *stream) + FUNC_ATTR_NONNULL_ALL { - Stream *stream = data; - assert(stream->read_cb); - rstream_start(stream, stream->read_cb, stream->cb_data); + rstream_stop_inner(stream); + stream->want_read = false; } // Callbacks used by libuv @@ -87,11 +93,10 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) /// Called by libuv to allocate memory for reading. static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { - Stream *stream = handle->data; - // `uv_buf_t.len` happens to have different size on Windows. - size_t write_count; - buf->base = rbuffer_write_ptr(stream->buffer, &write_count); - buf->len = UV_BUF_LEN(write_count); + RStream *stream = handle->data; + buf->base = stream->write_pos; + // `uv_buf_t.len` happens to have different size on Windows (as a treat) + buf->len = UV_BUF_LEN(rstream_space(stream)); } /// Callback invoked by libuv after it copies the data into the buffer provided @@ -99,27 +104,27 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) /// 0-length buffer. static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) { - Stream *stream = uvstream->data; + RStream *stream = uvstream->data; if (cnt <= 0) { // cnt == 0 means libuv asked for a buffer and decided it wasn't needed: // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start. // - // We don't need to do anything with the RBuffer because the next call + // We don't need to do anything with the buffer because the next call // to `alloc_cb` will return the same unused pointer (`rbuffer_produced` // won't be called) if (cnt == UV_ENOBUFS || cnt == 0) { return; } else if (cnt == UV_EOF && uvstream->type == UV_TTY) { // The TTY driver might signal EOF without closing the stream - invoke_read_cb(stream, 0, true); + invoke_read_cb(stream, true); } else { DLOG("closing Stream (%p): %s (%s)", (void *)stream, uv_err_name((int)cnt), os_strerror((int)cnt)); // Read error or EOF, either way stop the stream and invoke the callback // with eof == true uv_read_stop(uvstream); - invoke_read_cb(stream, 0, true); + invoke_read_cb(stream, true); } return; } @@ -127,10 +132,13 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) // at this point we're sure that cnt is positive, no error occurred size_t nread = (size_t)cnt; stream->num_bytes += nread; - // Data was already written, so all we need is to update 'wpos' to reflect - // the space actually used in the buffer. - rbuffer_produced(stream->buffer, nread); - invoke_read_cb(stream, nread, false); + stream->write_pos += cnt; + invoke_read_cb(stream, false); +} + +static size_t rstream_space(RStream *stream) +{ + return (size_t)((stream->buffer + ARENA_BLOCK_SIZE) - stream->write_pos); } /// Called by the by the 'idle' handle to emulate a reading event @@ -141,66 +149,91 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) static void fread_idle_cb(uv_idle_t *handle) { uv_fs_t req; - Stream *stream = handle->data; + RStream *stream = handle->data; + stream->uvbuf.base = stream->write_pos; // `uv_buf_t.len` happens to have different size on Windows. - size_t write_count; - stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); - stream->uvbuf.len = UV_BUF_LEN(write_count); - - // the offset argument to uv_fs_read is int64_t, could someone really try - // to read more than 9 quintillion (9e18) bytes? - // upcast is meant to avoid tautological condition warning on 32 bits - uintmax_t fpos_intmax = stream->fpos; - if (fpos_intmax > INT64_MAX) { - ELOG("stream offset overflow"); - preserve_exit("stream offset overflow"); - } + stream->uvbuf.len = UV_BUF_LEN(rstream_space(stream)); // Synchronous read - uv_fs_read(handle->loop, - &req, - stream->fd, - &stream->uvbuf, - 1, - (int64_t)stream->fpos, - NULL); + uv_fs_read(handle->loop, &req, stream->s.fd, &stream->uvbuf, 1, stream->fpos, NULL); uv_fs_req_cleanup(&req); if (req.result <= 0) { - uv_idle_stop(&stream->uv.idle); - invoke_read_cb(stream, 0, true); + uv_idle_stop(&stream->s.uv.idle); + invoke_read_cb(stream, true); return; } - // no errors (req.result (ssize_t) is positive), it's safe to cast. - size_t nread = (size_t)req.result; - rbuffer_produced(stream->buffer, nread); - stream->fpos += nread; - invoke_read_cb(stream, nread, false); + // no errors (req.result (ssize_t) is positive), it's safe to use. + stream->write_pos += req.result; + stream->fpos += req.result; + invoke_read_cb(stream, false); } static void read_event(void **argv) { - Stream *stream = argv[0]; + RStream *stream = argv[0]; + stream->pending_read = false; if (stream->read_cb) { - size_t count = (uintptr_t)argv[1]; - bool eof = (uintptr_t)argv[2]; - stream->did_eof = eof; - stream->read_cb(stream, stream->buffer, count, stream->cb_data, eof); + size_t available = rstream_available(stream); + size_t consumed = stream->read_cb(stream, stream->read_pos, available, stream->s.cb_data, + stream->did_eof); + assert(consumed <= available); + rstream_consume(stream, consumed); + } + stream->s.pending_reqs--; + if (stream->s.closed && !stream->s.pending_reqs) { + stream_close_handle(&stream->s, true); + } +} + +size_t rstream_available(RStream *stream) +{ + return (size_t)(stream->write_pos - stream->read_pos); +} + +void rstream_consume(RStream *stream, size_t consumed) +{ + stream->read_pos += consumed; + size_t remaining = (size_t)(stream->write_pos - stream->read_pos); + if (remaining > 0 && stream->read_pos > stream->buffer) { + memmove(stream->buffer, stream->read_pos, remaining); + stream->read_pos = stream->buffer; + stream->write_pos = stream->buffer + remaining; + } else if (remaining == 0) { + stream->read_pos = stream->write_pos = stream->buffer; } - stream->pending_reqs--; - if (stream->closed && !stream->pending_reqs) { - stream_close_handle(stream); + + if (stream->want_read && stream->paused_full && rstream_space(stream)) { + assert(stream->read_cb); + stream->paused_full = false; + rstream_start_inner(stream); } } -static void invoke_read_cb(Stream *stream, size_t count, bool eof) +static void invoke_read_cb(RStream *stream, bool eof) { + stream->did_eof |= eof; + + if (!rstream_space(stream)) { + rstream_stop_inner(stream); + stream->paused_full = true; + } + + // we cannot use pending_reqs as a socket can have both pending reads and writes + if (stream->pending_read) { + return; + } + // Don't let the stream be closed before the event is processed. - stream->pending_reqs++; + stream->s.pending_reqs++; + stream->pending_read = true; + CREATE_EVENT(stream->s.events, read_event, stream); +} - CREATE_EVENT(stream->events, read_event, - stream, (void *)(uintptr_t *)count, (void *)(uintptr_t)eof); +void rstream_may_close(RStream *stream) +{ + stream_may_close(&stream->s, true); } diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index 4e878a2ecf..1214c3e336 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -35,7 +35,7 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint if (host_end && addr != host_end) { // Split user specified address into two strings, addr(hostname) and port. // The port part in watcher->addr will be updated later. - *host_end = '\0'; + *host_end = NUL; char *port = host_end + 1; intmax_t iport; @@ -135,17 +135,17 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) return 0; } -int socket_watcher_accept(SocketWatcher *watcher, Stream *stream) +int socket_watcher_accept(SocketWatcher *watcher, RStream *stream) FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) { uv_stream_t *client; if (watcher->stream->type == UV_TCP) { - client = (uv_stream_t *)(&stream->uv.tcp); + client = (uv_stream_t *)(&stream->s.uv.tcp); uv_tcp_init(watcher->uv.tcp.handle.loop, (uv_tcp_t *)client); uv_tcp_nodelay((uv_tcp_t *)client, true); } else { - client = (uv_stream_t *)&stream->uv.pipe; + client = (uv_stream_t *)&stream->s.uv.pipe; uv_pipe_init(watcher->uv.pipe.handle.loop, (uv_pipe_t *)client, 0); } @@ -156,7 +156,7 @@ int socket_watcher_accept(SocketWatcher *watcher, Stream *stream) return result; } - stream_init(NULL, stream, -1, client); + stream_init(NULL, &stream->s, -1, client); return 0; } @@ -197,7 +197,7 @@ static void connect_cb(uv_connect_t *req, int status) } } -bool socket_connect(Loop *loop, Stream *stream, bool is_tcp, const char *address, int timeout, +bool socket_connect(Loop *loop, RStream *stream, bool is_tcp, const char *address, int timeout, const char **error) { bool success = false; @@ -206,7 +206,7 @@ bool socket_connect(Loop *loop, Stream *stream, bool is_tcp, const char *address req.data = &status; uv_stream_t *uv_stream; - uv_tcp_t *tcp = &stream->uv.tcp; + uv_tcp_t *tcp = &stream->s.uv.tcp; uv_getaddrinfo_t addr_req; addr_req.addrinfo = NULL; const struct addrinfo *addrinfo = NULL; @@ -237,7 +237,7 @@ tcp_retry: uv_tcp_connect(&req, tcp, addrinfo->ai_addr, connect_cb); uv_stream = (uv_stream_t *)tcp; } else { - uv_pipe_t *pipe = &stream->uv.pipe; + uv_pipe_t *pipe = &stream->s.uv.pipe; uv_pipe_init(&loop->uv, pipe, 0); uv_pipe_connect(&req, pipe, address, connect_cb); uv_stream = (uv_stream_t *)pipe; @@ -245,7 +245,7 @@ tcp_retry: status = 1; LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1); if (status == 0) { - stream_init(NULL, stream, -1, uv_stream); + stream_init(NULL, &stream->s, -1, uv_stream); success = true; } else if (is_tcp && addrinfo->ai_next) { addrinfo = addrinfo->ai_next; diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 0b9ed4f25b..bc1b503f4c 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -8,7 +8,6 @@ #include "nvim/event/loop.h" #include "nvim/event/stream.h" #include "nvim/log.h" -#include "nvim/rbuffer.h" #include "nvim/types_defs.h" #ifdef MSWIN # include "nvim/os/os_win_console.h" @@ -85,21 +84,17 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream) } stream->internal_data = NULL; - stream->fpos = 0; stream->curmem = 0; stream->maxmem = 0; stream->pending_reqs = 0; - stream->read_cb = NULL; stream->write_cb = NULL; stream->close_cb = NULL; stream->internal_close_cb = NULL; stream->closed = false; - stream->buffer = NULL; stream->events = NULL; - stream->num_bytes = 0; } -void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) +void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data, bool rstream) FUNC_ATTR_NONNULL_ARG(1) { assert(!stream->closed); @@ -116,18 +111,18 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) #endif if (!stream->pending_reqs) { - stream_close_handle(stream); + stream_close_handle(stream, rstream); } } -void stream_may_close(Stream *stream) +void stream_may_close(Stream *stream, bool rstream) { if (!stream->closed) { - stream_close(stream, NULL, NULL); + stream_close(stream, NULL, NULL, rstream); } } -void stream_close_handle(Stream *stream) +void stream_close_handle(Stream *stream, bool rstream) FUNC_ATTR_NONNULL_ALL { uv_handle_t *handle = NULL; @@ -145,16 +140,22 @@ void stream_close_handle(Stream *stream) assert(handle != NULL); if (!uv_is_closing(handle)) { - uv_close(handle, close_cb); + uv_close(handle, rstream ? rstream_close_cb : close_cb); } } -static void close_cb(uv_handle_t *handle) +static void rstream_close_cb(uv_handle_t *handle) { - Stream *stream = handle->data; + RStream *stream = handle->data; if (stream->buffer) { - rbuffer_free(stream->buffer); + free_block(stream->buffer); } + close_cb(handle); +} + +static void close_cb(uv_handle_t *handle) +{ + Stream *stream = handle->data; if (stream->close_cb) { stream->close_cb(stream, stream->close_cb_data); } diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index c67a9b96ed..07aab87e4d 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -141,7 +141,7 @@ static void write_cb(uv_write_t *req, int status) if (data->stream->closed && data->stream->pending_reqs == 0) { // Last pending write, free the stream; - stream_close_handle(data->stream); + stream_close_handle(data->stream, false); } xfree(data); @@ -158,3 +158,8 @@ void wstream_release_wbuffer(WBuffer *buffer) xfree(buffer); } } + +void wstream_may_close(Stream *stream) +{ + stream_may_close(stream, false); +} diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 834cc6698a..7858e44f17 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -34,6 +34,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -969,13 +970,13 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out char *t = xmalloc(len); *t = NUL; if (newcmd != NULL) { - STRCAT(t, newcmd); + strcat(t, newcmd); } if (ins_prevcmd) { - STRCAT(t, prevcmd); + strcat(t, prevcmd); } char *p = t + strlen(t); - STRCAT(t, trailarg); + strcat(t, trailarg); xfree(newcmd); newcmd = t; @@ -1028,8 +1029,8 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out } newcmd = xmalloc(strlen(prevcmd) + 2 * strlen(p_shq) + 1); STRCPY(newcmd, p_shq); - STRCAT(newcmd, prevcmd); - STRCAT(newcmd, p_shq); + strcat(newcmd, prevcmd); + strcat(newcmd, p_shq); free_newcmd = true; } if (addr_count == 0) { // :! @@ -1359,11 +1360,11 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp) { bool is_fish_shell = #if defined(UNIX) - strncmp(invocation_path_tail(p_sh, NULL), "fish", 4) == 0; + strncmp(invocation_path_tail(p_sh, NULL), S_LEN("fish")) == 0; #else false; #endif - bool is_pwsh = strncmp(invocation_path_tail(p_sh, NULL), "pwsh", 4) == 0 + bool is_pwsh = strncmp(invocation_path_tail(p_sh, NULL), S_LEN("pwsh")) == 0 || strncmp(invocation_path_tail(p_sh, NULL), "powershell", 10) == 0; @@ -4107,7 +4108,7 @@ skip: // the line as reference, because the substitute may // have changed the number of characters. Same for // "prev_matchcol". - STRCAT(new_start, sub_firstline + copycol); + strcat(new_start, sub_firstline + copycol); matchcol = (colnr_T)strlen(sub_firstline) - matchcol; prev_matchcol = (colnr_T)strlen(sub_firstline) - prev_matchcol; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index f4a6e61831..a602719f6d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -17,6 +17,7 @@ #include "nvim/bufwrite.h" #include "nvim/change.h" #include "nvim/channel.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1fcfc505df..9a6a845958 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -29,6 +29,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -1728,12 +1729,6 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) } const char *errormsg = NULL; -#undef ERROR -#define ERROR(msg) \ - do { \ - errormsg = msg; \ - goto end; \ - } while (0) cmdmod_T save_cmdmod = cmdmod; cmdmod = cmdinfo->cmdmod; @@ -1744,16 +1739,19 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY) // allow :put in terminals && !(curbuf->terminal && eap->cmdidx == CMD_put)) { - ERROR(_(e_modifiable)); + errormsg = _(e_modifiable); + goto end; } if (!IS_USER_CMDIDX(eap->cmdidx)) { if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) { // Command not allowed in the command line window - ERROR(_(e_cmdwin)); + errormsg = _(e_cmdwin); + goto end; } if (text_locked() && !(eap->argt & EX_LOCK_OK)) { // Command not allowed when text is locked - ERROR(_(get_text_locked_msg())); + errormsg = _(get_text_locked_msg()); + goto end; } } // Disallow editing another buffer when "curbuf->b_ro_locked" is set. @@ -1801,7 +1799,6 @@ end: do_cmdline_end(); return retv; -#undef ERROR } static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetline, void *cookie) @@ -2695,7 +2692,7 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod, /// Apply the command modifiers. Saves current state in "cmdmod", call /// undo_cmdmod() later. -static void apply_cmdmod(cmdmod_T *cmod) +void apply_cmdmod(cmdmod_T *cmod) { if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox) { sandbox++; @@ -3829,8 +3826,8 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) // No $* in arg, build "<makeprg> <arg>" instead new_cmdline = xmalloc(strlen(program) + strlen(arg) + 2); STRCPY(new_cmdline, program); - STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, arg); + strcat(new_cmdline, " "); + strcat(new_cmdline, arg); } msg_make(arg); @@ -4117,7 +4114,7 @@ static char *getargcmd(char **argp) if (*arg == '+') { // +[command] arg++; - if (ascii_isspace(*arg) || *arg == '\0') { + if (ascii_isspace(*arg) || *arg == NUL) { command = dollar_command; } else { command = arg; @@ -4194,7 +4191,7 @@ static int getargopt(exarg_T *eap) // Note: Keep this in sync with get_argopt_name. // ":edit ++[no]bin[ary] file" - if (strncmp(arg, "bin", 3) == 0 || strncmp(arg, "nobin", 5) == 0) { + if (strncmp(arg, S_LEN("bin")) == 0 || strncmp(arg, S_LEN("nobin")) == 0) { if (*arg == 'n') { arg += 2; eap->force_bin = FORCE_NOBIN; @@ -4209,33 +4206,33 @@ static int getargopt(exarg_T *eap) } // ":read ++edit file" - if (strncmp(arg, "edit", 4) == 0) { + if (strncmp(arg, S_LEN("edit")) == 0) { eap->read_edit = true; eap->arg = skipwhite(arg + 4); return OK; } // ":write ++p foo/bar/file - if (strncmp(arg, "p", 1) == 0) { + if (strncmp(arg, S_LEN("p")) == 0) { eap->mkdir_p = true; eap->arg = skipwhite(arg + 1); return OK; } - if (strncmp(arg, "ff", 2) == 0) { + if (strncmp(arg, S_LEN("ff")) == 0) { arg += 2; pp = &eap->force_ff; - } else if (strncmp(arg, "fileformat", 10) == 0) { + } else if (strncmp(arg, S_LEN("fileformat")) == 0) { arg += 10; pp = &eap->force_ff; - } else if (strncmp(arg, "enc", 3) == 0) { - if (strncmp(arg, "encoding", 8) == 0) { + } else if (strncmp(arg, S_LEN("enc")) == 0) { + if (strncmp(arg, S_LEN("encoding")) == 0) { arg += 8; } else { arg += 3; } pp = &eap->force_enc; - } else if (strncmp(arg, "bad", 3) == 0) { + } else if (strncmp(arg, S_LEN("bad")) == 0) { arg += 3; pp = &bad_char_idx; } @@ -4299,19 +4296,19 @@ int expand_argopt(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches, int char *name_end = xp->xp_pattern - 1; if (name_end - xp->xp_line >= 2 - && strncmp(name_end - 2, "ff", 2) == 0) { + && strncmp(name_end - 2, S_LEN("ff")) == 0) { cb = get_fileformat_name; } else if (name_end - xp->xp_line >= 10 - && strncmp(name_end - 10, "fileformat", 10) == 0) { + && strncmp(name_end - 10, S_LEN("fileformat")) == 0) { cb = get_fileformat_name; } else if (name_end - xp->xp_line >= 3 - && strncmp(name_end - 3, "enc", 3) == 0) { + && strncmp(name_end - 3, S_LEN("enc")) == 0) { cb = get_encoding_name; } else if (name_end - xp->xp_line >= 8 - && strncmp(name_end - 8, "encoding", 8) == 0) { + && strncmp(name_end - 8, S_LEN("encoding")) == 0) { cb = get_encoding_name; } else if (name_end - xp->xp_line >= 3 - && strncmp(name_end - 3, "bad", 3) == 0) { + && strncmp(name_end - 3, S_LEN("bad")) == 0) { cb = get_bad_name; } @@ -7216,7 +7213,7 @@ char *expand_sfile(char *arg) char *result = xstrdup(arg); for (char *p = result; *p;) { - if (strncmp(p, "<sfile>", 7) != 0) { + if (strncmp(p, S_LEN("<sfile>")) != 0) { p++; } else { // replace "<sfile>" with the sourced file name, and do ":" stuff @@ -7239,7 +7236,7 @@ char *expand_sfile(char *arg) memmove(newres, result, (size_t)(p - result)); STRCPY(newres + (p - result), repl); len = strlen(newres); - STRCAT(newres, p + srclen); + strcat(newres, p + srclen); xfree(repl); xfree(result); result = newres; @@ -7303,12 +7300,12 @@ static void ex_filetype(exarg_T *eap) // Accept "plugin" and "indent" in any order. while (true) { - if (strncmp(arg, "plugin", 6) == 0) { + if (strncmp(arg, S_LEN("plugin")) == 0) { plugin = true; arg = skipwhite(arg + 6); continue; } - if (strncmp(arg, "indent", 6) == 0) { + if (strncmp(arg, S_LEN("indent")) == 0) { indent = true; arg = skipwhite(arg + 6); continue; @@ -7387,7 +7384,7 @@ static void ex_setfiletype(exarg_T *eap) } char *arg = eap->arg; - if (strncmp(arg, "FALLBACK ", 9) == 0) { + if (strncmp(arg, S_LEN("FALLBACK ")) == 0) { arg += 9; } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 472741d537..0f4f31df02 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -11,6 +11,7 @@ #include "nvim/ascii_defs.h" #include "nvim/charset.h" #include "nvim/debugger.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -253,7 +254,7 @@ bool cause_errthrow(const char *mesg, bool multiline, bool severe, bool *ignore) if (plist == msg_list || severe) { // Skip the extra "Vim " prefix for message "E458". char *tmsg = elem->msg; - if (strncmp(tmsg, "Vim E", 5) == 0 + if (strncmp(tmsg, S_LEN("Vim E")) == 0 && ascii_isdigit(tmsg[5]) && ascii_isdigit(tmsg[6]) && ascii_isdigit(tmsg[7]) @@ -405,7 +406,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, bool || (ascii_isdigit(p[3]) && p[4] == ':')))))) { if (*p == NUL || p == mesg) { - STRCAT(val, mesg); // 'E123' missing or at beginning + strcat(val, mesg); // 'E123' missing or at beginning } else { // '"filename" E123: message text' if (mesg[0] != '"' || p - 2 < &mesg[1] @@ -414,7 +415,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, bool continue; } - STRCAT(val, p); + strcat(val, p); p[-2] = NUL; snprintf(val + strlen(p), strlen(" (%s)"), " (%s)", &mesg[1]); p[-2] = '"'; @@ -442,7 +443,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname) // would be treated differently from real interrupt or error exceptions // when no active try block is found, see do_cmdline(). if (type == ET_USER) { - if (strncmp(value, "Vim", 3) == 0 + if (strncmp(value, S_LEN("Vim")) == 0 && (((char *)value)[3] == NUL || ((char *)value)[3] == ':' || ((char *)value)[3] == '(')) { emsg(_("E608: Cannot :throw exceptions with 'Vim' prefix")); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index cc2608433d..74e6e3422e 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -28,6 +28,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" @@ -2798,13 +2799,13 @@ int check_opt_wim(void) if (p[i] != NUL && p[i] != ',' && p[i] != ':') { return FAIL; } - if (i == 7 && strncmp(p, "longest", 7) == 0) { + if (i == 7 && strncmp(p, S_LEN("longest")) == 0) { new_wim_flags[idx] |= WIM_LONGEST; - } else if (i == 4 && strncmp(p, "full", 4) == 0) { + } else if (i == 4 && strncmp(p, S_LEN("full")) == 0) { new_wim_flags[idx] |= WIM_FULL; - } else if (i == 4 && strncmp(p, "list", 4) == 0) { + } else if (i == 4 && strncmp(p, S_LEN("list")) == 0) { new_wim_flags[idx] |= WIM_LIST; - } else if (i == 8 && strncmp(p, "lastused", 8) == 0) { + } else if (i == 8 && strncmp(p, S_LEN("lastused")) == 0) { new_wim_flags[idx] |= WIM_BUFLASTUSED; } else { return FAIL; @@ -3454,11 +3455,9 @@ void cmdline_screen_cleared(void) /// called by ui_flush, do what redraws necessary to keep cmdline updated. void cmdline_ui_flush(void) { - static bool flushing = false; - if (!ui_has(kUICmdline) || flushing) { + if (!ui_has(kUICmdline)) { return; } - flushing = true; int level = ccline.level; CmdlineInfo *line = &ccline; while (level > 0 && line) { @@ -3473,7 +3472,6 @@ void cmdline_ui_flush(void) } line = line->prev_ccline; } - flushing = false; } // Put a character on the command line. Shifts the following text to the diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 0e5d2fe4f5..50ee197ef4 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -15,6 +15,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 4e47fa76fe..b592283d92 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -54,12 +54,12 @@ /// must not be used during iteration! void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col, int end_row, colnr_T end_col, DecorInline decor, uint16_t decor_flags, bool right_gravity, - bool end_right_gravity, bool no_undo, bool invalidate, bool scoped, Error *err) + bool end_right_gravity, bool no_undo, bool invalidate, Error *err) { uint32_t *ns = map_put_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL, NULL); uint32_t id = idp ? *idp : 0; - uint16_t flags = mt_flags(right_gravity, no_undo, invalidate, decor.ext, scoped) | decor_flags; + uint16_t flags = mt_flags(right_gravity, no_undo, invalidate, decor.ext) | decor_flags; if (id == 0) { id = ++*ns; } else { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index a8b0dbddee..50031eedee 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -399,7 +399,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i emsg(_(e_path_too_long_for_completion)); break; } - if (strncmp(wc_part, "**", 2) == 0) { + if (strncmp(wc_part, S_LEN("**")) == 0) { ff_expand_buffer[len++] = *wc_part++; ff_expand_buffer[len++] = *wc_part++; @@ -451,7 +451,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i STRCPY(buf, ff_expand_buffer); STRCPY(buf + eb_len, search_ctx->ffsc_fix_path); if (os_isdir(buf)) { - STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path); + strcat(ff_expand_buffer, search_ctx->ffsc_fix_path); add_pathsep(ff_expand_buffer); } else { char *p = path_tail(search_ctx->ffsc_fix_path); @@ -462,7 +462,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i if (p > search_ctx->ffsc_fix_path) { // do not add '..' to the path and start upwards searching len = (int)(p - search_ctx->ffsc_fix_path) - 1; - if ((len >= 2 && strncmp(search_ctx->ffsc_fix_path, "..", 2) == 0) + if ((len >= 2 && strncmp(search_ctx->ffsc_fix_path, S_LEN("..")) == 0) && (len == 2 || search_ctx->ffsc_fix_path[2] == PATHSEP)) { xfree(buf); goto error_return; @@ -479,7 +479,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i + strlen(search_ctx->ffsc_fix_path + len) + 1); STRCPY(temp, search_ctx->ffsc_fix_path + len); - STRCAT(temp, search_ctx->ffsc_wc_path); + strcat(temp, search_ctx->ffsc_wc_path); xfree(search_ctx->ffsc_wc_path); xfree(wc_path); search_ctx->ffsc_wc_path = temp; @@ -505,24 +505,36 @@ error_return: /// @return the stopdir string. Check that ';' is not escaped. char *vim_findfile_stopdir(char *buf) { - char *r_ptr = buf; - - while (*r_ptr != NUL && *r_ptr != ';') { - if (r_ptr[0] == '\\' && r_ptr[1] == ';') { - // Overwrite the escape char, - // use strlen(r_ptr) to move the trailing '\0'. - STRMOVE(r_ptr, r_ptr + 1); - r_ptr++; + for (; *buf != NUL && *buf != ';' && (buf[0] != '\\' || buf[1] != ';'); buf++) {} + char *dst = buf; + if (*buf == ';') { + goto is_semicolon; + } + if (*buf == NUL) { + goto is_nul; + } + goto start; + while (*buf != NUL && *buf != ';') { + if (buf[0] == '\\' && buf[1] == ';') { +start: + // Overwrite the escape char. + *dst++ = ';'; + buf += 2; + } else { + *dst++ = *buf++; } - r_ptr++; } - if (*r_ptr == ';') { - *r_ptr = 0; - r_ptr++; - } else if (*r_ptr == NUL) { - r_ptr = NULL; + assert(dst < buf); + *dst = NUL; + if (*buf == ';') { +is_semicolon: + *buf = NUL; + buf++; + } else { // if (*buf == NUL) +is_nul: + buf = NULL; } - return r_ptr; + return buf; } /// Clean up the given search context. Can handle a NULL pointer. @@ -669,7 +681,7 @@ char *vim_findfile(void *search_ctx_arg) ff_free_stack_element(stackp); goto fail; } - STRCAT(file_path, stackp->ffs_fix_path); + strcat(file_path, stackp->ffs_fix_path); if (!add_pathsep(file_path)) { ff_free_stack_element(stackp); goto fail; @@ -678,7 +690,7 @@ char *vim_findfile(void *search_ctx_arg) rest_of_wildcards = stackp->ffs_wc_path; if (*rest_of_wildcards != NUL) { len = strlen(file_path); - if (strncmp(rest_of_wildcards, "**", 2) == 0) { + if (strncmp(rest_of_wildcards, S_LEN("**")) == 0) { // pointer to the restrict byte // The restrict byte is not a character! p = rest_of_wildcards + 2; @@ -769,7 +781,7 @@ char *vim_findfile(void *search_ctx_arg) ff_free_stack_element(stackp); goto fail; } - STRCAT(file_path, search_ctx->ffsc_file_to_search); + strcat(file_path, search_ctx->ffsc_file_to_search); // Try without extra suffix and then with suffixes // from 'suffixesadd'. @@ -855,7 +867,7 @@ char *vim_findfile(void *search_ctx_arg) // if wildcards contains '**' we have to descent till we reach the // leaves of the directory tree. - if (strncmp(stackp->ffs_wc_path, "**", 2) == 0) { + if (strncmp(stackp->ffs_wc_path, S_LEN("**")) == 0) { for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) { if (path_fnamecmp(stackp->ffs_filearray[i], @@ -910,7 +922,7 @@ char *vim_findfile(void *search_ctx_arg) if (!add_pathsep(file_path)) { goto fail; } - STRCAT(file_path, search_ctx->ffsc_fix_path); + strcat(file_path, search_ctx->ffsc_fix_path); // create a new stack entry sptr = ff_create_stack_element(file_path, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index df9c4928c9..4b8121b423 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -27,6 +27,7 @@ #include "nvim/diff.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval.h" @@ -1895,7 +1896,7 @@ theend: bool is_dev_fd_file(char *fname) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - return strncmp(fname, "/dev/fd/", 8) == 0 + return strncmp(fname, S_LEN("/dev/fd/")) == 0 && ascii_isdigit((uint8_t)fname[8]) && *skipdigits(fname + 9) == NUL && (fname[9] != NUL @@ -2418,7 +2419,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) // the file name has at most BASENAMELEN characters. if (strlen(ptr) > BASENAMELEN) { - ptr[BASENAMELEN] = '\0'; + ptr[BASENAMELEN] = NUL; } char *s = ptr + strlen(ptr); @@ -3329,7 +3330,7 @@ static void vim_mktempdir(void) #endif // If our "root" tempdir is invalid or fails, proceed without "<user>/". // Else user1 could break user2 by creating "/tmp/nvim.user2/". - tmp[strlen(tmp) - strlen(user)] = '\0'; + tmp[strlen(tmp) - strlen(user)] = NUL; } // Now try to create "/tmp/nvim.<user>/XXXXXX". diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 59a4dc6aad..78fe33cc78 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -22,6 +22,7 @@ #include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_session.h" @@ -3314,7 +3315,7 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *r = xmalloc(len); snprintf(r, len, txt, dashes, count); len = strlen(r); - STRCAT(r, s); + strcat(r, s); // remove 'foldmarker' and 'commentstring' foldtext_cleanup(r + len); rettv->vval.v_string = r; diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 62c99ce082..61c80a3d2e 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -306,6 +306,7 @@ local keysets_defs = assert(io.open(keysets_outputf, 'wb')) -- so that the dispatcher can find the C functions that you are creating! -- =========================================================================== output:write([[ +#include "nvim/errors.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/globals.h" diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 516b5ad5ae..c5b37672bf 100644 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -128,8 +128,16 @@ for i = 1, #events do write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then + -- Lua callbacks may emit other events or the same event again. Avoid the latter + -- by adding a recursion guard to each generated function that may call a Lua callback. + call_output:write(' static bool entered = false;\n') + call_output:write(' if (entered) {\n') + call_output:write(' return;\n') + 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') 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 0718d965c6..591a6b93df 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -148,7 +148,7 @@ local get_defaults = function(d, n) return value_dumpers[type(d)](d) end ---- @type {[1]:string,[2]:string}[] +--- @type [string,string][] local defines = {} --- @param i integer diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9b19644b7e..6167418052 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -19,6 +19,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -565,7 +566,7 @@ void AppendToRedobuffLit(const char *str, int len) // CTRL-V '0' must be inserted as CTRL-V 048. if (*s == NUL && c == '0') { - add_buff(&redobuff, "048", 3); + add_buff(&redobuff, S_LEN("048")); } else { add_char_buff(&redobuff, c); } @@ -776,7 +777,7 @@ int start_redo(int count, bool old_redo) // copy the buffer name, if present if (c == '"') { - add_buff(&readbuf2, "\"", 1); + add_buff(&readbuf2, S_LEN("\"")); c = read_redo(false, old_redo); // if a numbered buffer is used, increment the number diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 83fef1fe75..410e8f2e7c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -793,195 +793,7 @@ EXTERN disptick_T display_tick INIT( = 0); // cursor position in Insert mode. EXTERN linenr_T spell_redraw_lnum INIT( = 0); -// uncrustify:off - -// The error messages that can be shared are included here. -// Excluded are errors that are only used once and debugging messages. -EXTERN const char e_abort[] INIT(= N_("E470: Command aborted")); -EXTERN const char e_afterinit[] INIT(= N_("E905: Cannot set this option after startup")); -EXTERN const char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job")); -EXTERN const char e_argreq[] INIT(= N_("E471: Argument required")); -EXTERN const char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); -EXTERN const char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits")); -EXTERN const char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search")); -EXTERN const char e_invalid_buffer_name_str[] INIT(= N_("E158: Invalid buffer name: %s")); -EXTERN const char e_command_too_recursive[] INIT(= N_("E169: Command too recursive")); -EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded")); -EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif")); -EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry")); -EXTERN const char e_endwhile[] INIT(= N_("E170: Missing :endwhile")); -EXTERN const char e_endfor[] INIT(= N_("E170: Missing :endfor")); -EXTERN const char e_while[] INIT(= N_("E588: :endwhile without :while")); -EXTERN const char e_for[] INIT(= N_("E588: :endfor without :for")); -EXTERN const char e_exists[] INIT(= N_("E13: File exists (add ! to override)")); -EXTERN const char e_failed[] INIT(= N_("E472: Command failed")); -EXTERN const char e_internal[] INIT(= N_("E473: Internal error")); -EXTERN const char e_intern2[] INIT(= N_("E685: Internal error: %s")); -EXTERN const char e_interr[] INIT(= N_("Interrupted")); -EXTERN const char e_invarg[] INIT(= N_("E474: Invalid argument")); -EXTERN const char e_invarg2[] INIT(= N_("E475: Invalid argument: %s")); -EXTERN const char e_invargval[] INIT(= N_("E475: Invalid value for argument %s")); -EXTERN const char e_invargNval[] INIT(= N_("E475: Invalid value for argument %s: %s")); -EXTERN const char e_duparg2[] INIT(= N_("E983: Duplicate argument: %s")); -EXTERN const char e_invexpr2[] INIT(= N_("E15: Invalid expression: \"%s\"")); -EXTERN const char e_invrange[] INIT(= N_("E16: Invalid range")); -EXTERN const char e_invcmd[] INIT(= N_("E476: Invalid command")); -EXTERN const char e_isadir2[] INIT(= N_("E17: \"%s\" is a directory")); -EXTERN const char e_no_spell[] INIT(= N_("E756: Spell checking is not possible")); -EXTERN const char e_invchan[] INIT(= N_("E900: Invalid channel id")); -EXTERN const char e_invchanjob[] INIT(= N_("E900: Invalid channel id: not a job")); -EXTERN const char e_jobtblfull[] INIT(= N_("E901: Job table is full")); -EXTERN const char e_jobspawn[] INIT(= N_("E903: Process failed to start: %s: \"%s\"")); -EXTERN const char e_channotpty[] INIT(= N_("E904: channel is not a pty")); -EXTERN const char e_stdiochan2[] INIT(= N_("E905: Couldn't open stdio channel: %s")); -EXTERN const char e_invstream[] INIT(= N_("E906: invalid stream for channel")); -EXTERN const char e_invstreamrpc[] INIT(= N_("E906: invalid stream for rpc channel, use 'rpc'")); -EXTERN const char e_streamkey[] INIT(= N_("E5210: dict key '%s' already set for buffered stream in channel %" PRIu64)); -EXTERN const char e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\"")); -EXTERN const char e_fsync[] INIT(= N_("E667: Fsync failed: %s")); -EXTERN const char e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s")); -EXTERN const char e_markinval[] INIT(= N_("E19: Mark has invalid line number")); -EXTERN const char e_marknotset[] INIT(= N_("E20: Mark not set")); -EXTERN const char e_modifiable[] INIT(= N_("E21: Cannot make changes, 'modifiable' is off")); -EXTERN const char e_nesting[] INIT(= N_("E22: Scripts nested too deep")); -EXTERN const char e_noalt[] INIT(= N_("E23: No alternate file")); -EXTERN const char e_noabbr[] INIT(= N_("E24: No such abbreviation")); -EXTERN const char e_nobang[] INIT(= N_("E477: No ! allowed")); -EXTERN const char e_nogroup[] INIT(= N_("E28: No such highlight group name: %s")); -EXTERN const char e_noinstext[] INIT(= N_("E29: No inserted text yet")); -EXTERN const char e_nolastcmd[] INIT(= N_("E30: No previous command line")); -EXTERN const char e_nomap[] INIT(= N_("E31: No such mapping")); -EXTERN const char e_nomatch[] INIT(= N_("E479: No match")); -EXTERN const char e_nomatch2[] INIT(= N_("E480: No match: %s")); -EXTERN const char e_noname[] INIT(= N_("E32: No file name")); -EXTERN const char e_nopresub[] INIT(= N_("E33: No previous substitute regular expression")); -EXTERN const char e_noprev[] INIT(= N_("E34: No previous command")); -EXTERN const char e_noprevre[] INIT(= N_("E35: No previous regular expression")); -EXTERN const char e_norange[] INIT(= N_("E481: No range allowed")); -EXTERN const char e_noroom[] INIT(= N_("E36: Not enough room")); -EXTERN const char e_notmp[] INIT(= N_("E483: Can't get temp file name")); -EXTERN const char e_notopen[] INIT(= N_("E484: Can't open file %s")); -EXTERN const char e_notopen_2[] INIT(= N_("E484: Can't open file %s: %s")); -EXTERN const char e_notread[] INIT(= N_("E485: Can't read file %s")); -EXTERN const char e_null[] INIT(= N_("E38: Null argument")); -EXTERN const char e_number_exp[] INIT(= N_("E39: Number expected")); -EXTERN const char e_openerrf[] INIT(= N_("E40: Can't open errorfile %s")); -EXTERN const char e_outofmem[] INIT(= N_("E41: Out of memory!")); -EXTERN const char e_patnotf[] INIT(= N_("Pattern not found")); -EXTERN const char e_patnotf2[] INIT(= N_("E486: Pattern not found: %s")); -EXTERN const char e_positive[] INIT(= N_("E487: Argument must be positive")); -EXTERN const char e_prev_dir[] INIT(= N_("E459: Cannot go back to previous directory")); - -EXTERN const char e_no_errors[] INIT(= N_("E42: No Errors")); -EXTERN const char e_loclist[] INIT(= N_("E776: No location list")); -EXTERN const char e_re_damg[] INIT(= N_("E43: Damaged match string")); -EXTERN const char e_re_corr[] INIT(= N_("E44: Corrupted regexp program")); -EXTERN const char e_readonly[] INIT(= N_("E45: 'readonly' option is set (add ! to override)")); -EXTERN const char e_letwrong[] INIT(= N_("E734: Wrong variable type for %s=")); -EXTERN const char e_illvar[] INIT(= N_("E461: Illegal variable name: %s")); -EXTERN const char e_cannot_mod[] INIT(= N_("E995: Cannot modify existing variable")); -EXTERN const char e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%.*s\"")); -EXTERN const char e_stringreq[] INIT(= N_("E928: String required")); -EXTERN const char e_dictreq[] INIT(= N_("E715: Dictionary required")); -EXTERN const char e_blobidx[] INIT(= N_("E979: Blob index out of range: %" PRId64)); -EXTERN const char e_invalblob[] INIT(= N_("E978: Invalid operation for Blob")); -EXTERN const char e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s")); -EXTERN const char e_toofewarg[] INIT(= N_("E119: Not enough arguments for function: %s")); -EXTERN const char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: \"%s\"")); -EXTERN const char e_dictkey_len[] INIT(= N_("E716: Key not present in Dictionary: \"%.*s\"")); -EXTERN const char e_listreq[] INIT(= N_("E714: List required")); -EXTERN const char e_listblobreq[] INIT(= N_("E897: List or Blob required")); -EXTERN const char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary")); -EXTERN const char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List, Dictionary or Blob")); -EXTERN const char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); -EXTERN const char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox")); -EXTERN const char e_secure[] INIT(= N_("E523: Not allowed here")); -EXTERN const char e_textlock[] INIT(= N_("E565: Not allowed to change text or change window")); -EXTERN const char e_screenmode[] INIT(= N_("E359: Screen mode setting not supported")); -EXTERN const char e_scroll[] INIT(= N_("E49: Invalid scroll size")); -EXTERN const char e_shellempty[] INIT(= N_("E91: 'shell' option is empty")); -EXTERN const char e_signdata[] INIT(= N_("E255: Couldn't read in sign data!")); -EXTERN const char e_swapclose[] INIT(= N_("E72: Close error on swap file")); -EXTERN const char e_toocompl[] INIT(= N_("E74: Command too complex")); -EXTERN const char e_longname[] INIT(= N_("E75: Name too long")); -EXTERN const char e_toomsbra[] INIT(= N_("E76: Too many [")); -EXTERN const char e_toomany[] INIT(= N_("E77: Too many file names")); -EXTERN const char e_trailing[] INIT(= N_("E488: Trailing characters")); -EXTERN const char e_trailing_arg[] INIT(= N_("E488: Trailing characters: %s")); -EXTERN const char e_umark[] INIT(= N_("E78: Unknown mark")); -EXTERN const char e_wildexpand[] INIT(= N_("E79: Cannot expand wildcards")); -EXTERN const char e_winheight[] INIT(= N_("E591: 'winheight' cannot be smaller than 'winminheight'")); -EXTERN const char e_winwidth[] INIT(= N_("E592: 'winwidth' cannot be smaller than 'winminwidth'")); -EXTERN const char e_write[] INIT(= N_("E80: Error while writing")); -EXTERN const char e_zerocount[] INIT(= N_("E939: Positive count required")); -EXTERN const char e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context")); -EXTERN const char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); -EXTERN const char e_empty_buffer[] INIT(= N_("E749: Empty buffer")); -EXTERN const char e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist")); - -EXTERN const char e_str_not_inside_function[] INIT(= N_("E193: %s not inside a function")); - -EXTERN const char e_invalpat[] INIT(= N_("E682: Invalid search pattern or delimiter")); -EXTERN const char e_bufloaded[] INIT(= N_("E139: File is loaded in another buffer")); -EXTERN const char e_notset[] INIT(= N_("E764: Option '%s' is not set")); -EXTERN const char e_invalidreg[] INIT(= N_("E850: Invalid register name")); -EXTERN const char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); -EXTERN const char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); -EXTERN const char e_menu_only_exists_in_another_mode[] -INIT(= N_("E328: Menu only exists in another mode")); -EXTERN const char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window")); -EXTERN const char e_listarg[] INIT(= N_("E686: Argument of %s must be a List")); -EXTERN const char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); -EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long")); -EXTERN const char e_using_float_as_string[] INIT(= N_("E806: Using a Float as a String")); -EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); -EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d")); -EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s")); -EXTERN const char e_auabort[] INIT(= N_("E855: Autocommands caused command to abort")); - -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_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_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")); - -EXTERN const char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range")); - -EXTERN const char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invalid character in group name")); - -EXTERN const char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); - -EXTERN const char e_invalid_column_number_nr[] INIT( = N_("E964: Invalid column number: %ld")); -EXTERN const char e_invalid_line_number_nr[] INIT(= N_("E966: Invalid line number: %ld")); - -EXTERN const char e_stray_closing_curly_str[] -INIT(= N_("E1278: Stray '}' without a matching '{': %s")); -EXTERN const char e_missing_close_curly_str[] -INIT(= N_("E1279: Missing '}': %s")); - -EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s")); - -EXTERN const char e_undobang_cannot_redo_or_move_branch[] -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_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); - -EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s")); - -EXTERN const char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); -EXTERN const char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); - -EXTERN const char line_msg[] INIT(= N_(" line ")); - -EXTERN FILE *time_fd INIT(= NULL); // Where to write --startuptime report. +EXTERN FILE *time_fd INIT( = NULL); // Where to write --startuptime report. // Some compilers warn for not using a return value, but in some situations we // can't do anything useful with the value. Assign to this variable to avoid @@ -989,11 +801,9 @@ EXTERN FILE *time_fd INIT(= NULL); // Where to write --startuptime report. EXTERN int vim_ignored; // stdio is an RPC channel (--embed). -EXTERN bool embedded_mode INIT(= false); +EXTERN bool embedded_mode INIT( = false); // Do not start UI (--headless, -l) nor read/write to stdio (unless embedding). -EXTERN bool headless_mode INIT(= false); - -// uncrustify:on +EXTERN bool headless_mode INIT( = false); /// Only filled for Win32. EXTERN char windowsVersion[20] INIT( = { 0 }); diff --git a/src/nvim/help.c b/src/nvim/help.c index e9f67ca3e4..6ba3e17e0b 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -13,6 +13,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" +#include "nvim/errors.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" @@ -654,7 +655,7 @@ void get_local_additions(void) // files. This uses the very first line in the help file. char *const fname = path_tail(curbuf->b_fname); if (path_fnamecmp(fname, "help.txt") == 0 - || (path_fnamencmp(fname, "help.", 5) == 0 + || (path_fnamencmp(fname, S_LEN("help.")) == 0 && ASCII_ISALPHA(fname[5]) && ASCII_ISALPHA(fname[6]) && TOLOWER_ASC(fname[7]) == 'x' @@ -957,8 +958,8 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool if (s == p2 && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t') && (vim_strchr(" \t\n\r", (uint8_t)s[1]) != NULL - || s[1] == '\0')) { - *p2 = '\0'; + || s[1] == NUL)) { + *p2 = NUL; p1++; size_t s_len = (size_t)(p2 - p1) + strlen(fname) + 2; s = xmalloc(s_len); @@ -1014,7 +1015,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool // Write the tags into the file. for (int i = 0; i < ga.ga_len; i++) { s = ((char **)ga.ga_data)[i]; - if (strncmp(s, "help-tags\t", 10) == 0) { + if (strncmp(s, S_LEN("help-tags\t")) == 0) { // help-tags entry was added in formatted form fputs(s, fd_tags); } else { @@ -1148,7 +1149,7 @@ void ex_helptags(exarg_T *eap) bool add_help_tags = false; // Check for ":helptags ++t {dir}". - if (strncmp(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) { + if (strncmp(eap->arg, S_LEN("++t")) == 0 && ascii_iswhite(eap->arg[3])) { add_help_tags = true; eap->arg = skipwhite(eap->arg + 3); } diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h index cb3a84bcaf..558727fc51 100644 --- a/src/nvim/highlight.h +++ b/src/nvim/highlight.h @@ -78,6 +78,8 @@ EXTERN const char *hlf_names[] INIT( = { [HLF_CU] = "Cursor", [HLF_BTITLE] = "FloatTitle", [HLF_BFOOTER] = "FloatFooter", + [HLF_TS] = "StatusLineTerm", + [HLF_TSNC] = "StatusLineTermNC", }); EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context. diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 25ab9dc2d9..17e3db04da 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -125,6 +125,8 @@ typedef enum { HLF_CU, ///< Cursor HLF_BTITLE, ///< Float Border Title HLF_BFOOTER, ///< Float Border Footer + HLF_TS, ///< status line for terminal window + HLF_TSNC, ///< status line for non-current terminal window HLF_COUNT, ///< MUST be the last one } hlf_T; diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index ccb093c116..cc9e606d1d 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -22,6 +22,7 @@ #include "nvim/cursor_shape.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" @@ -150,34 +151,36 @@ static const char *highlight_init_both[] = { "lCursor guifg=bg guibg=fg", // UI - "default link CursorIM Cursor", - "default link CursorLineFold FoldColumn", - "default link CursorLineSign SignColumn", - "default link EndOfBuffer NonText", - "default link FloatBorder NormalFloat", - "default link FloatFooter FloatTitle", - "default link FloatTitle Title", - "default link FoldColumn SignColumn", - "default link IncSearch CurSearch", - "default link LineNrAbove LineNr", - "default link LineNrBelow LineNr", - "default link MsgSeparator StatusLine", - "default link MsgArea NONE", - "default link NormalNC NONE", - "default link PmenuExtra Pmenu", - "default link PmenuExtraSel PmenuSel", - "default link PmenuKind Pmenu", - "default link PmenuKindSel PmenuSel", - "default link PmenuSbar Pmenu", - "default link Substitute Search", - "default link TabLine StatusLineNC", - "default link TabLineFill TabLine", - "default link TermCursorNC NONE", - "default link VertSplit WinSeparator", - "default link VisualNOS Visual", - "default link Whitespace NonText", - "default link WildMenu PmenuSel", - "default link WinSeparator Normal", + "default link CursorIM Cursor", + "default link CursorLineFold FoldColumn", + "default link CursorLineSign SignColumn", + "default link EndOfBuffer NonText", + "default link FloatBorder NormalFloat", + "default link FloatFooter FloatTitle", + "default link FloatTitle Title", + "default link FoldColumn SignColumn", + "default link IncSearch CurSearch", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", + "default link MsgSeparator StatusLine", + "default link MsgArea NONE", + "default link NormalNC NONE", + "default link PmenuExtra Pmenu", + "default link PmenuExtraSel PmenuSel", + "default link PmenuKind Pmenu", + "default link PmenuKindSel PmenuSel", + "default link PmenuSbar Pmenu", + "default link Substitute Search", + "default link StatusLineTerm StatusLine", + "default link StatusLineTermNC StatusLineNC", + "default link TabLine StatusLineNC", + "default link TabLineFill TabLine", + "default link TermCursorNC NONE", + "default link VertSplit WinSeparator", + "default link VisualNOS Visual", + "default link Whitespace NonText", + "default link WildMenu PmenuSel", + "default link WinSeparator Normal", // Syntax "default link Character Constant", @@ -1056,7 +1059,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } int from_id = syn_check_group(from_start, (size_t)(from_end - from_start)); - if (strncmp(to_start, "NONE", 4) == 0) { + if (strncmp(to_start, S_LEN("NONE")) == 0) { to_id = 0; } else { to_id = syn_check_group(to_start, (size_t)(to_end - to_start)); @@ -1173,7 +1176,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) break; } vim_memcpy_up(key, key_start, key_len); - key[key_len] = '\0'; + key[key_len] = NUL; linep = skipwhite(linep); if (strcmp(key, "NONE") == 0) { @@ -1966,7 +1969,7 @@ int syn_name2id_len(const char *name, size_t len) // Avoid using stricmp() too much, it's slow on some systems // Avoid alloc()/free(), these are slow too. vim_memcpy_up(name_u, name, len); - name_u[len] = '\0'; + name_u[len] = NUL; // map_get(..., int) returns 0 when no key is present, which is // the expected value for missing highlight group. diff --git a/src/nvim/indent.c b/src/nvim/indent.c index d635c7d7bf..a4964db465 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -13,6 +13,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" @@ -146,25 +147,42 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts) } /// Find the size of the tab that covers a particular column. -int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts) +/// +/// If this is being called as part of a shift operation, col is not the cursor +/// column but is the column number to the left of the first non-whitespace +/// character in the line. If the shift is to the left (left == true), then +/// return the size of the tab interval to the left of the column. +int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts, bool left) { - colnr_T tabcol = 0; - int t; - int tab_size = 0; - if (vts == NULL || vts[0] == 0) { return (int)ts; } - const int tabcount = vts[0]; + colnr_T tabcol = 0; // Column of the tab stop under consideration. + int t; // Tabstop index in the list of variable tab stops. + int tab_size = 0; // Size of the tab stop interval to the right or left of the col. + const int tabcount // Number of tab stops in the list of variable tab stops. + = vts[0]; for (t = 1; t <= tabcount; t++) { tabcol += vts[t]; if (tabcol > col) { - tab_size = vts[t]; + // If shifting left (left == true), and if the column to the left of + // the first first non-blank character (col) in the line is + // already to the left of the first tabstop, set the shift amount + // (tab_size) to just enough to shift the line to the left margin. + // The value doesn't seem to matter as long as it is at least that + // distance. + if (left && (t == 1)) { + tab_size = col; + } else { + tab_size = vts[t - (left ? 1 : 0)]; + } break; } } - if (t > tabcount) { + if (t > tabcount) { // If the value of the index t is beyond the + // end of the list, use the tab stop value at + // the end of the list. tab_size = vts[tabcount]; } @@ -311,35 +329,35 @@ int tabstop_first(colnr_T *ts) /// 'tabstop' value when 'shiftwidth' is zero. int get_sw_value(buf_T *buf) { - int result = get_sw_value_col(buf, 0); + int result = get_sw_value_col(buf, 0, false); return result; } /// Idem, using "pos". -int get_sw_value_pos(buf_T *buf, pos_T *pos) +int get_sw_value_pos(buf_T *buf, pos_T *pos, bool left) { pos_T save_cursor = curwin->w_cursor; curwin->w_cursor = *pos; - int sw_value = get_sw_value_col(buf, get_nolist_virtcol()); + int sw_value = get_sw_value_col(buf, get_nolist_virtcol(), left); curwin->w_cursor = save_cursor; return sw_value; } /// Idem, using the first non-black in the current line. -int get_sw_value_indent(buf_T *buf) +int get_sw_value_indent(buf_T *buf, bool left) { pos_T pos = curwin->w_cursor; pos.col = (colnr_T)getwhitecols_curline(); - return get_sw_value_pos(buf, &pos); + return get_sw_value_pos(buf, &pos, left); } /// Idem, using virtual column "col". -int get_sw_value_col(buf_T *buf, colnr_T col) +int get_sw_value_col(buf_T *buf, colnr_T col, bool left) { return buf->b_p_sw ? (int)buf->b_p_sw - : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); + : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array, left); } /// Return the effective softtabstop value for the current buffer, @@ -764,20 +782,20 @@ bool briopt_check(win_T *wp) char *p = wp->w_p_briopt; while (*p != NUL) { // Note: Keep this in sync with p_briopt_values - if (strncmp(p, "shift:", 6) == 0 + if (strncmp(p, S_LEN("shift:")) == 0 && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) { p += 6; bri_shift = getdigits_int(&p, true, 0); - } else if (strncmp(p, "min:", 4) == 0 && ascii_isdigit(p[4])) { + } else if (strncmp(p, S_LEN("min:")) == 0 && ascii_isdigit(p[4])) { p += 4; bri_min = getdigits_int(&p, true, 0); - } else if (strncmp(p, "sbr", 3) == 0) { + } else if (strncmp(p, S_LEN("sbr")) == 0) { p += 3; bri_sbr = true; - } else if (strncmp(p, "list:", 5) == 0) { + } else if (strncmp(p, S_LEN("list:")) == 0) { p += 5; bri_list = (int)getdigits(&p, false, 0); - } else if (strncmp(p, "column:", 7) == 0) { + } else if (strncmp(p, S_LEN("column:")) == 0) { p += 7; bri_vcol = (int)getdigits(&p, false, 0); } diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 8b1c09b32f..711e2292b1 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -22,6 +22,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -167,6 +168,7 @@ struct compl_S { ///< cp_flags has CP_FREE_FNAME int cp_flags; ///< CP_ values int cp_number; ///< sequence number + int cp_score; ///< fuzzy match score }; /// state information used for getting the next set of insert completion @@ -224,13 +226,6 @@ static char *compl_leader = NULL; static bool compl_get_longest = false; ///< put longest common string in compl_leader -static bool compl_no_insert = false; ///< false: select & insert - ///< true: noinsert -static bool compl_no_select = false; ///< false: select & insert - ///< true: noselect -static bool compl_longest = false; ///< false: insert full match - ///< true: insert longest prefix - /// Selected one of the matches. When false the match was edited or using the /// longest common string. static bool compl_used_match; @@ -287,7 +282,7 @@ static bool compl_opt_refresh_always = false; static size_t spell_bad_len = 0; // length of located bad word -static int pum_selected_item = -1; +static int compl_selected_item = -1; /// CTRL-X pressed in Insert mode. void ins_ctrl_x(void) @@ -1048,22 +1043,10 @@ bool ins_compl_long_shown_match(void) && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col; } -/// Set variables that store noselect and noinsert behavior from the -/// 'completeopt' value. -void completeopt_was_set(void) +/// Get the local or global value of 'completeopt' flags. +unsigned get_cot_flags(void) { - compl_no_insert = false; - compl_no_select = false; - compl_longest = false; - if (strstr(p_cot, "noselect") != NULL) { - compl_no_select = true; - } - if (strstr(p_cot, "noinsert") != NULL) { - compl_no_insert = true; - } - if (strstr(p_cot, "longest") != NULL) { - compl_longest = true; - } + return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags; } /// "compl_match_array" points the currently displayed list of entries in the @@ -1087,7 +1070,7 @@ bool pum_wanted(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // "completeopt" must contain "menu" or "menuone" - return vim_strchr(p_cot, 'm') != NULL; + return (get_cot_flags() & COT_ANY_MENU) != 0; } /// Check that there are two or more matches to be shown in the popup menu. @@ -1106,7 +1089,7 @@ static bool pum_enough_matches(void) comp = comp->cp_next; } while (!is_first_match(comp)); - if (strstr(p_cot, "menuone") != NULL) { + if (get_cot_flags() & COT_MENUONE) { return i >= 1; } return i >= 2; @@ -1160,6 +1143,14 @@ static void trigger_complete_changed_event(int cur) restore_v_event(v_event, &save_v_event); } +/// pumitem qsort compare func +static int ins_compl_fuzzy_sort(const void *a, const void *b) +{ + const int sa = (*(pumitem_T *)a).pum_score; + const int sb = (*(pumitem_T *)b).pum_score; + return sa == sb ? 0 : sa < sb ? 1 : -1; +} + /// Build a popup menu to show the completion matches. /// /// @return the popup menu entry that should be selected, @@ -1177,11 +1168,22 @@ static int ins_compl_build_pum(void) } const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0; + int max_fuzzy_score = 0; + unsigned cur_cot_flags = get_cot_flags(); + bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; + bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; do { + // When 'completeopt' contains "fuzzy" and leader is not NULL or empty, + // set the cp_score for later comparisons. + if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { + comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader); + } + if (!match_at_original_text(comp) && (compl_leader == NULL - || ins_compl_equal(comp, compl_leader, (size_t)lead_len))) { + || ins_compl_equal(comp, compl_leader, (size_t)lead_len) + || (compl_fuzzy_match && comp->cp_score > 0))) { compl_match_arraysize++; } comp = comp->cp_next; @@ -1210,8 +1212,9 @@ static int ins_compl_build_pum(void) do { if (!match_at_original_text(comp) && (compl_leader == NULL - || ins_compl_equal(comp, compl_leader, (size_t)lead_len))) { - if (!shown_match_ok) { + || ins_compl_equal(comp, compl_leader, (size_t)lead_len) + || (compl_fuzzy_match && comp->cp_score > 0))) { + if (!shown_match_ok && !compl_fuzzy_match) { if (comp == compl_shown_match || did_find_shown_match) { // This item is the shown match or this is the // first displayed item after the shown match. @@ -1224,6 +1227,26 @@ static int ins_compl_build_pum(void) shown_compl = comp; } cur = i; + } else if (compl_fuzzy_match) { + // Update the maximum fuzzy score and the shown match + // if the current item's score is higher + if (comp->cp_score > max_fuzzy_score) { + did_find_shown_match = true; + max_fuzzy_score = comp->cp_score; + compl_shown_match = comp; + shown_match_ok = true; + } + + // If there is no "no select" condition and the max fuzzy + // score is positive, or there is no completion leader or the + // leader length is zero, mark the shown match as valid and + // reset the current index. + if (!compl_no_select + && (max_fuzzy_score > 0 + || (compl_leader == NULL || lead_len == 0))) { + shown_match_ok = true; + cur = 0; + } } if (comp->cp_text[CPT_ABBR] != NULL) { @@ -1233,6 +1256,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; if (comp->cp_text[CPT_MENU] != NULL) { compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU]; } else { @@ -1240,7 +1264,7 @@ static int ins_compl_build_pum(void) } } - if (comp == compl_shown_match) { + if (comp == compl_shown_match && !compl_fuzzy_match) { did_find_shown_match = true; // When the original text is the shown match don't set @@ -1259,6 +1283,12 @@ static int ins_compl_build_pum(void) comp = comp->cp_next; } while (comp != NULL && !is_first_match(comp)); + if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { + // sort by the largest score of fuzzy match + qsort(compl_match_array, (size_t)compl_match_arraysize, sizeof(pumitem_T), + ins_compl_fuzzy_sort); + } + if (!shown_match_ok) { // no displayed match at all cur = -1; } @@ -1310,7 +1340,7 @@ void ins_compl_show_pum(void) // Use the cursor to get all wrapping and other settings right. const colnr_T col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; - pum_selected_item = cur; + compl_selected_item = cur; pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0); curwin->w_cursor.col = col; @@ -2178,7 +2208,7 @@ bool ins_compl_prep(int c) // Set "compl_get_longest" when finding the first matches. if (ctrl_x_mode_not_defined_yet() || (ctrl_x_mode_normal() && !compl_started)) { - compl_get_longest = compl_longest; + compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0; compl_used_match = true; } @@ -2598,6 +2628,10 @@ static void restore_orig_extmarks(void) static void set_completion(colnr_T startcol, list_T *list) { int flags = CP_ORIGINAL_TEXT; + unsigned cur_cot_flags = get_cot_flags(); + bool compl_longest = (cur_cot_flags & COT_LONGEST) != 0; + bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0; + bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; // If already doing completions stop it. if (ctrl_x_mode_not_default()) { @@ -3588,6 +3622,42 @@ static void ins_compl_show_filename(void) redraw_cmdline = false; // don't overwrite! } +/// Find a completion item when 'completeopt' contains "fuzzy". +static compl_T *find_comp_when_fuzzy(void) +{ + int target_idx = -1; + const bool is_forward = compl_shows_dir_forward(); + const bool is_backward = compl_shows_dir_backward(); + compl_T *comp = NULL; + + if (compl_match_array == NULL + || (is_forward && compl_selected_item == compl_match_arraysize - 1) + || (is_backward && compl_selected_item == 0)) { + return compl_first_match; + } + + if (is_forward) { + target_idx = compl_selected_item + 1; + } else if (is_backward) { + target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1 + : compl_selected_item - 1; + } + + const int score = compl_match_array[target_idx].pum_score; + char *const str = compl_match_array[target_idx].pum_text; + + comp = compl_first_match; + do { + if (comp->cp_score == score + && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) { + return comp; + } + comp = comp->cp_next; + } while (comp != NULL && !is_first_match(comp)); + + return NULL; +} + /// Find the next set of matches for completion. Repeat the completion "todo" /// times. The number of matches found is returned in 'num_matches'. /// @@ -3605,17 +3675,22 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a { bool found_end = false; compl_T *found_compl = NULL; + unsigned cur_cot_flags = get_cot_flags(); + bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; + bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; while (--todo >= 0) { if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; + compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_next + : find_comp_when_fuzzy(); found_end = (compl_first_match != NULL && (is_first_match(compl_shown_match->cp_next) || is_first_match(compl_shown_match))); } else if (compl_shows_dir_backward() && compl_shown_match->cp_prev != NULL) { found_end = is_first_match(compl_shown_match); - compl_shown_match = compl_shown_match->cp_prev; + compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_prev + : find_comp_when_fuzzy(); found_end |= is_first_match(compl_shown_match); } else { if (!allow_get_expansion) { @@ -3659,7 +3734,8 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a if (!match_at_original_text(compl_shown_match) && compl_leader != NULL && !ins_compl_equal(compl_shown_match, - compl_leader, strlen(compl_leader))) { + compl_leader, strlen(compl_leader)) + && !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) { todo++; } else { // Remember a matching item. @@ -3704,6 +3780,9 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match int todo = count; const bool started = compl_started; buf_T *const orig_curbuf = curbuf; + unsigned cur_cot_flags = get_cot_flags(); + bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0; + bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; // When user complete function return -1 for findstart which is next // time of 'always', compl_shown_match become NULL. @@ -3711,7 +3790,9 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match return -1; } - if (compl_leader != NULL && !match_at_original_text(compl_shown_match)) { + if (compl_leader != NULL + && !match_at_original_text(compl_shown_match) + && !compl_fuzzy_match) { // Update "compl_shown_match" to the actually shown match ins_compl_update_shown_match(); } @@ -3840,7 +3921,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) } } } - if (compl_pending != 0 && !got_int && !compl_no_insert) { + if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT)) { int todo = compl_pending > 0 ? compl_pending : -compl_pending; compl_pending = 0; @@ -3853,7 +3934,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func) static int ins_compl_key2dir(int c) { if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; + return pum_want.item < compl_selected_item ? BACKWARD : FORWARD; } if (c == Ctrl_P || c == Ctrl_L || c == K_PAGEUP || c == K_KPAGEUP @@ -3879,7 +3960,7 @@ static bool ins_compl_pum_key(int c) static int ins_compl_key2count(int c) { if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { - int offset = pum_want.item - pum_selected_item; + int offset = pum_want.item - compl_selected_item; return abs(offset); } @@ -3978,7 +4059,7 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) compl_pattern = xmalloc(7); STRCPY(compl_pattern, "\\<"); quote_meta(compl_pattern + 2, line + compl_col, 1); - STRCAT(compl_pattern, "\\k"); + strcat(compl_pattern, "\\k"); } else { compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); STRCPY(compl_pattern, "\\<"); diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c index 44ddfbba00..a2dffa4602 100644 --- a/src/nvim/keycodes.c +++ b/src/nvim/keycodes.c @@ -8,6 +8,7 @@ #include "nvim/ascii_defs.h" #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/gettext_defs.h" @@ -903,7 +904,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co } // Check for special <> keycodes, like "<C-S-LeftMouse>" if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3 - && strncmp(src, "<lt>", 4) != 0))) { + && strncmp(src, S_LEN("<lt>")) != 0))) { // Change <SID>Func to K_SNR <script-nr> _Func. This name is used // for script-local user functions. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c index c34b303193..24b2c82381 100644 --- a/src/nvim/linematch.c +++ b/src/nvim/linematch.c @@ -5,6 +5,7 @@ #include <stdint.h> #include <string.h> +#include "nvim/ascii_defs.h" #include "nvim/linematch.h" #include "nvim/macros_defs.h" #include "nvim/memory.h" @@ -61,7 +62,7 @@ static int matching_chars_iwhite(const char *s1, const char *s2) d++; } } - strsproc[k][i] = '\0'; + strsproc[k][i] = NUL; } int matching = matching_chars(strsproc[0], strsproc[1]); xfree(strsproc[0]); diff --git a/src/nvim/log.c b/src/nvim/log.c index fbb3e0385a..774f1c956d 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -46,7 +46,7 @@ static uv_mutex_t mutex; static bool log_try_create(char *fname) { - if (fname == NULL || fname[0] == '\0') { + if (fname == NULL || fname[0] == NUL) { return false; } FILE *log_file = fopen(fname, "a"); @@ -67,7 +67,7 @@ static void log_path_init(void) size_t size = sizeof(log_file_path); expand_env("$" ENV_LOGFILE, log_file_path, (int)size - 1); if (strequal("$" ENV_LOGFILE, log_file_path) - || log_file_path[0] == '\0' + || log_file_path[0] == NUL || os_isdir(log_file_path) || !log_try_create(log_file_path)) { // Make $XDG_STATE_HOME if it does not exist. @@ -88,7 +88,7 @@ static void log_path_init(void) } // Fall back to stderr if (len >= size || !log_try_create(log_file_path)) { - log_file_path[0] = '\0'; + log_file_path[0] = NUL; return; } os_setenv(ENV_LOGFILE, log_file_path, true); @@ -324,7 +324,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, // Get a name for this Nvim instance. // TODO(justinmk): expose this as v:name ? - if (name[0] == '\0') { + if (name[0] == NUL) { // Parent servername. const char *parent = path_tail(os_getenv(ENV_NVIM)); // Servername. Empty until starting=false. @@ -350,7 +350,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, func_name, line_num); if (name[0] == '?') { // No v:servername yet. Clear `name` so that the next log can try again. - name[0] = '\0'; + name[0] = NUL; } if (rv < 0) { diff --git a/src/nvim/lua/api_wrappers.c b/src/nvim/lua/api_wrappers.c index 2b7b0c6471..36847d1fc9 100644 --- a/src/nvim/lua/api_wrappers.c +++ b/src/nvim/lua/api_wrappers.c @@ -5,6 +5,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" +#include "nvim/errors.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/func_attr.h" diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index a76b8213e5..6246452b92 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -22,6 +22,7 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c index f62e0ace04..61277949c4 100644 --- a/src/nvim/lua/secure.c +++ b/src/nvim/lua/secure.c @@ -3,6 +3,7 @@ #include <string.h> #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/ex_cmds_defs.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -103,12 +104,12 @@ void ex_trust(exarg_T *eap) action = "deny"; } else if (strcmp(arg1, "++remove") == 0) { action = "remove"; - } else if (*arg1 != '\0') { + } else if (*arg1 != NUL) { semsg(e_invarg2, arg1); goto theend; } - if (path[0] == '\0') { + if (path[0] == NUL) { path = NULL; } diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index ba83239dc5..f4dacd7a55 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -7,6 +7,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" +#include "nvim/errors.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 22ee0a1c98..ee0eabbebb 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -17,10 +17,13 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" +#include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" +#include "nvim/eval/window.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/fold.h" #include "nvim/globals.h" @@ -40,6 +43,7 @@ #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/types_defs.h" +#include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/stdlib.c.generated.h" @@ -568,6 +572,99 @@ static int nlua_foldupdate(lua_State *lstate) return 0; } +static int nlua_with(lua_State *L) +{ + int flags = 0; + buf_T *buf = NULL; + win_T *win = NULL; + +#define APPLY_FLAG(key, flag) \ + if (strequal((key), k) && (v)) { \ + flags |= (flag); \ + } + + luaL_argcheck(L, lua_istable(L, 1), 1, "table expected"); + lua_pushnil(L); // [dict, ..., nil] + while (lua_next(L, 1)) { + // [dict, ..., key, value] + if (lua_type(L, -2) == LUA_TSTRING) { + const char *k = lua_tostring(L, -2); + bool v = lua_toboolean(L, -1); + if (strequal("buf", k)) { \ + buf = handle_get_buffer((int)luaL_checkinteger(L, -1)); + } else if (strequal("win", k)) { \ + win = handle_get_window((int)luaL_checkinteger(L, -1)); + } else { + APPLY_FLAG("sandbox", CMOD_SANDBOX); + APPLY_FLAG("silent", CMOD_SILENT); + APPLY_FLAG("emsg_silent", CMOD_ERRSILENT); + APPLY_FLAG("unsilent", CMOD_UNSILENT); + APPLY_FLAG("noautocmd", CMOD_NOAUTOCMD); + APPLY_FLAG("hide", CMOD_HIDE); + APPLY_FLAG("keepalt", CMOD_KEEPALT); + APPLY_FLAG("keepmarks", CMOD_KEEPMARKS); + APPLY_FLAG("keepjumps", CMOD_KEEPJUMPS); + APPLY_FLAG("lockmarks", CMOD_LOCKMARKS); + APPLY_FLAG("keeppatterns", CMOD_KEEPPATTERNS); + } + } + // pop the value; lua_next will pop the key. + lua_pop(L, 1); // [dict, ..., key] + } + int status = 0; + int rets = 0; + + cmdmod_T save_cmdmod = cmdmod; + cmdmod.cmod_flags = flags; + apply_cmdmod(&cmdmod); + + if (buf || win) { + try_start(); + } + + aco_save_T aco; + win_execute_T win_execute_args; + Error err = ERROR_INIT; + + if (win) { + tabpage_T *tabpage = win_find_tabpage(win); + if (!win_execute_before(&win_execute_args, win, tabpage)) { + goto end; + } + } else if (buf) { + aucmd_prepbuf(&aco, buf); + } + + int s = lua_gettop(L); + lua_pushvalue(L, 2); + status = lua_pcall(L, 0, LUA_MULTRET, 0); + rets = lua_gettop(L) - s; + + if (win) { + win_execute_after(&win_execute_args); + } else if (buf) { + aucmd_restbuf(&aco); + } + +end: + if (buf || win) { + try_end(&err); + } + + undo_cmdmod(&cmdmod); + cmdmod = save_cmdmod; + + if (status) { + return lua_error(L); + } else if (ERROR_SET(&err)) { + nlua_push_errstr(L, "%s", err.msg); + api_clear_error(&err); + return lua_error(L); + } + + return rets; +} + // Access to internal functions. For use in runtime/ static void nlua_state_add_internal(lua_State *const lstate) { @@ -582,6 +679,9 @@ static void nlua_state_add_internal(lua_State *const lstate) // _updatefolds lua_pushcfunction(lstate, &nlua_foldupdate); lua_setfield(lstate, -2, "_foldupdate"); + + lua_pushcfunction(lstate, &nlua_with); + lua_setfield(lstate, -2, "_with_c"); } void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index e87cf756a8..b13a162074 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -279,7 +279,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position memcpy(buf, line + position.column, tocopy); // Translate embedded \n to NUL - memchrsub(buf, '\n', '\0', tocopy); + memchrsub(buf, '\n', NUL, tocopy); *bytes_read = (uint32_t)tocopy; if (tocopy < BUFSIZE) { // now add the final \n. If it didn't fit, input_cb will be called again diff --git a/src/nvim/main.c b/src/nvim/main.c index cf1324d37f..5e243df975 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -37,6 +37,7 @@ #include "nvim/diff.h" #include "nvim/drawline.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -154,7 +155,6 @@ void event_init(void) loop_init(&main_loop, NULL); resize_events = multiqueue_new_child(main_loop.events); - input_init(); signal_init(); // mspgack-rpc initialization channel_init(); diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 9320390d68..d02c865328 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -19,6 +19,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -402,43 +403,43 @@ static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapa // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in // any order. while (true) { - if (strncmp(to_parse, "<buffer>", 8) == 0) { + if (strncmp(to_parse, S_LEN("<buffer>")) == 0) { to_parse = skipwhite(to_parse + 8); mapargs->buffer = true; continue; } - if (strncmp(to_parse, "<nowait>", 8) == 0) { + if (strncmp(to_parse, S_LEN("<nowait>")) == 0) { to_parse = skipwhite(to_parse + 8); mapargs->nowait = true; continue; } - if (strncmp(to_parse, "<silent>", 8) == 0) { + if (strncmp(to_parse, S_LEN("<silent>")) == 0) { to_parse = skipwhite(to_parse + 8); mapargs->silent = true; continue; } // Ignore obsolete "<special>" modifier. - if (strncmp(to_parse, "<special>", 9) == 0) { + if (strncmp(to_parse, S_LEN("<special>")) == 0) { to_parse = skipwhite(to_parse + 9); continue; } - if (strncmp(to_parse, "<script>", 8) == 0) { + if (strncmp(to_parse, S_LEN("<script>")) == 0) { to_parse = skipwhite(to_parse + 8); mapargs->script = true; continue; } - if (strncmp(to_parse, "<expr>", 6) == 0) { + if (strncmp(to_parse, S_LEN("<expr>")) == 0) { to_parse = skipwhite(to_parse + 6); mapargs->expr = true; continue; } - if (strncmp(to_parse, "<unique>", 8) == 0) { + if (strncmp(to_parse, S_LEN("<unique>")) == 0) { to_parse = skipwhite(to_parse + 8); mapargs->unique = true; continue; @@ -1251,32 +1252,32 @@ char *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit, b xp->xp_context = EXPAND_MAPPINGS; expand_buffer = false; while (true) { - if (strncmp(arg, "<buffer>", 8) == 0) { + if (strncmp(arg, S_LEN("<buffer>")) == 0) { expand_buffer = true; arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<unique>", 8) == 0) { + if (strncmp(arg, S_LEN("<unique>")) == 0) { arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<nowait>", 8) == 0) { + if (strncmp(arg, S_LEN("<nowait>")) == 0) { arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<silent>", 8) == 0) { + if (strncmp(arg, S_LEN("<silent>")) == 0) { arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<special>", 9) == 0) { + if (strncmp(arg, S_LEN("<special>")) == 0) { arg = skipwhite(arg + 9); continue; } - if (strncmp(arg, "<script>", 8) == 0) { + if (strncmp(arg, S_LEN("<script>")) == 0) { arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<expr>", 6) == 0) { + if (strncmp(arg, S_LEN("<expr>")) == 0) { arg = skipwhite(arg + 6); continue; } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 6ce42bb7fe..16f444a316 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -14,6 +14,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 34d6cd118f..9e3005b6a3 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -2286,7 +2286,7 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) void marktree_put_test(MarkTree *b, uint32_t ns, uint32_t id, int row, int col, bool right_gravity, int end_row, int end_col, bool end_right, bool meta_inline) { - uint16_t flags = mt_flags(right_gravity, false, false, false, false); + uint16_t flags = mt_flags(right_gravity, false, false, false); // The specific choice is irrelevant here, we pick one counted decor // type to test the counting and filtering logic. flags |= meta_inline ? MT_FLAG_DECOR_VIRT_TEXT_INLINE : 0; diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 8e5cf30ff3..15df57ef63 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -35,8 +35,6 @@ #define MT_FLAG_DECOR_VIRT_LINES (((uint16_t)1) << 11) #define MT_FLAG_DECOR_VIRT_TEXT_INLINE (((uint16_t)1) << 12) -#define MT_FLAG_SCOPED (((uint16_t)1) << 13) - // These _must_ be last to preserve ordering of marks #define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14) #define MT_FLAG_LAST (((uint16_t)1) << 15) @@ -46,7 +44,7 @@ | MT_FLAG_DECOR_VIRT_TEXT_INLINE) #define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO \ - | MT_FLAG_INVALIDATE | MT_FLAG_INVALID | MT_FLAG_SCOPED) + | MT_FLAG_INVALIDATE | MT_FLAG_INVALID) // this is defined so that start and end of the same range have adjacent ids #define MARKTREE_END_FLAG ((uint64_t)1) @@ -110,24 +108,12 @@ static inline bool mt_decor_sign(MTKey key) return key.flags & (MT_FLAG_DECOR_SIGNTEXT | MT_FLAG_DECOR_SIGNHL); } -static inline bool mt_scoped(MTKey key) -{ - return key.flags & MT_FLAG_SCOPED; -} - -static inline bool mt_scoped_in_win(MTKey key, win_T *wp) -{ - return !mt_scoped(key) || set_has(uint32_t, &wp->w_ns_set, key.ns); -} - -static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext, - bool scoped) +static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext) { return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0) | (no_undo ? MT_FLAG_NO_UNDO : 0) | (invalidate ? MT_FLAG_INVALIDATE : 0) - | (decor_ext ? MT_FLAG_DECOR_EXT : 0) - | (scoped ? MT_FLAG_SCOPED : 0)); + | (decor_ext ? MT_FLAG_DECOR_EXT : 0)); } static inline MTPair mtpair_from(MTKey start, MTKey end) diff --git a/src/nvim/match.c b/src/nvim/match.c index 580d7d1069..6ae4e41147 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -10,6 +10,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/window.h" diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index a345795bbe..018febb995 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -43,6 +43,7 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/getchar.h" @@ -370,9 +371,9 @@ int enc_canon_props(const char *name) int i = enc_canon_search(name); if (i >= 0) { return enc_canon_table[i].prop; - } else if (strncmp(name, "2byte-", 6) == 0) { + } else if (strncmp(name, S_LEN("2byte-")) == 0) { return ENC_DBCS; - } else if (strncmp(name, "8bit-", 5) == 0 || strncmp(name, "iso-8859-", 9) == 0) { + } else if (strncmp(name, S_LEN("8bit-")) == 0 || strncmp(name, S_LEN("iso-8859-")) == 0) { return ENC_8BIT; } return 0; @@ -392,10 +393,10 @@ int bomb_size(void) if (*curbuf->b_p_fenc == NUL || strcmp(curbuf->b_p_fenc, "utf-8") == 0) { n = 3; - } else if (strncmp(curbuf->b_p_fenc, "ucs-2", 5) == 0 - || strncmp(curbuf->b_p_fenc, "utf-16", 6) == 0) { + } else if (strncmp(curbuf->b_p_fenc, S_LEN("ucs-2")) == 0 + || strncmp(curbuf->b_p_fenc, S_LEN("utf-16")) == 0) { n = 2; - } else if (strncmp(curbuf->b_p_fenc, "ucs-4", 5) == 0) { + } else if (strncmp(curbuf->b_p_fenc, S_LEN("ucs-4")) == 0) { n = 4; } } @@ -1545,7 +1546,7 @@ int utf16_to_utf8(const wchar_t *utf16, int utf16len, char **utf8) return uv_translate_sys_error(GetLastError()); } - (*utf8)[bufsize] = '\0'; + (*utf8)[bufsize] = NUL; return 0; } @@ -2187,10 +2188,10 @@ const char *mb_unescape(const char **const pp) /// Skip the Vim specific head of a 'encoding' name. char *enc_skip(char *p) { - if (strncmp(p, "2byte-", 6) == 0) { + if (strncmp(p, S_LEN("2byte-")) == 0) { return p + 6; } - if (strncmp(p, "8bit-", 5) == 0) { + if (strncmp(p, S_LEN("8bit-")) == 0) { return p + 5; } return p; @@ -2221,37 +2222,42 @@ char *enc_canonize(char *enc) } } *p = NUL; + char *p_e = p; // Skip "2byte-" and "8bit-". p = enc_skip(r); // Change "microsoft-cp" to "cp". Used in some spell files. - if (strncmp(p, "microsoft-cp", 12) == 0) { - STRMOVE(p, p + 10); + if (strncmp(p, S_LEN("microsoft-cp")) == 0) { + memmove(p, p + STRLEN_LITERAL("microsoft-"), + (size_t)(p_e - (p + STRLEN_LITERAL("microsoft-"))) + 1); } // "iso8859" -> "iso-8859" - if (strncmp(p, "iso8859", 7) == 0) { - STRMOVE(p + 4, p + 3); - p[3] = '-'; + if (strncmp(p, S_LEN("iso8859")) == 0) { + memmove(p + STRLEN_LITERAL("iso-"), p + STRLEN_LITERAL("iso"), + (size_t)(p_e - (p + STRLEN_LITERAL("iso"))) + 1); + p[STRLEN_LITERAL("iso")] = '-'; } // "iso-8859n" -> "iso-8859-n" - if (strncmp(p, "iso-8859", 8) == 0 && p[8] != '-') { - STRMOVE(p + 9, p + 8); - p[8] = '-'; + if (strncmp(p, S_LEN("iso-8859")) == 0 && p[8] != '-') { + memmove(p + STRLEN_LITERAL("iso-8859-"), p + STRLEN_LITERAL("iso-8859"), + (size_t)(p_e - (p + STRLEN_LITERAL("iso-8859"))) + 1); + p[STRLEN_LITERAL("iso-8859")] = '-'; } // "latin-N" -> "latinN" - if (strncmp(p, "latin-", 6) == 0) { - STRMOVE(p + 5, p + 6); + if (strncmp(p, S_LEN("latin-")) == 0) { + memmove(p + STRLEN_LITERAL("latin"), p + STRLEN_LITERAL("latin-"), + (size_t)(p_e - (p + STRLEN_LITERAL("latin-"))) + 1); } int i; if (enc_canon_search(p) >= 0) { // canonical name can be used unmodified if (p != r) { - STRMOVE(r, p); + memmove(r, p, (size_t)(p_e - p) + 1); } } else if ((i = enc_alias_search(p)) >= 0) { // alias recognized, get canonical name @@ -2314,7 +2320,7 @@ char *enc_locale(void) if (p > s + 2 && !STRNICMP(p + 1, "EUC", 3) && !isalnum((uint8_t)p[4]) && p[4] != '-' && p[-3] == '_') { // Copy "XY.EUC" to "euc-XY" to buf[10]. - memmove(buf, "euc-", 4); + memmove(buf, S_LEN("euc-")); buf[4] = (char)(ASCII_ISALNUM(p[-2]) ? TOLOWER_ASC(p[-2]) : 0); buf[5] = (char)(ASCII_ISALNUM(p[-1]) ? TOLOWER_ASC(p[-1]) : 0); buf[6] = NUL; diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index a1713edb66..498d212da3 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -46,6 +46,7 @@ #include "nvim/assert_defs.h" #include "nvim/buffer_defs.h" +#include "nvim/errors.h" #include "nvim/fileio.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5acf4f0c37..a21c232a2d 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -864,7 +864,7 @@ void ml_recover(bool checkext) goto theend; } ZeroBlock *b0p = hp->bh_data; - if (strncmp(b0p->b0_version, "VIM 3.0", 7) == 0) { + if (strncmp(b0p->b0_version, S_LEN("VIM 3.0")) == 0) { msg_start(); msg_outtrans(mfp->mf_fname, MSG_HIST); msg_puts_attr(_(" cannot be used with this version of Vim.\n"), @@ -1538,7 +1538,7 @@ static time_t swapfile_info(char *fname) int fd = os_open(fname, O_RDONLY, 0); if (fd >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { - if (strncmp(b0.b0_version, "VIM 3.0", 7) == 0) { + if (strncmp(b0.b0_version, S_LEN("VIM 3.0")) == 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]")); diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 789535e270..50860c69d0 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -19,6 +19,7 @@ #include "nvim/context.h" #include "nvim/decoration_provider.h" #include "nvim/drawline.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -202,7 +203,7 @@ void *xmallocz(size_t size) } void *ret = xmalloc(total_size); - ((char *)ret)[size] = '\0'; + ((char *)ret)[size] = NUL; return ret; } @@ -232,7 +233,7 @@ void *xmemcpyz(void *dst, const void *src, size_t len) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { memcpy(dst, src, len); - ((char *)dst)[len] = '\0'; + ((char *)dst)[len] = NUL; return dst; } @@ -240,7 +241,7 @@ void *xmemcpyz(void *dst, const void *src, size_t len) size_t xstrnlen(const char *s, size_t n) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - const char *end = memchr(s, '\0', n); + const char *end = memchr(s, NUL, n); if (end == NULL) { return n; } @@ -287,7 +288,7 @@ void *xmemscan(const void *addr, char c, size_t size) void strchrsub(char *str, char c, char x) FUNC_ATTR_NONNULL_ALL { - assert(c != '\0'); + assert(c != NUL); while ((str = strchr(str, c))) { *str++ = x; } @@ -387,7 +388,7 @@ char *xstpcpy(char *restrict dst, const char *restrict src) char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const char *p = memchr(src, '\0', maxlen); + const char *p = memchr(src, NUL, maxlen); if (p) { size_t srclen = (size_t)(p - src); memcpy(dst, src, srclen); @@ -419,7 +420,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t dsize) if (dsize) { size_t len = MIN(slen, dsize - 1); memcpy(dst, src, len); - dst[len] = '\0'; + dst[len] = NUL; } return slen; // Does not include NUL. @@ -449,7 +450,7 @@ size_t xstrlcat(char *const dst, const char *const src, const size_t dsize) if (slen > dsize - dlen - 1) { memmove(dst + dlen, src, dsize - dlen - 1); - dst[dsize - 1] = '\0'; + dst[dsize - 1] = NUL; } else { memmove(dst + dlen, src, slen + 1); } @@ -509,7 +510,7 @@ char *xstrndup(const char *str, size_t len) FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL { - char *p = memchr(str, '\0', len); + char *p = memchr(str, NUL, len); return xmemdupz(str, p ? (size_t)(p - str) : len); } @@ -882,7 +883,6 @@ void free_all_mem(void) decor_free_all_mem(); drawline_free_all_mem(); - input_free_all_mem(); if (ui_client_channel_id) { ui_client_free_all_mem(); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 0788670142..277ffe97aa 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -72,5 +72,3 @@ EXTERN size_t arena_alloc_count INIT( = 0); // Like strcpy() but allows overlapped source and destination. #define STRMOVE(d, s) memmove((d), (s), strlen(s) + 1) - -#define STRCAT(d, s) strcat((char *)(d), (char *)(s)) // NOLINT(runtime/printf) diff --git a/src/nvim/menu.c b/src/nvim/menu.c index ab28eeca1c..aa76b8010d 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -13,6 +13,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -60,7 +61,7 @@ static const char e_nomenu[] = N_("E329: No menu \"%s\""); static bool menu_is_winbar(const char *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return (strncmp(name, "WinBar", 6) == 0); + return (strncmp(name, S_LEN("WinBar")) == 0); } static vimmenu_T **get_root_menu(const char *const name) @@ -89,17 +90,17 @@ void ex_menu(exarg_T *eap) char *arg = eap->arg; while (true) { - if (strncmp(arg, "<script>", 8) == 0) { + if (strncmp(arg, S_LEN("<script>")) == 0) { noremap = REMAP_SCRIPT; arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<silent>", 8) == 0) { + if (strncmp(arg, S_LEN("<silent>")) == 0) { silent = true; arg = skipwhite(arg + 8); continue; } - if (strncmp(arg, "<special>", 9) == 0) { + if (strncmp(arg, S_LEN("<special>")) == 0) { // Ignore obsolete "<special>" modifier. arg = skipwhite(arg + 9); continue; @@ -109,7 +110,7 @@ void ex_menu(exarg_T *eap) // Locate an optional "icon=filename" argument // TODO(nvim): Currently this is only parsed. Should expose it to UIs. - if (strncmp(arg, "icon=", 5) == 0) { + if (strncmp(arg, S_LEN("icon=")) == 0) { arg += 5; while (*arg != NUL && *arg != ' ') { if (*arg == '\\') { @@ -152,10 +153,10 @@ void ex_menu(exarg_T *eap) pri_tab[MENUDEPTH] = -1; // mark end of the table // Check for "disable" or "enable" argument. - if (strncmp(arg, "enable", 6) == 0 && ascii_iswhite(arg[6])) { + if (strncmp(arg, S_LEN("enable")) == 0 && ascii_iswhite(arg[6])) { enable = kTrue; arg = skipwhite(arg + 6); - } else if (strncmp(arg, "disable", 7) == 0 && ascii_iswhite(arg[7])) { + } else if (strncmp(arg, S_LEN("disable")) == 0 && ascii_iswhite(arg[7])) { enable = kFalse; arg = skipwhite(arg + 7); } @@ -888,10 +889,10 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for } if (!ascii_iswhite(*p)) { - if (strncmp(arg, "enable", 6) == 0 + if (strncmp(arg, S_LEN("enable")) == 0 && (arg[6] == NUL || ascii_iswhite(arg[6]))) { p = arg + 6; - } else if (strncmp(arg, "disable", 7) == 0 + } else if (strncmp(arg, S_LEN("disable")) == 0 && (arg[7] == NUL || ascii_iswhite(arg[7]))) { p = arg + 7; } else { @@ -1056,7 +1057,7 @@ char *get_menu_names(expand_T *xp, int idx) } // hack on menu separators: use a 'magic' char for the separator // so that '.' in names gets escaped properly - STRCAT(tbuffer, "\001"); + strcat(tbuffer, "\001"); str = tbuffer; } else { if (should_advance) { @@ -1332,14 +1333,14 @@ bool menu_is_menubar(const char *const name) bool menu_is_popup(const char *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return strncmp(name, "PopUp", 5) == 0; + return strncmp(name, S_LEN("PopUp")) == 0; } // Return true if "name" is a toolbar menu name. bool menu_is_toolbar(const char *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return strncmp(name, "ToolBar", 7) == 0; + return strncmp(name, S_LEN("ToolBar")) == 0; } /// @return true if the name is a menu separator identifier: Starts and ends @@ -1700,7 +1701,7 @@ void ex_menutranslate(exarg_T *eap) } // ":menutrans clear": clear all translations. - if (strncmp(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5))) { + if (strncmp(arg, S_LEN("clear")) == 0 && ends_excmd(*skipwhite(arg + 5))) { GA_DEEP_CLEAR(&menutrans_ga, menutrans_T, FREE_MENUTRANS); // Delete all "menutrans_" global variables. diff --git a/src/nvim/message.c b/src/nvim/message.c index 10b90bde29..86e25915a3 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -18,6 +18,7 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -472,7 +473,7 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen) } } else if (e + 3 < buflen) { // set the middle and copy the last part - memmove(buf + e, "...", 3); + memmove(buf + e, S_LEN("...")); len = (int)strlen(s + i) + 1; if (len >= buflen - e - 3) { len = buflen - e - 3 - 1; @@ -2689,7 +2690,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) *p++ = '\r'; } memcpy(p, s, (size_t)len); - *(p + len) = '\0'; + *(p + len) = NUL; if (info_message) { printf("%s", buf); } else { @@ -3181,7 +3182,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen) if (*s != '\n' && *s != '\r') { while (cur_col < msg_col) { if (capture_ga) { - ga_concat_len(capture_ga, " ", 1); + ga_concat_len(capture_ga, S_LEN(" ")); } if (redir_reg) { write_reg_contents(redir_reg, " ", 1, true); diff --git a/src/nvim/move.c b/src/nvim/move.c index 418ece09ed..a2bb1b4685 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -20,6 +20,7 @@ #include "nvim/diff.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/eval/window.h" #include "nvim/fold.h" diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 5737a0440f..6a0dc10214 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -31,8 +31,6 @@ #include "nvim/msgpack_rpc/packer.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/os/input.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_client.h" @@ -89,7 +87,7 @@ void rpc_start(Channel *channel) kv_init(rpc->call_stack); if (channel->streamtype != kChannelStreamInternal) { - Stream *out = channel_outstream(channel); + RStream *out = channel_outstream(channel); #ifdef NVIM_LOG_DEBUG Stream *in = channel_instream(channel); DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id, @@ -202,10 +200,25 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem return frame.errored ? NIL : frame.result; } -static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, bool eof) +static size_t receive_msgpack(RStream *stream, const char *rbuf, size_t c, void *data, bool eof) { Channel *channel = data; channel_incref(channel); + size_t consumed = 0; + + DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p", + channel->id, c, (void *)stream); + + if (c > 0) { + Unpacker *p = channel->rpc.unpacker; + p->read_ptr = rbuf; + p->read_size = c; + parse_msgpack(channel); + + if (!unpacker_closed(p)) { + consumed = c - p->read_size; + } + } if (eof) { channel_close(channel->id, kChannelPartRpc, NULL); @@ -213,25 +226,10 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client", channel->id); chan_close_with_error(channel, buf, LOGLVL_INF); - goto end; - } - - DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p", - channel->id, rbuffer_size(rbuf), (void *)stream); - - Unpacker *p = channel->rpc.unpacker; - size_t size = 0; - p->read_ptr = rbuffer_read_ptr(rbuf, &size); - p->read_size = size; - parse_msgpack(channel); - - if (!unpacker_closed(p)) { - size_t consumed = size - p->read_size; - rbuffer_consumed_compact(rbuf, consumed); } -end: channel_decref(channel); + return consumed; } static ChannelCallFrame *find_call_frame(RpcState *rpc, uint32_t request_id) diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c index cac68f76f0..9c0d2910fa 100644 --- a/src/nvim/msgpack_rpc/packer.c +++ b/src/nvim/msgpack_rpc/packer.c @@ -113,7 +113,6 @@ void mpack_handle(ObjectType type, handle_T handle, PackerBuffer *packer) mpack_w(&packer->ptr, 0xc7); mpack_w(&packer->ptr, packsize); mpack_w(&packer->ptr, exttype); - // check_buffer(packer); memcpy(packer->ptr, buf, (size_t)packsize); packer->ptr += packsize; } diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 56b03d67d0..24bd343a34 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -4,6 +4,7 @@ #include <string.h> #include <uv.h> +#include "nvim/ascii_defs.h" #include "nvim/channel.h" #include "nvim/eval.h" #include "nvim/event/defs.h" @@ -131,7 +132,7 @@ bool server_owns_pipe_address(const char *path) /// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen. int server_start(const char *addr) { - if (addr == NULL || addr[0] == '\0') { + if (addr == NULL || addr[0] == NUL) { WLOG("Empty or NULL address"); return 1; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 8ba375f29d..3343803bb1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -28,6 +28,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -1986,7 +1987,7 @@ bool add_to_showcmd(int c) size_t overflow = old_len + extra_len - limit; memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1); } - STRCAT(showcmd_buf, p); + strcat(showcmd_buf, p); if (char_avail()) { return false; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5c1e291ac6..8f8baaf619 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -24,6 +24,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" @@ -281,8 +282,10 @@ void op_shift(oparg_T *oap, bool curs_top, int amount) /// @param call_changed_bytes call changed_bytes() void shift_line(bool left, bool round, int amount, int call_changed_bytes) { - const int sw_val = get_sw_value_indent(curbuf); - + int sw_val = get_sw_value_indent(curbuf, left); + if (sw_val == 0) { + sw_val = 1; // shouldn't happen, just in case + } int count = get_indent(); // get current indent if (round) { // round off indent @@ -327,7 +330,7 @@ static void shift_block(oparg_T *oap, int amount) const int oldstate = State; char *newp; const int oldcol = curwin->w_cursor.col; - const int sw_val = get_sw_value_indent(curbuf); + const int sw_val = get_sw_value_indent(curbuf, left); const int ts_val = (int)curbuf->b_p_ts; struct block_def bd; int incr; @@ -2663,7 +2666,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) 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]); + strcat(pnew, reg->y_array[0]); xfree(curr->y_array[j]); xfree(reg->y_array[0]); curr->y_array[j++] = pnew; @@ -3428,7 +3431,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) 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); + strcat(newp, ptr); // insert second line ml_append(lnum, newp, 0, false); new_lnum++; @@ -4722,7 +4725,7 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0'; } - buf2[i] = '\0'; + buf2[i] = NUL; } else if (pre == 0) { vim_snprintf(buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); } else if (pre == '0') { @@ -4744,7 +4747,7 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } } *ptr = NUL; - STRCAT(buf1, buf2); + strcat(buf1, buf2); ins_str(buf1); // insert the new number endpos = curwin->w_cursor; if (curwin->w_cursor.col) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 799513e018..4c68e2bf16 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -44,6 +44,7 @@ #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/vars.h" @@ -235,11 +236,11 @@ static void set_init_default_backupskip(void) == NULL) { ga_grow(&ga, (int)len); if (!GA_EMPTY(&ga)) { - STRCAT(ga.ga_data, ","); + strcat(ga.ga_data, ","); } - STRCAT(ga.ga_data, p); + strcat(ga.ga_data, p); add_pathsep(ga.ga_data); - STRCAT(ga.ga_data, "*"); + strcat(ga.ga_data, "*"); ga.ga_len += (int)len; } xfree(item); @@ -346,7 +347,7 @@ void set_init_1(bool clean_arg) const size_t backupdir_len = strlen(backupdir); backupdir = xrealloc(backupdir, backupdir_len + 3); memmove(backupdir + 2, backupdir, backupdir_len + 1); - memmove(backupdir, ".,", 2); + memmove(backupdir, S_LEN(".,")); set_string_default(kOptBackupdir, backupdir, true); set_string_default(kOptViewdir, stdpaths_user_state_subpath("view", 2, true), true); @@ -1003,10 +1004,10 @@ static set_op_T get_op(const char *arg) static set_prefix_T get_option_prefix(char **argp) { - if (strncmp(*argp, "no", 2) == 0) { + if (strncmp(*argp, S_LEN("no")) == 0) { *argp += 2; return PREFIX_NO; - } else if (strncmp(*argp, "inv", 3) == 0) { + } else if (strncmp(*argp, S_LEN("inv")) == 0) { *argp += 3; return PREFIX_INV; } @@ -1425,7 +1426,7 @@ int do_set(char *arg, int opt_flags) did_show = true; } else { while (*arg != NUL) { // loop to process all options - if (strncmp(arg, "all", 3) == 0 && !ASCII_ISALPHA(arg[3]) + if (strncmp(arg, S_LEN("all")) == 0 && !ASCII_ISALPHA(arg[3]) && !(opt_flags & OPT_MODELINE)) { // ":set all" show all options. // ":set all&" set all options to their default value. @@ -2770,7 +2771,7 @@ static void do_spelllang_source(win_T *win) char *q = win->w_s->b_p_spl; // Skip the first name if it is "cjk". - if (strncmp(q, "cjk,", 4) == 0) { + if (strncmp(q, S_LEN("cjk,")) == 0) { q += 4; } @@ -4569,6 +4570,8 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) return &(buf->b_p_def); case PV_INC: return &(buf->b_p_inc); + case PV_COT: + return &(buf->b_p_cot); case PV_DICT: return &(buf->b_p_dict); case PV_TSR: @@ -4652,6 +4655,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var; case PV_INC: return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var; + case PV_COT: + return *buf->b_p_cot != NUL ? &(buf->b_p_cot) : p->var; case PV_DICT: return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var; case PV_TSR: @@ -5331,6 +5336,8 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_inc = empty_string_option; buf->b_p_inex = xstrdup(p_inex); COPY_OPT_SCTX(buf, BV_INEX); + 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; @@ -5443,11 +5450,11 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } p--; } - if (strncmp(p, "no", 2) == 0) { + if (strncmp(p, S_LEN("no")) == 0) { xp->xp_context = EXPAND_BOOL_SETTINGS; xp->xp_prefix = XP_PREFIX_NO; p += 2; - } else if (strncmp(p, "inv", 3) == 0) { + } else if (strncmp(p, S_LEN("inv")) == 0) { xp->xp_context = EXPAND_BOOL_SETTINGS; xp->xp_prefix = XP_PREFIX_INV; p += 3; @@ -5642,7 +5649,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) // manually handle it here to make sure we have the correct xp_context set. // for 'spellsuggest' start at "file:" if (options[opt_idx].var == &p_sps) { - if (strncmp(xp->xp_pattern, "file:", 5) == 0) { + if (strncmp(xp->xp_pattern, S_LEN("file:")) == 0) { xp->xp_pattern += 5; return; } else if (options[expand_option_idx].opt_expand_cb != NULL) { @@ -6074,16 +6081,16 @@ int fill_culopt_flags(char *val, win_T *wp) } while (*p != NUL) { // Note: Keep this in sync with p_culopt_values. - if (strncmp(p, "line", 4) == 0) { + if (strncmp(p, S_LEN("line")) == 0) { p += 4; culopt_flags_new |= CULOPT_LINE; - } else if (strncmp(p, "both", 4) == 0) { + } else if (strncmp(p, S_LEN("both")) == 0) { p += 4; culopt_flags_new |= CULOPT_LINE | CULOPT_NBR; - } else if (strncmp(p, "number", 6) == 0) { + } else if (strncmp(p, S_LEN("number")) == 0) { p += 6; culopt_flags_new |= CULOPT_NBR; - } else if (strncmp(p, "screenline", 10) == 0) { + } else if (strncmp(p, S_LEN("screenline")) == 0) { p += 10; culopt_flags_new |= CULOPT_SCRLINE; } @@ -6131,8 +6138,8 @@ int option_set_callback_func(char *optval, Callback *optcb) typval_T *tv; if (*optval == '{' - || (strncmp(optval, "function(", 9) == 0) - || (strncmp(optval, "funcref(", 8) == 0)) { + || (strncmp(optval, S_LEN("function(")) == 0) + || (strncmp(optval, S_LEN("funcref(")) == 0)) { // Lambda expression or a funcref tv = eval_expr(optval, NULL); if (tv == NULL) { @@ -6185,10 +6192,10 @@ bool can_bs(int what) return vim_strchr(p_bs, what) != NULL; } -/// Get the local or global value of 'backupcopy'. +/// Get the local or global value of 'backupcopy' flags. /// /// @param buf The buffer. -unsigned get_bkc_value(buf_T *buf) +unsigned get_bkc_flags(buf_T *buf) { return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags; } @@ -6204,7 +6211,7 @@ char *get_flp_value(buf_T *buf) return buf->b_p_flp; } -/// Get the local or global value of the 'virtualedit' flags. +/// Get the local or global value of 'virtualedit' flags. unsigned get_ve_flags(win_T *wp) { return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index c98c84d34e..bd0fe699d9 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -430,6 +430,19 @@ EXTERN char *p_cpt; ///< 'complete' EXTERN OptInt p_columns; ///< 'columns' EXTERN int p_confirm; ///< 'confirm' EXTERN char *p_cot; ///< 'completeopt' +EXTERN unsigned cot_flags; ///< flags from 'completeopt' +// Keep in sync with p_cot_values in optionstr.c +#define COT_MENU 0x001 +#define COT_MENUONE 0x002 +#define COT_ANY_MENU 0x003 // combination of menu flags +#define COT_LONGEST 0x004 // false: insert full match, + // true: insert longest prefix +#define COT_PREVIEW 0x008 +#define COT_POPUP 0x010 +#define COT_ANY_PREVIEW 0x018 // combination of preview flags +#define COT_NOINSERT 0x020 // false: select & insert, true: noinsert +#define COT_NOSELECT 0x040 // false: select & insert, true: noselect +#define COT_FUZZY 0x080 // true: fuzzy match enabled #ifdef BACKSLASH_IN_FILENAME EXTERN char *p_csl; ///< 'completeslash' #endif diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 452cc6876b..f323926015 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1324,8 +1324,8 @@ return { defaults = { if_true = '' }, desc = [=[ A template for a comment. The "%s" in the value is replaced with the - comment text. For example, C uses "/*%s*/". Used for |commenting| and to - add markers for folding, see |fold-marker|. + comment text, and should be padded with a space when possible. + Used for |commenting| and to add markers for folding, see |fold-marker|. ]=], full_name = 'commentstring', redraw = { 'curswant' }, @@ -1443,6 +1443,10 @@ return { completion in the preview window. Only works in combination with "menu" or "menuone". + popup Show extra information about the currently selected + completion in a popup window. Only works in combination + with "menu" or "menuone". Overrides "preview". + noinsert Do not insert any text for a match until the user selects a match from the menu. Only works in combination with "menu" or "menuone". No effect if "longest" is present. @@ -1451,14 +1455,18 @@ return { select one from the menu. Only works in combination with "menu" or "menuone". - popup Show extra information about the currently selected - completion in a popup window. Only works in combination - with "menu" or "menuone". Overrides "preview". + fuzzy Enable |fuzzy-matching| for completion candidates. This + allows for more flexible and intuitive matching, where + characters can be skipped and matches can be found even + if the exact sequence is not typed. Only makes a + difference how completion candidates are reduced from the + list of alternatives, but not how the candidates are + collected (using different completion types). ]=], expand_cb = 'expand_set_completeopt', full_name = 'completeopt', list = 'onecomma', - scope = { 'global' }, + scope = { 'global', 'buffer' }, short_desc = N_('options for Insert mode completion'), type = 'string', varname = 'p_cot', @@ -7317,8 +7325,8 @@ return { message; also for quickfix message (e.g., ":cn") s don't give "search hit BOTTOM, continuing at TOP" or *shm-s* "search hit TOP, continuing at BOTTOM" messages; when using - the search count do not show "W" after the count message (see - S below) + the search count do not show "W" before the count message + (see |shm-S| below) t truncate file message at the start if it is too long *shm-t* to fit on the command-line, "<" will appear in the left most column; ignored in Ex mode @@ -7340,7 +7348,11 @@ return { `:silent` was used for the command; note that this also affects messages from 'autoread' reloading S do not show search count message when searching, e.g. *shm-S* - "[1/5]" + "[1/5]". When the "S" flag is not present (e.g. search count + is shown), the "search hit BOTTOM, continuing at TOP" and + "search hit TOP, continuing at BOTTOM" messages are only + indicated by a "W" (Mnemonic: Wrapped) letter before the + search count statistics. This gives you the opportunity to avoid that a change between buffers requires you to hit <Enter>, but still gives as useful a message as diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 29433ddbb5..503aa7f350 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -14,6 +14,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" @@ -121,8 +122,8 @@ static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL }; static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent", "syntax", "diff", NULL }; static char *(p_fcl_values[]) = { "all", NULL }; -static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect", - "popup", NULL }; +static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "popup", + "noinsert", "noselect", "fuzzy", NULL }; #ifdef BACKSLASH_IN_FILENAME static char *(p_csl_values[]) = { "slash", "backslash", NULL }; #endif @@ -157,6 +158,7 @@ void didset_string_options(void) opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true); opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true); opt_strings_flags(p_bo, p_bo_values, &bo_flags, true); + opt_strings_flags(p_cot, p_cot_values, &cot_flags, true); opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true); opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); @@ -218,6 +220,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_ft); check_string_option(&buf->b_p_cinw); check_string_option(&buf->b_p_cinsd); + check_string_option(&buf->b_p_cot); check_string_option(&buf->b_p_cpt); check_string_option(&buf->b_p_cfu); check_string_option(&buf->b_p_ofu); @@ -285,15 +288,15 @@ int check_signcolumn(win_T *wp) } if (check_opt_strings(val, p_scl_values, false) == OK) { - if (!strncmp(val, "no", 2)) { // no + if (!strncmp(val, S_LEN("no"))) { // no wp->w_minscwidth = wp->w_maxscwidth = SCL_NO; - } else if (!strncmp(val, "nu", 2) && (wp->w_p_nu || wp->w_p_rnu)) { // number + } else if (!strncmp(val, S_LEN("nu")) && (wp->w_p_nu || wp->w_p_rnu)) { // number wp->w_minscwidth = wp->w_maxscwidth = SCL_NUM; - } else if (!strncmp(val, "yes:", 4)) { // yes:<NUM> + } else if (!strncmp(val, S_LEN("yes:"))) { // yes:<NUM> wp->w_minscwidth = wp->w_maxscwidth = val[4] - '0'; } else if (*val == 'y') { // yes wp->w_minscwidth = wp->w_maxscwidth = 1; - } else if (!strncmp(val, "auto:", 5)) { // auto:<NUM> + } else if (!strncmp(val, S_LEN("auto:"))) { // auto:<NUM> wp->w_minscwidth = 0; wp->w_maxscwidth = val[5] - '0'; } else { // auto @@ -303,7 +306,7 @@ int check_signcolumn(win_T *wp) return OK; } - if (strncmp(val, "auto:", 5) != 0 + if (strncmp(val, S_LEN("auto:")) != 0 || strlen(val) != 8 || !ascii_isdigit(val[5]) || val[6] != '-' @@ -992,10 +995,23 @@ int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches) /// The 'completeopt' option is changed. const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) { - if (check_opt_strings(p_cot, p_cot_values, true) != OK) { + buf_T *buf = (buf_T *)args->os_buf; + char *cot = p_cot; + unsigned *flags = &cot_flags; + + if (args->os_flags & OPT_LOCAL) { + cot = buf->b_p_cot; + flags = &buf->b_cot_flags; + } + + if (check_opt_strings(cot, p_cot_values, true) != OK) { return e_invarg; } - completeopt_was_set(); + + if (opt_strings_flags(cot, p_cot_values, flags, true) != OK) { + return e_invarg; + } + return NULL; } @@ -1716,9 +1732,9 @@ const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) OptInt *direction; - if (memcmp(string, "ver:", 4) == 0) { + if (memcmp(string, S_LEN("ver:")) == 0) { direction = &vertical; - } else if (memcmp(string, "hor:", 4) == 0) { + } else if (memcmp(string, S_LEN("hor:")) == 0) { direction = &horizontal; } else { return e_invarg; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 5a79004c41..4689414559 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -63,13 +63,13 @@ const char *os_getenv(const char *name) FUNC_ATTR_NONNULL_ALL { char *e = NULL; - if (name[0] == '\0') { + if (name[0] == NUL) { return NULL; } int r = 0; if (map_has(cstr_t, &envmap, name) && !!(e = (char *)pmap_get(cstr_t)(&envmap, name))) { - if (e[0] != '\0') { + if (e[0] != NUL) { // Found non-empty cached env var. // NOTE: This risks incoherence if an in-process library changes the // environment without going through our os_setenv() wrapper. If @@ -85,11 +85,11 @@ const char *os_getenv(const char *name) if (r == UV_ENOBUFS) { e = xmalloc(size); r = uv_os_getenv(name, e, &size); - if (r != 0 || size == 0 || e[0] == '\0') { + if (r != 0 || size == 0 || e[0] == NUL) { XFREE_CLEAR(e); goto end; } - } else if (r != 0 || size == 0 || buf[0] == '\0') { + } else if (r != 0 || size == 0 || buf[0] == NUL) { e = NULL; goto end; } else { @@ -110,7 +110,7 @@ end: bool os_env_exists(const char *name) FUNC_ATTR_NONNULL_ALL { - if (name[0] == '\0') { + if (name[0] == NUL) { return false; } // Use a tiny buffer because we don't care about the value: if uv_os_getenv() @@ -134,14 +134,14 @@ bool os_env_exists(const char *name) int os_setenv(const char *name, const char *value, int overwrite) FUNC_ATTR_NONNULL_ALL { - if (name[0] == '\0') { + if (name[0] == NUL) { return -1; } #ifdef MSWIN if (!overwrite && os_getenv(name) != NULL) { return 0; } - if (value[0] == '\0') { + if (value[0] == NUL) { // Windows (Vim-compat): Empty string undefines the env var. return os_unsetenv(name); } @@ -174,7 +174,7 @@ int os_setenv(const char *name, const char *value, int overwrite) int os_unsetenv(const char *name) FUNC_ATTR_NONNULL_ALL { - if (name[0] == '\0') { + if (name[0] == NUL) { return -1; } pmap_del2(&envmap, name); @@ -366,7 +366,7 @@ void os_get_hostname(char *hostname, size_t size) struct utsname vutsname; if (uname(&vutsname) < 0) { - *hostname = '\0'; + *hostname = NUL; } else { xstrlcpy(hostname, vutsname.nodename, size); } @@ -374,12 +374,12 @@ void os_get_hostname(char *hostname, size_t size) wchar_t host_utf16[MAX_COMPUTERNAME_LENGTH + 1]; DWORD host_wsize = sizeof(host_utf16) / sizeof(host_utf16[0]); if (GetComputerNameW(host_utf16, &host_wsize) == 0) { - *hostname = '\0'; + *hostname = NUL; DWORD err = GetLastError(); semsg("GetComputerNameW failed: %d", err); return; } - host_utf16[host_wsize] = '\0'; + host_utf16[host_wsize] = NUL; char *host_utf8; int conversion_result = utf16_to_utf8(host_utf16, -1, &host_utf8); @@ -391,7 +391,7 @@ void os_get_hostname(char *hostname, size_t size) xfree(host_utf8); #else emsg("os_get_hostname failed: missing uname()"); - *hostname = '\0'; + *hostname = NUL; #endif } @@ -885,9 +885,9 @@ void vim_get_prefix_from_exepath(char *exe_name) // but c_grammar.lua does not recognize it (yet). xstrlcpy(exe_name, get_vim_var_str(VV_PROGPATH), MAXPATHL * sizeof(*exe_name)); char *path_end = path_tail_with_sep(exe_name); - *path_end = '\0'; // remove the trailing "nvim.exe" + *path_end = NUL; // remove the trailing "nvim.exe" path_end = path_tail(exe_name); - *path_end = '\0'; // remove the trailing "bin/" + *path_end = NUL; // remove the trailing "bin/" } /// Vim getenv() wrapper with special handling of $HOME, $VIM, $VIMRUNTIME, diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index e58eb96c2e..585c4964e2 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -21,8 +21,6 @@ #include "nvim/os/fileio.h" #include "nvim/os/fs.h" #include "nvim/os/os_defs.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/types_defs.h" #ifdef HAVE_SYS_UIO_H @@ -120,12 +118,9 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const int flags) assert(!ret_fp->wr || !ret_fp->non_blocking); ret_fp->fd = fd; ret_fp->eof = false; - ret_fp->rv = rbuffer_new(kRWBufferSize); - ret_fp->_error = 0; - if (ret_fp->wr) { - ret_fp->rv->data = ret_fp; - ret_fp->rv->full_cb = (rbuffer_callback)&file_rb_write_full_cb; - } + ret_fp->buffer = alloc_block(); + ret_fp->read_pos = ret_fp->buffer; + ret_fp->write_pos = ret_fp->buffer; ret_fp->bytes_read = 0; return 0; } @@ -148,8 +143,9 @@ void file_open_buffer(FileDescriptor *ret_fp, char *data, size_t len) ret_fp->non_blocking = false; ret_fp->fd = -1; ret_fp->eof = true; - ret_fp->rv = rbuffer_new_wrap_buf(data, len); - ret_fp->_error = 0; + ret_fp->buffer = NULL; // we don't take ownership + ret_fp->read_pos = data; + ret_fp->write_pos = data + len; ret_fp->bytes_read = 0; } @@ -163,36 +159,18 @@ int file_close(FileDescriptor *const fp, const bool do_fsync) FUNC_ATTR_NONNULL_ALL { if (fp->fd < 0) { - rbuffer_free(fp->rv); return 0; } const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp)); const int close_error = os_close(fp->fd); - rbuffer_free(fp->rv); + free_block(fp->buffer); if (close_error != 0) { return close_error; } return flush_error; } -/// Flush file modifications to disk -/// -/// @param[in,out] fp File to work with. -/// -/// @return 0 or error code. -int file_flush(FileDescriptor *const fp) - FUNC_ATTR_NONNULL_ALL -{ - if (!fp->wr) { - return 0; - } - file_rb_write_full_cb(fp->rv, fp); - const int error = fp->_error; - fp->_error = 0; - return error; -} - /// Flush file modifications to disk and run fsync() /// /// @param[in,out] fp File to work with. @@ -218,36 +196,29 @@ int file_fsync(FileDescriptor *const fp) return 0; } -/// Buffer used for writing -/// -/// Like IObuff, but allows file_\* callers not to care about spoiling it. -static char writebuf[kRWBufferSize]; - -/// Function run when RBuffer is full when writing to a file -/// -/// Actually does writing to the file, may also be invoked directly. +/// Flush file modifications to disk /// -/// @param[in,out] rv RBuffer instance used. /// @param[in,out] fp File to work with. -static void file_rb_write_full_cb(RBuffer *const rv, void *const fp_in) +/// +/// @return 0 or error code. +int file_flush(FileDescriptor *fp) FUNC_ATTR_NONNULL_ALL { - FileDescriptor *const fp = fp_in; - assert(fp->wr); - assert(rv->data == (void *)fp); - if (rbuffer_size(rv) == 0) { - return; + if (!fp->wr) { + return 0; + } + + ptrdiff_t to_write = fp->write_pos - fp->read_pos; + if (to_write == 0) { + return 0; } - const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize); - const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes, + const ptrdiff_t wres = os_write(fp->fd, fp->read_pos, (size_t)to_write, fp->non_blocking); - if (wres != (ptrdiff_t)read_bytes) { - if (wres >= 0) { - fp->_error = UV_EIO; - } else { - fp->_error = (int)wres; - } + fp->read_pos = fp->write_pos = fp->buffer; + if (wres != to_write) { + return (wres >= 0) ? UV_EIO : (int)wres; } + return 0; } /// Read from file @@ -262,77 +233,78 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, const size_t FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { assert(!fp->wr); - char *buf = ret_buf; - size_t read_remaining = size; - RBuffer *const rv = fp->rv; + size_t from_buffer = MIN((size_t)(fp->write_pos - fp->read_pos), size); + memcpy(ret_buf, fp->read_pos, from_buffer); + + char *buf = ret_buf + from_buffer; + size_t read_remaining = size - from_buffer; + if (!read_remaining) { + fp->bytes_read += from_buffer; + fp->read_pos += from_buffer; + return (ptrdiff_t)from_buffer; + } + + // at this point, we have consumed all of an existing buffer. restart from the beginning + fp->read_pos = fp->write_pos = fp->buffer; + +#ifdef HAVE_READV bool called_read = false; while (read_remaining) { - const size_t rv_size = rbuffer_size(rv); - if (rv_size > 0) { - const size_t rsize = rbuffer_read(rv, buf, MIN(rv_size, read_remaining)); - buf += rsize; - read_remaining -= rsize; - } - if (fp->eof - // Allow only at most one os_read[v] call. - || (called_read && fp->non_blocking)) { + // Allow only at most one os_read[v] call. + if (fp->eof || (called_read && fp->non_blocking)) { break; } - if (read_remaining) { - assert(rbuffer_size(rv) == 0); - rbuffer_reset(rv); -#ifdef HAVE_READV - // If there is readv() syscall, then take an opportunity to populate - // both target buffer and RBuffer at once, … - size_t write_count; - struct iovec iov[] = { - { .iov_base = buf, .iov_len = read_remaining }, - { .iov_base = rbuffer_write_ptr(rv, &write_count), - .iov_len = kRWBufferSize }, - }; - assert(write_count == kRWBufferSize); - const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov, - ARRAY_SIZE(iov), fp->non_blocking); - if (r_ret > 0) { - if (r_ret > (ptrdiff_t)read_remaining) { - rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining)); - read_remaining = 0; - } else { - buf += (size_t)r_ret; - read_remaining -= (size_t)r_ret; - } - } else if (r_ret < 0) { - return r_ret; - } -#else - if (read_remaining >= kRWBufferSize) { - // …otherwise leave RBuffer empty and populate only target buffer, - // because filtering information through rbuffer will be more syscalls. - const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining, - fp->non_blocking); - if (r_ret >= 0) { - read_remaining -= (size_t)r_ret; - fp->bytes_read += (size - read_remaining); - return (ptrdiff_t)(size - read_remaining); - } else if (r_ret < 0) { - return r_ret; - } + // If there is readv() syscall, then take an opportunity to populate + // both target buffer and RBuffer at once, … + struct iovec iov[] = { + { .iov_base = buf, .iov_len = read_remaining }, + { .iov_base = fp->write_pos, + .iov_len = ARENA_BLOCK_SIZE }, + }; + const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov, + ARRAY_SIZE(iov), fp->non_blocking); + if (r_ret > 0) { + if (r_ret > (ptrdiff_t)read_remaining) { + fp->write_pos += (size_t)(r_ret - (ptrdiff_t)read_remaining); + read_remaining = 0; } else { - size_t write_count; - const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, - rbuffer_write_ptr(rv, &write_count), - kRWBufferSize, fp->non_blocking); - assert(write_count == kRWBufferSize); - if (r_ret > 0) { - rbuffer_produced(rv, (size_t)r_ret); - } else if (r_ret < 0) { - return r_ret; - } + buf += r_ret; + read_remaining -= (size_t)r_ret; } -#endif - called_read = true; + } else if (r_ret < 0) { + return r_ret; + } + called_read = true; + } +#else + if (fp->eof) { + // already eof, cannot read more + } else if (read_remaining >= ARENA_BLOCK_SIZE) { + // …otherwise leave fp->buffer empty and populate only target buffer, + // because filtering information through rbuffer will be more syscalls. + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining, + fp->non_blocking); + if (r_ret >= 0) { + read_remaining -= (size_t)r_ret; + } else if (r_ret < 0) { + return r_ret; + } + } else { + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, + fp->write_pos, + ARENA_BLOCK_SIZE, fp->non_blocking); + if (r_ret < 0) { + return r_ret; + } else { + fp->write_pos += r_ret; + size_t to_copy = MIN((size_t)r_ret, read_remaining); + memcpy(buf, fp->read_pos, to_copy); + fp->read_pos += to_copy; + read_remaining -= to_copy; } } +#endif + fp->bytes_read += (size - read_remaining); return (ptrdiff_t)(size - read_remaining); } @@ -348,40 +320,68 @@ ptrdiff_t file_write(FileDescriptor *const fp, const char *const buf, const size FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { assert(fp->wr); - const size_t written = rbuffer_write(fp->rv, buf, size); - if (fp->_error != 0) { - const int error = fp->_error; - fp->_error = 0; - return error; - } else if (written != size) { - return UV_EIO; + ptrdiff_t space = (fp->buffer + ARENA_BLOCK_SIZE) - fp->write_pos; + // includes the trivial case of size==0 + if (size < (size_t)space) { + memcpy(fp->write_pos, buf, size); + fp->write_pos += size; + return (ptrdiff_t)size; + } + + // TODO(bfredl): just as for reading, use iovec to combine fp->buffer with buf + int status = file_flush(fp); + if (status < 0) { + return status; + } + + if (size < ARENA_BLOCK_SIZE) { + memcpy(fp->write_pos, buf, size); + fp->write_pos += size; + return (ptrdiff_t)size; } - return (ptrdiff_t)written; -} -/// Buffer used for skipping. Its contents is undefined and should never be -/// used. -static char skipbuf[kRWBufferSize]; + const ptrdiff_t wres = os_write(fp->fd, buf, size, + fp->non_blocking); + return (wres != (ptrdiff_t)size && wres >= 0) ? UV_EIO : wres; +} /// Skip some bytes /// /// This is like `fseek(fp, size, SEEK_CUR)`, but actual implementation simply -/// reads to a buffer and discards the result. +/// reads to the buffer and discards the result. ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size) FUNC_ATTR_NONNULL_ALL { assert(!fp->wr); - size_t read_bytes = 0; - do { - const ptrdiff_t new_read_bytes = - file_read(fp, skipbuf, MIN(size - read_bytes, sizeof(skipbuf))); - if (new_read_bytes < 0) { - return new_read_bytes; - } else if (new_read_bytes == 0) { + size_t from_buffer = MIN((size_t)(fp->write_pos - fp->read_pos), size); + size_t skip_remaining = size - from_buffer; + if (skip_remaining == 0) { + fp->read_pos += from_buffer; + fp->bytes_read += from_buffer; + return (ptrdiff_t)from_buffer; + } + + fp->read_pos = fp->write_pos = fp->buffer; + bool called_read = false; + while (skip_remaining > 0) { + // Allow only at most one os_read[v] call. + if (fp->eof || (called_read && fp->non_blocking)) { break; } - read_bytes += (size_t)new_read_bytes; - } while (read_bytes < size && !file_eof(fp)); + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, fp->buffer, ARENA_BLOCK_SIZE, + fp->non_blocking); + if (r_ret < 0) { + return r_ret; + } else if ((size_t)r_ret > skip_remaining) { + fp->read_pos = fp->buffer + skip_remaining; + fp->write_pos = fp->buffer + r_ret; + fp->bytes_read += size; + return (ptrdiff_t)size; + } + skip_remaining -= (size_t)r_ret; + called_read = true; + } - return (ptrdiff_t)read_bytes; + fp->bytes_read += size - skip_remaining; + return (ptrdiff_t)(size - skip_remaining); } diff --git a/src/nvim/os/fileio_defs.h b/src/nvim/os/fileio_defs.h index 10277d2a7a..0f76fdb2aa 100644 --- a/src/nvim/os/fileio_defs.h +++ b/src/nvim/os/fileio_defs.h @@ -4,13 +4,13 @@ #include <stdint.h> #include "nvim/func_attr.h" -#include "nvim/rbuffer_defs.h" /// Structure used to read from/write to file typedef struct { - int fd; ///< File descriptor. - int _error; ///< Error code for use with RBuffer callbacks or zero. - RBuffer *rv; ///< Read or write buffer. + int fd; ///< File descriptor. Can be -1 if no backing file (file_open_buffer) + char *buffer; ///< Read or write buffer. always ARENA_BLOCK_SIZE if allocated + char *read_pos; ///< read position in buffer + char *write_pos; ///< write position in buffer bool wr; ///< True if file is in write mode. bool eof; ///< True if end of file was encountered. bool non_blocking; ///< True if EAGAIN should not restart syscalls. @@ -28,7 +28,7 @@ static inline bool file_eof(const FileDescriptor *fp) /// performed. static inline bool file_eof(const FileDescriptor *const fp) { - return fp->eof && rbuffer_size(fp->rv) == 0; + return fp->eof && fp->read_pos == fp->write_pos; } static inline int file_fd(const FileDescriptor *fp) diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 19bdf30311..b681b2f832 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -17,6 +17,7 @@ #endif #include "auto/config.h" +#include "nvim/errors.h" #include "nvim/os/fs.h" #include "nvim/os/os_defs.h" @@ -301,7 +302,7 @@ static bool is_executable_ext(const char *name, char **abspath) char *nameext = strrchr(name, '.'); size_t nameext_len = nameext ? strlen(nameext) : 0; xstrlcpy(os_buf, name, sizeof(os_buf)); - char *buf_end = xstrchrnul(os_buf, '\0'); + char *buf_end = xstrchrnul(os_buf, NUL); const char *pathext = os_getenv("PATHEXT"); if (!pathext) { pathext = ".com;.exe;.bat;.cmd"; @@ -309,7 +310,7 @@ static bool is_executable_ext(const char *name, char **abspath) const char *ext = pathext; while (*ext) { // If $PATHEXT itself contains dot: - if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) { + if (ext[0] == '.' && (ext[1] == NUL || ext[1] == ENV_SEPCHAR)) { if (is_executable(name, abspath)) { return true; } @@ -435,7 +436,7 @@ FILE *os_fopen(const char *path, const char *flags) assert(flags != NULL && strlen(flags) > 0 && strlen(flags) <= 2); int iflags = 0; // Per table in fopen(3) manpage. - if (flags[1] == '\0' || flags[1] == 'b') { + if (flags[1] == NUL || flags[1] == 'b') { switch (flags[0]) { case 'r': iflags = O_RDONLY; diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 60b5b48745..ea21a32230 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -27,13 +27,11 @@ #include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/profile.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/state.h" #include "nvim/state_defs.h" #define READ_BUFFER_SIZE 0xfff -#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4) +#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN) typedef enum { kInputNone, @@ -41,8 +39,11 @@ typedef enum { kInputEof, } InbufPollResult; -static Stream read_stream = { .closed = true }; // Input before UI starts. -static RBuffer *input_buffer = NULL; +static RStream read_stream = { .s.closed = true }; // Input before UI starts. +static char input_buffer[INPUT_BUFFER_SIZE]; +static char *input_read_pos = input_buffer; +static char *input_write_pos = input_buffer; + static bool input_eof = false; static bool blocking = false; static int cursorhold_time = 0; ///< time waiting for CursorHold event @@ -52,38 +53,26 @@ static int cursorhold_tb_change_cnt = 0; ///< tb_change_cnt when waiting starte # include "os/input.c.generated.h" #endif -void input_init(void) -{ - input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN); -} - void input_start(void) { - if (!read_stream.closed) { + if (!read_stream.s.closed) { return; } used_stdin = true; - rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO, READ_BUFFER_SIZE); + rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO); rstream_start(&read_stream, input_read_cb, NULL); } void input_stop(void) { - if (read_stream.closed) { + if (read_stream.s.closed) { return; } rstream_stop(&read_stream); - stream_close(&read_stream, NULL, NULL); -} - -#ifdef EXITFREE -void input_free_all_mem(void) -{ - rbuffer_free(input_buffer); + rstream_may_close(&read_stream); } -#endif static void cursorhold_event(void **argv) { @@ -119,9 +108,12 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e restart_cursorhold_wait(tb_change_cnt); } - if (maxlen && rbuffer_size(input_buffer)) { + if (maxlen && input_available()) { restart_cursorhold_wait(tb_change_cnt); - return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); + size_t to_read = MIN((size_t)maxlen, input_available()); + memcpy(buf, input_read_pos, to_read); + input_read_pos += to_read; + return (int)to_read; } // No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped. @@ -138,7 +130,7 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e uint64_t wait_start = os_hrtime(); cursorhold_time = MIN(cursorhold_time, (int)p_ut); if ((result = inbuf_poll((int)p_ut - cursorhold_time, events)) == kInputNone) { - if (read_stream.closed && silent_mode) { + if (read_stream.s.closed && silent_mode) { // Drained eventloop & initial input; exit silent/batch-mode (-es/-Es). read_error_exit(); } @@ -161,11 +153,14 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e return 0; } - if (maxlen && rbuffer_size(input_buffer)) { + if (maxlen && input_available()) { restart_cursorhold_wait(tb_change_cnt); - // Safe to convert rbuffer_read to int, it will never overflow since we use - // relatively small buffers. - return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); + // Safe to convert `to_read` to int, it will never overflow since + // INPUT_BUFFER_SIZE fits in an int + size_t to_read = MIN((size_t)maxlen, input_available()); + memcpy(buf, input_read_pos, to_read); + input_read_pos += to_read; + return (int)to_read; } // If there are events, return the keys directly @@ -247,11 +242,28 @@ bool os_isatty(int fd) return uv_guess_handle(fd) == UV_TTY; } -void input_enqueue_raw(String keys) +size_t input_available(void) { - if (keys.size > 0) { - rbuffer_write(input_buffer, keys.data, keys.size); + return (size_t)(input_write_pos - input_read_pos); +} + +static size_t input_space(void) +{ + return (size_t)(input_buffer + INPUT_BUFFER_SIZE - input_write_pos); +} + +void input_enqueue_raw(const char *data, size_t size) +{ + if (input_read_pos > input_buffer) { + size_t available = input_available(); + memmove(input_buffer, input_read_pos, available); + input_read_pos = input_buffer; + input_write_pos = input_buffer + available; } + + size_t to_write = MIN(size, input_space()); + memcpy(input_write_pos, data, to_write); + input_write_pos += to_write; } size_t input_enqueue(String keys) @@ -259,7 +271,7 @@ size_t input_enqueue(String keys) const char *ptr = keys.data; const char *end = ptr + keys.size; - while (rbuffer_space(input_buffer) >= 19 && ptr < end) { + while (input_space() >= 19 && ptr < end) { // A "<x>" form occupies at least 1 characters, and produces up // to 19 characters (1 + 5 * 3 for the char and 3 for a modifier). // In the case of K_SPECIAL (0x80), 3 bytes are escaped and needed, @@ -272,7 +284,7 @@ size_t input_enqueue(String keys) if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); - rbuffer_write(input_buffer, (char *)buf, new_size); + input_enqueue_raw((char *)buf, new_size); continue; } @@ -293,11 +305,11 @@ size_t input_enqueue(String keys) // copy the character, escaping K_SPECIAL if ((uint8_t)(*ptr) == K_SPECIAL) { - rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1); - rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_SPECIAL }, 1); - rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_FILLER }, 1); + input_enqueue_raw((char *)&(uint8_t){ K_SPECIAL }, 1); + input_enqueue_raw((char *)&(uint8_t){ KS_SPECIAL }, 1); + input_enqueue_raw((char *)&(uint8_t){ KE_FILLER }, 1); } else { - rbuffer_write(input_buffer, ptr, 1); + input_enqueue_raw(ptr, 1); } ptr++; } @@ -422,7 +434,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs return bufsize; } -size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col) +void input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col) { modifier |= check_multiclick(code, grid, row, col); uint8_t buf[7]; @@ -442,8 +454,7 @@ size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int co mouse_col = col; size_t written = 3 + (size_t)(p - buf); - rbuffer_write(input_buffer, (char *)buf, written); - return written; + input_enqueue_raw((char *)buf, written); } /// @return true if the main loop is blocked and waiting for input. @@ -484,22 +495,15 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events) return input_eof ? kInputEof : kInputNone; } -bool input_available(void) -{ - return rbuffer_size(input_buffer) != 0; -} - -static void input_read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, bool at_eof) +static size_t input_read_cb(RStream *stream, const char *buf, size_t c, void *data, bool at_eof) { if (at_eof) { input_eof = true; } - assert(rbuffer_space(input_buffer) >= rbuffer_size(buf)); - RBUFFER_UNTIL_EMPTY(buf, ptr, len) { - (void)rbuffer_write(input_buffer, ptr, len); - rbuffer_consumed(buf, len); - } + assert(input_space() >= c); + input_enqueue_raw(buf, c); + return c; } static void process_ctrl_c(void) @@ -508,23 +512,24 @@ static void process_ctrl_c(void) return; } - size_t consume_count = 0; - RBUFFER_EACH_REVERSE(input_buffer, c, i) { - if ((uint8_t)c == Ctrl_C - || ((uint8_t)c == 'C' && i >= 3 - && (uint8_t)(*rbuffer_get(input_buffer, i - 3)) == K_SPECIAL - && (uint8_t)(*rbuffer_get(input_buffer, i - 2)) == KS_MODIFIER - && (uint8_t)(*rbuffer_get(input_buffer, i - 1)) == MOD_MASK_CTRL)) { - *rbuffer_get(input_buffer, i) = Ctrl_C; + size_t available = input_available(); + ssize_t i; + for (i = (ssize_t)available - 1; i >= 0; i--) { + uint8_t c = (uint8_t)input_read_pos[i]; + if (c == Ctrl_C + || (c == 'C' && i >= 3 + && (uint8_t)input_read_pos[i - 3] == K_SPECIAL + && (uint8_t)input_read_pos[i - 2] == KS_MODIFIER + && (uint8_t)input_read_pos[i - 1] == MOD_MASK_CTRL)) { + input_read_pos[i] = Ctrl_C; got_int = true; - consume_count = i; break; } } - if (got_int && consume_count) { + if (got_int && i > 0) { // Remove all unprocessed input (typeahead) before the CTRL-C. - rbuffer_consumed(input_buffer, consume_count); + input_read_pos += i; } } @@ -548,7 +553,7 @@ static int push_event_key(uint8_t *buf, int maxlen) bool os_input_ready(MultiQueue *events) { return (typebuf_was_filled // API call filled typeahead - || rbuffer_size(input_buffer) // Input buffer filled + || input_available() // Input buffer filled || pending_events(events)); // Events must be processed } diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 4d34e8fac4..cfa4dcada7 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -169,7 +169,7 @@ int pty_process_spawn(PtyProcess *ptyproc) int status = 0; // zero or negative error code (libuv convention) Process *proc = (Process *)ptyproc; - assert(proc->err.closed); + assert(proc->err.s.closed); uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; uv_disable_stdio_inheritance(); @@ -208,8 +208,8 @@ int pty_process_spawn(PtyProcess *ptyproc) && (status = set_duplicating_descriptor(master, &proc->in.uv.pipe))) { goto error; } - if (!proc->out.closed - && (status = set_duplicating_descriptor(master, &proc->out.uv.pipe))) { + if (!proc->out.s.closed + && (status = set_duplicating_descriptor(master, &proc->out.s.uv.pipe))) { goto error; } diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 12831ff05f..39c3966c1c 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -55,7 +55,7 @@ int pty_process_spawn(PtyProcess *ptyproc) wchar_t *env = NULL; const char *emsg = NULL; - assert(proc->err.closed); + assert(proc->err.s.closed); if (!os_has_conpty_working() || (conpty_object = os_conpty_init(&in_name, &out_name, ptyproc->width, @@ -72,10 +72,10 @@ int pty_process_spawn(PtyProcess *ptyproc) pty_process_connect_cb); } - if (!proc->out.closed) { + if (!proc->out.s.closed) { out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect(out_req, - &proc->out.uv.pipe, + &proc->out.s.uv.pipe, out_name, pty_process_connect_cb); } @@ -216,7 +216,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) Process *proc = (Process *)ptyproc; assert(ptyproc->finish_wait != NULL); - if (proc->out.closed || proc->out.did_eof || !uv_is_readable(proc->out.uvstream)) { + if (proc->out.s.closed || proc->out.did_eof || !uv_is_readable(proc->out.s.uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } @@ -399,7 +399,7 @@ static int build_env_block(dict_T *denv, wchar_t **env_block) QUEUE_INSERT_TAIL(&env_q, &env_node->node); } - // Additional '\0' after the final entry + // Additional NUL after the final entry env_block_len++; *env_block = xmalloc(sizeof(**env_block) * env_block_len); diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 2a10510b0f..4b34ed1c4e 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -10,6 +10,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/event/defs.h" @@ -39,8 +40,6 @@ #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" -#include "nvim/rbuffer.h" -#include "nvim/rbuffer_defs.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/tag.h" @@ -48,17 +47,11 @@ #include "nvim/ui.h" #include "nvim/vim_defs.h" -#define DYNAMIC_BUFFER_INIT { NULL, 0, 0 } #define NS_1_SECOND 1000000000U // 1 second, in nanoseconds #define OUT_DATA_THRESHOLD 1024 * 10U // 10KB, "a few screenfuls" of data. #define SHELL_SPECIAL "\t \"&'$;<>()\\|" -typedef struct { - char *data; - size_t cap, len; -} DynamicBuffer; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/shell.c.generated.h" #endif @@ -255,11 +248,11 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } else { STRCPY(command, "("); } - STRCAT(command, pat[0] + 1); // exclude first backtick + strcat(command, pat[0] + 1); // exclude first backtick p = command + strlen(command) - 1; if (is_fish_shell) { *p-- = ';'; - STRCAT(command, " end"); + strcat(command, " end"); } else { *p-- = ')'; // remove last backtick } @@ -270,7 +263,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in ampersand = true; *p = ' '; } - STRCAT(command, ">"); + strcat(command, ">"); } else { STRCPY(command, ""); if (shell_style == STYLE_GLOB) { @@ -278,26 +271,26 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in // otherwise, this may set the positional parameters for the shell, // e.g. "$*". if (flags & EW_NOTFOUND) { - STRCAT(command, "set nonomatch; "); + strcat(command, "set nonomatch; "); } else { - STRCAT(command, "unset nonomatch; "); + strcat(command, "unset nonomatch; "); } } if (shell_style == STYLE_GLOB) { - STRCAT(command, "glob >"); + strcat(command, "glob >"); } else if (shell_style == STYLE_PRINT) { - STRCAT(command, "print -N >"); + strcat(command, "print -N >"); } else if (shell_style == STYLE_VIMGLOB) { - STRCAT(command, sh_vimglob_func); + strcat(command, sh_vimglob_func); } else if (shell_style == STYLE_GLOBSTAR) { - STRCAT(command, sh_globstar_opt); - STRCAT(command, sh_vimglob_func); + strcat(command, sh_globstar_opt); + strcat(command, sh_vimglob_func); } else { - STRCAT(command, "echo >"); + strcat(command, "echo >"); } } - STRCAT(command, tempname); + strcat(command, tempname); if (shell_style != STYLE_BT) { for (i = 0; i < num_pat; i++) { @@ -341,7 +334,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } if (ampersand) { - STRCAT(command, "&"); // put the '&' after the redirection + strcat(command, "&"); // put the '&' after the redirection } // Using zsh -G: If a pattern has no matches, it is just deleted from @@ -647,13 +640,13 @@ char *shell_argv_to_str(char **const argv) p++; } if (n < maxsize) { - rv[n - 1] = '\0'; + rv[n - 1] = NUL; } else { // Command too long, show ellipsis: "/bin/bash 'foo' 'bar'..." rv[maxsize - 4] = '.'; rv[maxsize - 3] = '.'; rv[maxsize - 2] = '.'; - rv[maxsize - 1] = '\0'; + rv[maxsize - 1] = NUL; } return rv; } @@ -668,7 +661,7 @@ char *shell_argv_to_str(char **const argv) /// @return shell command exit code int os_call_shell(char *cmd, ShellOpts opts, char *extra_args) { - DynamicBuffer input = DYNAMIC_BUFFER_INIT; + StringBuilder input = KV_INITIAL_VALUE; char *output = NULL; char **output_ptr = NULL; int current_state = State; @@ -697,9 +690,9 @@ int os_call_shell(char *cmd, ShellOpts opts, char *extra_args) size_t nread; int exitcode = do_os_system(shell_build_argv(cmd, extra_args), - input.data, input.len, output_ptr, &nread, + input.items, input.size, output_ptr, &nread, emsg_silent, forward_output); - xfree(input.data); + kv_destroy(input); if (output) { write_output(output, nread, true); @@ -860,10 +853,10 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu { out_data_decide_throttle(0); // Initialize throttle decider. out_data_ring(NULL, 0); // Initialize output ring-buffer. - bool has_input = (input != NULL && input[0] != '\0'); + bool has_input = (input != NULL && input[0] != NUL); // the output buffer - DynamicBuffer buf = DYNAMIC_BUFFER_INIT; + StringBuilder buf = KV_INITIAL_VALUE; stream_read_cb data_cb = system_data_cb; if (nread) { *nread = 0; @@ -906,9 +899,9 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu if (has_input) { wstream_init(&proc->in, 0); } - rstream_init(&proc->out, 0); + rstream_init(&proc->out); rstream_start(&proc->out, data_cb, &buf); - rstream_init(&proc->err, 0); + rstream_init(&proc->err); rstream_start(&proc->err, data_cb, &buf); // write the input, if any @@ -951,18 +944,17 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu // prepare the out parameters if requested if (output) { - if (buf.len == 0) { + assert(nread); + if (buf.size == 0) { // no data received from the process, return NULL *output = NULL; - xfree(buf.data); + *nread = 0; + kv_destroy(buf); } else { + *nread = buf.size; // NUL-terminate to make the output directly usable as a C string - buf.data[buf.len] = NUL; - *output = buf.data; - } - - if (nread) { - *nread = buf.len; + kv_push(buf, NUL); + *output = buf.items; } } @@ -972,29 +964,11 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu return exitcode; } -/// - ensures at least `desired` bytes in buffer -/// -/// TODO(aktau): fold with kvec/garray -static void dynamic_buffer_ensure(DynamicBuffer *buf, size_t desired) -{ - if (buf->cap >= desired) { - assert(buf->data); - return; - } - - buf->cap = desired; - kv_roundup32(buf->cap); - buf->data = xrealloc(buf->data, buf->cap); -} - -static void system_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) +static size_t system_data_cb(RStream *stream, const char *buf, size_t count, void *data, bool eof) { - DynamicBuffer *dbuf = data; - - size_t nread = buf->size; - dynamic_buffer_ensure(dbuf, dbuf->len + nread + 1); - rbuffer_read(buf, dbuf->data + dbuf->len, nread); - dbuf->len += nread; + StringBuilder *dbuf = data; + kv_concat_len(*dbuf, buf, count); + return count; } /// Tracks output received for the current executing shell command, and displays @@ -1023,7 +997,7 @@ static bool out_data_decide_throttle(size_t size) static uint64_t started = 0; // Start time of the current throttle. static size_t received = 0; // Bytes observed since last throttle. static size_t visit = 0; // "Pulse" count of the current throttle. - static char pulse_msg[] = { ' ', ' ', ' ', '\0' }; + static char pulse_msg[] = { ' ', ' ', ' ', NUL }; if (!size) { bool previous_decision = (visit > 0); @@ -1077,7 +1051,7 @@ static bool out_data_decide_throttle(size_t size) /// /// @param output Data to save, or NULL to invoke a special mode. /// @param size Length of `output`. -static void out_data_ring(char *output, size_t size) +static void out_data_ring(const char *output, size_t size) { #define MAX_CHUNK_SIZE (OUT_DATA_THRESHOLD / 2) static char last_skipped[MAX_CHUNK_SIZE]; // Saved output. @@ -1119,11 +1093,11 @@ static void out_data_ring(char *output, size_t size) /// @param output Data to append to screen lines. /// @param count Size of data. /// @param eof If true, there will be no more data output. -static void out_data_append_to_screen(char *output, size_t *count, bool eof) +static void out_data_append_to_screen(const char *output, size_t *count, bool eof) FUNC_ATTR_NONNULL_ALL { - char *p = output; - char *end = output + *count; + const char *p = output; + const char *end = output + *count; while (p < end) { if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) { msg_putchar_attr((uint8_t)(*p), 0); @@ -1151,25 +1125,16 @@ end: ui_flush(); } -static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) +static size_t out_data_cb(RStream *stream, const char *ptr, size_t count, void *data, bool eof) { - size_t cnt; - char *ptr = rbuffer_read_ptr(buf, &cnt); - - if (ptr != NULL && cnt > 0 - && out_data_decide_throttle(cnt)) { // Skip output above a threshold. + if (count > 0 && out_data_decide_throttle(count)) { // Skip output above a threshold. // Save the skipped output. If it is the final chunk, we display it later. - out_data_ring(ptr, cnt); - } else if (ptr != NULL) { - out_data_append_to_screen(ptr, &cnt, eof); - } - - if (cnt) { - rbuffer_consumed(buf, cnt); + out_data_ring(ptr, count); + } else if (count > 0) { + out_data_append_to_screen(ptr, &count, eof); } - // Move remaining data to start of buffer, so the buffer can never wrap around. - rbuffer_reset(buf); + return count; } /// Parses a command string into a sequence of words, taking quotes into @@ -1233,7 +1198,7 @@ static size_t word_length(const char *str) /// event loop starts. If we don't (by writing in chunks returned by `ml_get`) /// the buffer being modified might get modified by reading from the process /// before we finish writing. -static void read_input(DynamicBuffer *buf) +static void read_input(StringBuilder *buf) { size_t written = 0; size_t len = 0; @@ -1247,14 +1212,11 @@ static void read_input(DynamicBuffer *buf) } else if (lp[written] == NL) { // NL -> NUL translation len = 1; - dynamic_buffer_ensure(buf, buf->len + len); - buf->data[buf->len++] = NUL; + kv_push(*buf, NUL); } else { char *s = vim_strchr(lp + written, NL); len = s == NULL ? l : (size_t)(s - (lp + written)); - dynamic_buffer_ensure(buf, buf->len + len); - memcpy(buf->data + buf->len, lp + written, len); - buf->len += len; + kv_concat_len(*buf, lp + written, len); } if (len == l) { @@ -1263,8 +1225,7 @@ static void read_input(DynamicBuffer *buf) || (!curbuf->b_p_bin && curbuf->b_p_fixeol) || (lnum != curbuf->b_no_eol_lnum && (lnum != curbuf->b_ml.ml_line_count || curbuf->b_p_eol))) { - dynamic_buffer_ensure(buf, buf->len + 1); - buf->data[buf->len++] = NL; + kv_push(*buf, NL); } lnum++; if (lnum > curbuf->b_op_end.lnum) { @@ -1331,7 +1292,7 @@ static void shell_write_cb(Stream *stream, void *data, int status) msg_schedule_semsg(_("E5677: Error writing input to shell-command: %s"), uv_err_name(status)); } - stream_close(stream, NULL, NULL); + stream_close(stream, NULL, NULL, false); } /// Applies 'shellxescape' (p_sxe) and 'shellxquote' (p_sxq) to a command. diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index e5bdd56fe6..e4435bcaa8 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -69,7 +69,7 @@ static const char *const xdg_defaults[] = { const char *get_appname(void) { const char *env_val = os_getenv("NVIM_APPNAME"); - if (env_val == NULL || *env_val == '\0') { + if (env_val == NULL || *env_val == NUL) { env_val = "nvim"; } return env_val; diff --git a/src/nvim/path.c b/src/nvim/path.c index d782d1a989..aa630038a8 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -962,7 +962,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) char *file_pattern = xmalloc(len + 2); file_pattern[0] = '*'; file_pattern[1] = NUL; - STRCAT(file_pattern, pattern); + strcat(file_pattern, pattern); char *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true); xfree(file_pattern); if (pat == NULL) { @@ -1065,7 +1065,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) rel_path = xmalloc(strlen(short_name) + strlen(PATHSEPSTR) + 2); STRCPY(rel_path, "."); add_pathsep(rel_path); - STRCAT(rel_path, short_name); + strcat(rel_path, short_name); xfree(fnames[i]); fnames[i] = rel_path; diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5881d34c48..f273f88dd1 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -5,6 +5,7 @@ #include <stdint.h> #include <string.h> +#include "nvim/api/extmark.h" #include "nvim/ascii_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" @@ -158,7 +159,7 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco break; } else if (mark.pos.col == col) { if (!mt_end(mark) && (mark.flags & MT_FLAG_DECOR_VIRT_TEXT_INLINE) - && mt_scoped_in_win(mark, wp)) { + && ns_in_win(mark.ns, wp)) { DecorInline decor = mt_decor(mark); DecorVirtText *vt = decor.ext ? decor.data.ext.vt : NULL; while (vt) { diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 86f3611ec5..324254a188 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -19,6 +19,7 @@ #include "nvim/charset.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" @@ -699,7 +700,7 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma } // delete the empty last line ml_delete_buf(buf, buf->b_ml.ml_line_count, false); - if (strstr(p_cot, "popup") != NULL) { + if (get_cot_flags() & COT_POPUP) { extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo); } } @@ -794,7 +795,8 @@ static bool pum_set_selected(int n, int repeat) int prev_selected = pum_selected; pum_selected = n; - bool use_float = strstr(p_cot, "popup") != NULL; + unsigned cur_cot_flags = get_cot_flags(); + bool use_float = (cur_cot_flags & COT_POPUP) != 0; // when new leader add and info window is shown and no selected we still // need use the first index item to update the info float window position. bool force_select = use_float && pum_selected < 0 && win_float_find_preview(); @@ -860,7 +862,7 @@ static bool pum_set_selected(int n, int repeat) if ((pum_array[pum_selected].pum_info != NULL) && (Rows > 10) && (repeat <= 1) - && (vim_strchr(p_cot, 'p') != NULL)) { + && (cur_cot_flags & COT_ANY_PREVIEW)) { win_T *curwin_save = curwin; tabpage_T *curtab_save = curtab; diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h index 20a342b841..9e3f8f5a7f 100644 --- a/src/nvim/popupmenu.h +++ b/src/nvim/popupmenu.h @@ -10,10 +10,11 @@ /// Used for popup menu items. typedef struct { - char *pum_text; // main menu text - char *pum_kind; // extra kind text (may be truncated) - char *pum_extra; // extra menu text (may be truncated) - char *pum_info; // extra info + char *pum_text; ///< main menu text + char *pum_kind; ///< extra kind text (may be truncated) + char *pum_extra; ///< extra menu text (may be truncated) + char *pum_info; ///< extra info + int pum_score; ///< fuzzy match score } pumitem_T; EXTERN ScreenGrid pum_grid INIT( = SCREEN_GRID_INIT); diff --git a/src/nvim/profile.c b/src/nvim/profile.c index b88b08d3f0..f5e8e013ac 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" @@ -293,7 +294,7 @@ void ex_profile(exarg_T *eap) int len = (int)(e - eap->arg); e = skipwhite(e); - if (len == 5 && strncmp(eap->arg, "start", 5) == 0 && *e != NUL) { + if (len == 5 && strncmp(eap->arg, S_LEN("start")) == 0 && *e != NUL) { xfree(profile_fname); profile_fname = expand_env_save_opt(e, true); do_profiling = PROF_YES; @@ -368,12 +369,12 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg) return; } - if ((end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) - || (end_subcmd - arg == 4 && strncmp(arg, "file", 4) == 0)) { + if ((end_subcmd - arg == 5 && strncmp(arg, S_LEN("start")) == 0) + || (end_subcmd - arg == 4 && strncmp(arg, S_LEN("file")) == 0)) { xp->xp_context = EXPAND_FILES; xp->xp_pattern = skipwhite(end_subcmd); return; - } else if (end_subcmd - arg == 4 && strncmp(arg, "func", 4) == 0) { + } else if (end_subcmd - arg == 4 && strncmp(arg, S_LEN("func")) == 0) { xp->xp_context = EXPAND_USER_FUNC; xp->xp_pattern = skipwhite(end_subcmd); return; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index e022184f2f..ed3fd83fd5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -20,6 +20,7 @@ #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/window.h" @@ -723,7 +724,7 @@ static int qf_get_next_str_line(qfstate_T *state) state->linelen = len; } memcpy(state->linebuf, p_str, state->linelen); - state->linebuf[state->linelen] = '\0'; + state->linebuf[state->linelen] = NUL; // Increment using len in order to discard the rest of the line if it // exceeds LINE_MAXLEN. @@ -4530,7 +4531,7 @@ static char *get_mef_name(void) name = xmalloc(strlen(p_mef) + 30); STRCPY(name, p_mef); snprintf(name + (p - p_mef), strlen(name), "%d%d", start, off); - STRCAT(name, p + 2); + strcat(name, p + 2); // Don't accept a symbolic link, it's a security risk. FileInfo file_info; bool file_or_link_found = os_fileinfo_link(name, &file_info); @@ -7236,7 +7237,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p // Find all "*.txt" and "*.??x" files in the "doc" directory. add_pathsep(dirname); - STRCAT(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT + strcat(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT if (gen_expand_wildcards(1, &dirname, &fcount, &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { for (int fi = 0; fi < fcount && !got_int; fi++) { diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c deleted file mode 100644 index cf2e10f90d..0000000000 --- a/src/nvim/rbuffer.c +++ /dev/null @@ -1,247 +0,0 @@ -#include <assert.h> -#include <stdbool.h> -#include <stddef.h> -#include <string.h> - -#include "nvim/macros_defs.h" -#include "nvim/memory.h" -#include "nvim/rbuffer.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "rbuffer.c.generated.h" -#endif - -/// Creates a new `RBuffer` instance. -RBuffer *rbuffer_new(size_t capacity) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET -{ - if (!capacity) { - capacity = 0x10000; - } - - RBuffer *rv = xcalloc(1, sizeof(RBuffer) + capacity); - rv->full_cb = rv->nonfull_cb = NULL; - rv->data = NULL; - rv->size = 0; - rv->write_ptr = rv->read_ptr = rv->start_ptr; - rv->end_ptr = rv->start_ptr + capacity; - rv->temp = NULL; - return rv; -} - -/// Creates a new `RBuffer` instance for reading from a buffer. -/// -/// Must not be used with any write function like rbuffer_write_ptr or rbuffer_produced! -RBuffer *rbuffer_new_wrap_buf(char *data, size_t len) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET -{ - RBuffer *rv = xcalloc(1, sizeof(RBuffer)); - rv->full_cb = rv->nonfull_cb = NULL; - rv->data = NULL; - rv->size = len; - rv->read_ptr = data; - rv->write_ptr = data + len; - rv->end_ptr = NULL; - rv->temp = NULL; - return rv; -} - -void rbuffer_free(RBuffer *buf) FUNC_ATTR_NONNULL_ALL -{ - xfree(buf->temp); - xfree(buf); -} - -/// Return a pointer to a raw buffer containing the first empty slot available -/// for writing. The second argument is a pointer to the maximum number of -/// bytes that could be written. -/// -/// It is necessary to call this function twice to ensure all empty space was -/// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task. -char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL -{ - if (buf->size == rbuffer_capacity(buf)) { - *write_count = 0; - return NULL; - } - - if (buf->write_ptr >= buf->read_ptr) { - *write_count = (size_t)(buf->end_ptr - buf->write_ptr); - } else { - *write_count = (size_t)(buf->read_ptr - buf->write_ptr); - } - - return buf->write_ptr; -} - -// Reset an RBuffer so read_ptr is at the beginning of the memory. If -// necessary, this moves existing data by allocating temporary memory. -void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL -{ - size_t temp_size; - if ((temp_size = rbuffer_size(buf))) { - if (buf->temp == NULL) { - buf->temp = xcalloc(1, rbuffer_capacity(buf)); - } - rbuffer_read(buf, buf->temp, buf->size); - } - buf->read_ptr = buf->write_ptr = buf->start_ptr; - if (temp_size) { - rbuffer_write(buf, buf->temp, temp_size); - } -} - -/// Adjust `rbuffer` write pointer to reflect produced data. This is called -/// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr` -/// directly, this needs to called after the data was copied to the internal -/// buffer. The write pointer will be wrapped if required. -void rbuffer_produced(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL -{ - assert(count && count <= rbuffer_space(buf)); - - buf->write_ptr += count; - if (buf->write_ptr >= buf->end_ptr) { - // wrap around - buf->write_ptr -= rbuffer_capacity(buf); - } - - buf->size += count; - if (buf->full_cb && !rbuffer_space(buf)) { - buf->full_cb(buf, buf->data); - } -} - -/// Return a pointer to a raw buffer containing the first byte available -/// for reading. The second argument is a pointer to the maximum number of -/// bytes that could be read. -/// -/// It is necessary to call this function twice to ensure all available bytes -/// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task. -char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL -{ - if (!buf->size) { - *read_count = 0; - return buf->read_ptr; - } - - if (buf->read_ptr < buf->write_ptr) { - *read_count = (size_t)(buf->write_ptr - buf->read_ptr); - } else { - *read_count = (size_t)(buf->end_ptr - buf->read_ptr); - } - - return buf->read_ptr; -} - -/// Adjust `rbuffer` read pointer to reflect consumed data. This is called -/// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr` -/// directly, this needs to called after the data was copied from the internal -/// buffer. The read pointer will be wrapped if required. -void rbuffer_consumed(RBuffer *buf, size_t count) - FUNC_ATTR_NONNULL_ALL -{ - if (count == 0) { - return; - } - assert(count <= buf->size); - - buf->read_ptr += count; - if (buf->end_ptr && buf->read_ptr >= buf->end_ptr) { - buf->read_ptr -= rbuffer_capacity(buf); - } - - bool was_full = buf->size == rbuffer_capacity(buf); - buf->size -= count; - if (buf->nonfull_cb && was_full) { - buf->nonfull_cb(buf, buf->data); - } -} - -/// Use instead of rbuffer_consumed to use rbuffer in a linear, non-cyclic fashion. -/// -/// This is generally useful if we can guarantee to parse all input -/// except some small incomplete token, like when parsing msgpack. -void rbuffer_consumed_compact(RBuffer *buf, size_t count) - FUNC_ATTR_NONNULL_ALL -{ - assert(buf->read_ptr <= buf->write_ptr); - rbuffer_consumed(buf, count); - if (buf->read_ptr > buf->start_ptr) { - assert((size_t)(buf->write_ptr - buf->read_ptr) == buf->size - || buf->write_ptr == buf->start_ptr); - memmove(buf->start_ptr, buf->read_ptr, buf->size); - buf->read_ptr = buf->start_ptr; - buf->write_ptr = buf->read_ptr + buf->size; - } -} - -// Higher level functions for copying from/to RBuffer instances and data -// pointers -size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size) - FUNC_ATTR_NONNULL_ALL -{ - size_t size = src_size; - - RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { - size_t copy_count = MIN(src_size, wcnt); - memcpy(wptr, src, copy_count); - rbuffer_produced(buf, copy_count); - - if (!(src_size -= copy_count)) { - return size; - } - - src += copy_count; - } - - return size - src_size; -} - -size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size) - FUNC_ATTR_NONNULL_ALL -{ - size_t size = dst_size; - - RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) { - size_t copy_count = MIN(dst_size, rcnt); - memcpy(dst, rptr, copy_count); - rbuffer_consumed(buf, copy_count); - - if (!(dst_size -= copy_count)) { - return size; - } - - dst += copy_count; - } - - return size - dst_size; -} - -char *rbuffer_get(RBuffer *buf, size_t index) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - assert(index < buf->size); - char *rptr = buf->read_ptr + index; - if (rptr >= buf->end_ptr) { - rptr -= rbuffer_capacity(buf); - } - return rptr; -} - -int rbuffer_cmp(RBuffer *buf, const char *str, size_t count) - FUNC_ATTR_NONNULL_ALL -{ - assert(count <= buf->size); - size_t rcnt; - rbuffer_read_ptr(buf, &rcnt); - size_t n = MIN(count, rcnt); - int rv = memcmp(str, buf->read_ptr, n); - count -= n; - size_t remaining = buf->size - rcnt; - - if (rv || !count || !remaining) { - return rv; - } - - return memcmp(str + n, buf->start_ptr, count); -} diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h deleted file mode 100644 index 942e1f2365..0000000000 --- a/src/nvim/rbuffer.h +++ /dev/null @@ -1,71 +0,0 @@ -// Specialized ring buffer. This is basically an array that wraps read/write -// pointers around the memory region. It should be more efficient than the old -// RBuffer which required memmove() calls to relocate read/write positions. -// -// The main purpose of RBuffer is simplify memory management when reading from -// uv_stream_t instances: -// -// - The event loop writes data to a RBuffer, advancing the write pointer -// - The main loop reads data, advancing the read pointer -// - If the buffer becomes full(size == capacity) the rstream is temporarily -// stopped(automatic backpressure handling) -// -// Reference: http://en.wikipedia.org/wiki/Circular_buffer -#pragma once - -#include <stddef.h> -#include <stdint.h> - -#include "nvim/rbuffer_defs.h" // IWYU pragma: keep - -// Macros that simplify working with the read/write pointers directly by hiding -// ring buffer wrap logic. Some examples: -// -// - Pass the write pointer to a function(write_data) that incrementally -// produces data, returning the number of bytes actually written to the -// ring buffer: -// -// RBUFFER_UNTIL_FULL(rbuf, ptr, cnt) -// rbuffer_produced(rbuf, write_data(state, ptr, cnt)); -// -// - Pass the read pointer to a function(read_data) that incrementally -// consumes data, returning the number of bytes actually read from the -// ring buffer: -// -// RBUFFER_UNTIL_EMPTY(rbuf, ptr, cnt) -// rbuffer_consumed(rbuf, read_data(state, ptr, cnt)); -// -// Note that the rbuffer_{produced,consumed} calls are necessary or these macros -// create infinite loops -#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ - for (size_t rcnt = 0, _r = 1; _r; _r = 0) \ - for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \ - buf->size; \ - rptr = rbuffer_read_ptr(buf, &rcnt)) - -#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ - for (size_t wcnt = 0, _r = 1; _r; _r = 0) \ - for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \ - rbuffer_space(buf); \ - wptr = rbuffer_write_ptr(buf, &wcnt)) - -// Iteration -#define RBUFFER_EACH(buf, c, i) \ - for (size_t i = 0; \ - i < buf->size; \ - i = buf->size) \ - for (char c = 0; \ - i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ - i++) - -#define RBUFFER_EACH_REVERSE(buf, c, i) \ - for (size_t i = buf->size; \ - i != SIZE_MAX; \ - i = SIZE_MAX) \ - for (char c = 0; \ - i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ - ) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "rbuffer.h.generated.h" -#endif diff --git a/src/nvim/rbuffer_defs.h b/src/nvim/rbuffer_defs.h deleted file mode 100644 index 51dc349846..0000000000 --- a/src/nvim/rbuffer_defs.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <stddef.h> - -#include "nvim/func_attr.h" - -typedef struct rbuffer RBuffer; -/// Type of function invoked during certain events: -/// - When the RBuffer switches to the full state -/// - When the RBuffer switches to the non-full state -typedef void (*rbuffer_callback)(RBuffer *buf, void *data); - -struct rbuffer { - rbuffer_callback full_cb, nonfull_cb; - void *data; - size_t size; - // helper memory used to by rbuffer_reset if required - char *temp; - char *end_ptr, *read_ptr, *write_ptr; - char start_ptr[]; -}; - -static inline size_t rbuffer_size(RBuffer *buf) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; - -static inline size_t rbuffer_size(RBuffer *buf) -{ - return buf->size; -} - -static inline size_t rbuffer_capacity(RBuffer *buf) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; - -static inline size_t rbuffer_capacity(RBuffer *buf) -{ - return (size_t)(buf->end_ptr - buf->start_ptr); -} - -static inline size_t rbuffer_space(RBuffer *buf) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; - -static inline size_t rbuffer_space(RBuffer *buf) -{ - return rbuffer_capacity(buf) - buf->size; -} diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 5600d6a2f8..e00dbdfc78 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -18,6 +18,7 @@ #include "nvim/ascii_defs.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" @@ -11450,7 +11451,7 @@ static void nfa_set_code(int c) } if (addnl == true) { - STRCAT(code, " + NEWLINE "); + strcat(code, " + NEWLINE "); } } @@ -11494,7 +11495,7 @@ static void nfa_print_state(FILE *debugf, nfa_state_T *state) garray_T indent; ga_init(&indent, 1, 64); - ga_append(&indent, '\0'); + ga_append(&indent, NUL); nfa_print_state2(debugf, state, &indent); ga_clear(&indent); } @@ -11516,7 +11517,7 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent) uint8_t save[2]; strncpy(save, &p[last], 2); // NOLINT(runtime/printf) - memcpy(&p[last], "+-", 2); + memcpy(&p[last], S_LEN("+-")); fprintf(debugf, " %s", p); strncpy(&p[last], save, 2); // NOLINT(runtime/printf) } else { @@ -14800,7 +14801,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm } case NFA_ANY: - // Any char except '\0', (end of input) does not match. + // Any char except NUL, (end of input) does not match. if (curc > 0) { add_state = t->state->out; add_off = clen; @@ -15931,7 +15932,7 @@ regprog_T *vim_regcomp(const char *expr_arg, int re_flags) regexp_engine = (int)p_re; // Check for prefix "\%#=", that sets the regexp engine - if (strncmp(expr, "\\%#=", 4) == 0) { + if (strncmp(expr, S_LEN("\\%#=")) == 0) { int newengine = expr[4] - '0'; if (newengine == AUTOMATIC_ENGINE diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index d913d311db..6728262432 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -22,6 +22,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/debugger.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" @@ -408,7 +409,7 @@ int do_in_path(const char *path, const char *prefix, char *name, int flags, did_one = true; } else if (buflen + 2 + strlen(prefix) + strlen(name) < MAXPATHL) { add_pathsep(buf); - STRCAT(buf, prefix); + strcat(buf, prefix); tail = buf + strlen(buf); // Loop over all patterns in "name" @@ -1320,16 +1321,16 @@ expand: } if (flags & DIP_START) { - memcpy(tail - 15, "pack/*/start/*/", 15); // NOLINT + memcpy(tail - 15, S_LEN("pack/*/start/*/")); // NOLINT globpath(p_pp, tail - 15, gap, glob_flags, expand_dirs); - memcpy(tail - 8, "start/*/", 8); // NOLINT + memcpy(tail - 8, S_LEN("start/*/")); // NOLINT globpath(p_pp, tail - 8, gap, glob_flags, expand_dirs); } if (flags & DIP_OPT) { - memcpy(tail - 13, "pack/*/opt/*/", 13); // NOLINT + memcpy(tail - 13, S_LEN("pack/*/opt/*/")); // NOLINT globpath(p_pp, tail - 13, gap, glob_flags, expand_dirs); - memcpy(tail - 6, "opt/*/", 6); // NOLINT + memcpy(tail - 6, S_LEN("opt/*/")); // NOLINT globpath(p_pp, tail - 6, gap, glob_flags, expand_dirs); } @@ -1876,7 +1877,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, c const char *const line = skipwhite_len(p, len); len -= (size_t)(line - p); // Skip lines starting with '\" ', concat lines starting with '\' - if (len >= 3 && strncmp(line, "\"\\ ", 3) == 0) { + if (len >= 3 && strncmp(line, S_LEN("\"\\ ")) == 0) { return true; } else if (len == 0 || line[0] != '\\') { return false; diff --git a/src/nvim/search.c b/src/nvim/search.c index 746c253708..5318970d44 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -18,6 +18,7 @@ #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" @@ -542,7 +543,7 @@ void last_pat_prog(regmmatch_T *regmatch) return; } emsg_off++; // So it doesn't beep if bad expr - search_regcomp("", 0, NULL, 0, last_idx, SEARCH_KEEP, regmatch); + search_regcomp(S_LEN(""), NULL, 0, last_idx, SEARCH_KEEP, regmatch); emsg_off--; } @@ -1820,9 +1821,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) ptr = skipwhite(linep); if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) { ptr = skipwhite(ptr + 1); - if (strncmp(ptr, "if", 2) == 0 - || strncmp(ptr, "endif", 5) == 0 - || strncmp(ptr, "el", 2) == 0) { + if (strncmp(ptr, S_LEN("if")) == 0 + || strncmp(ptr, S_LEN("endif")) == 0 + || strncmp(ptr, S_LEN("el")) == 0) { hash_dir = 1; } } else if (linep[pos.col] == '/') { // Are we on a comment? @@ -1893,9 +1894,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } if (initc != '#') { ptr = skipwhite(skipwhite(linep) + 1); - if (strncmp(ptr, "if", 2) == 0 || strncmp(ptr, "el", 2) == 0) { + if (strncmp(ptr, S_LEN("if")) == 0 || strncmp(ptr, S_LEN("el")) == 0) { hash_dir = 1; - } else if (strncmp(ptr, "endif", 5) == 0) { + } else if (strncmp(ptr, S_LEN("endif")) == 0) { hash_dir = -1; } else { return NULL; @@ -1920,29 +1921,29 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) pos.col = (colnr_T)(ptr - linep); ptr = skipwhite(ptr + 1); if (hash_dir > 0) { - if (strncmp(ptr, "if", 2) == 0) { + if (strncmp(ptr, S_LEN("if")) == 0) { count++; - } else if (strncmp(ptr, "el", 2) == 0) { + } else if (strncmp(ptr, S_LEN("el")) == 0) { if (count == 0) { return &pos; } - } else if (strncmp(ptr, "endif", 5) == 0) { + } else if (strncmp(ptr, S_LEN("endif")) == 0) { if (count == 0) { return &pos; } count--; } } else { - if (strncmp(ptr, "if", 2) == 0) { + if (strncmp(ptr, S_LEN("if")) == 0) { if (count == 0) { return &pos; } count--; - } else if (initc == '#' && strncmp(ptr, "el", 2) == 0) { + } else if (initc == '#' && strncmp(ptr, S_LEN("el")) == 0) { if (count == 0) { return &pos; } - } else if (strncmp(ptr, "endif", 5) == 0) { + } else if (strncmp(ptr, S_LEN("endif")) == 0) { count++; } } @@ -3890,7 +3891,7 @@ search_line: // is not considered to be a comment line. if (skip_comments) { if ((*line != '#' - || strncmp(skipwhite(line + 1), "define", 6) != 0) + || strncmp(skipwhite(line + 1), S_LEN("define")) != 0) && get_leader_len(line, NULL, false, true)) { matched = false; } diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index d49224a987..5e0aac3f69 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -15,6 +15,7 @@ #include <stdio.h> #include <string.h> +#include "nvim/ascii_defs.h" #include "nvim/memory.h" #include "nvim/sha256.h" @@ -279,7 +280,7 @@ const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, const uin for (size_t j = 0; j < SHA256_SUM_SIZE; j++) { snprintf(hexit + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]); } - hexit[sizeof(hexit) - 1] = '\0'; + hexit[sizeof(hexit) - 1] = NUL; return hexit; } @@ -340,7 +341,7 @@ bool sha256_self_test(void) if (memcmp(output, sha_self_test_vector[i], SHA256_BUFFER_SIZE) != 0) { failures = true; - output[sizeof(output) - 1] = '\0'; + output[sizeof(output) - 1] = NUL; // printf("sha256_self_test %d failed %s\n", i, output); } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 597c7551fd..d5655b1754 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -19,6 +19,7 @@ #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/cmdhist.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/decode.h" #include "nvim/eval/encode.h" diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 4e6672c5dd..f1ded4d594 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -21,6 +21,7 @@ #include "nvim/decoration_defs.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -125,8 +126,8 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr | (has_hl ? MT_FLAG_DECOR_SIGNHL : 0); DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } }; - extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, decor, decor_flags, true, - false, true, true, false, NULL); + extmark_set(buf, ns, id, MIN(buf->b_ml.ml_line_count, lnum) - 1, 0, -1, -1, + decor, decor_flags, true, false, true, true, NULL); } /// For an existing, placed sign with "id", modify the sign, group or priority. @@ -246,12 +247,6 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum) return FAIL; } - // When deleting the last sign need to redraw the windows to remove the - // sign column. Not when curwin is NULL (this means we're exiting). - if (!buf_meta_total(buf, kMTMetaSignText) && curwin != NULL) { - changed_line_abv_curs(); - } - return OK; } @@ -297,8 +292,8 @@ static void sign_list_placed(buf_T *rbuf, char *group) qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp); for (size_t i = 0; i < kv_size(signs); i++) { - namebuf[0] = '\0'; - groupbuf[0] = '\0'; + namebuf[0] = NUL; + groupbuf[0] = NUL; MTKey mark = kv_A(signs, i); DecorSignHighlight *sh = decor_find_sign(mt_decor(mark)); @@ -499,22 +494,11 @@ static void sign_list_by_name(char *name) } } -static void may_force_numberwidth_recompute(buf_T *buf, int unplace) -{ - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf - && (wp->w_p_nu || wp->w_p_rnu) - && (unplace || wp->w_nrwidth_width < 2) - && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) { - wp->w_nrwidth_line_count = 0; - } -} - /// Place a sign at the specified file location or update a sign. static int sign_place(uint32_t *id, char *group, char *name, buf_T *buf, linenr_T lnum, int prio) { // Check for reserved character '*' in group name - if (group != NULL && (*group == '*' || *group == '\0')) { + if (group != NULL && (*group == '*' || *group == NUL)) { return FAIL; } @@ -531,11 +515,7 @@ static int sign_place(uint32_t *id, char *group, char *name, buf_T *buf, linenr_ // ":sign place {id} file={fname}": change sign type and/or priority lnum = buf_mod_sign(buf, id, group, prio, sp); } - if (lnum > 0) { - // When displaying signs in the 'number' column, if the width of the - // number column is less than 2, then force recomputing the width. - may_force_numberwidth_recompute(buf, false); - } else { + if (lnum <= 0) { semsg(_("E885: Not possible to change sign %s"), name); return FAIL; } @@ -562,13 +542,6 @@ static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum) } } - // When all the signs in a buffer are removed, force recomputing the - // number column width (if enabled) in all the windows displaying the - // buffer if 'signcolumn' is set to 'number' in that window. - if (!buf_meta_total(buf, kMTMetaSignText)) { - may_force_numberwidth_recompute(buf, true); - } - return OK; } @@ -637,17 +610,17 @@ static void sign_define_cmd(char *name, char *cmdline) break; } cmdline = skiptowhite_esc(arg); - if (strncmp(arg, "icon=", 5) == 0) { + if (strncmp(arg, S_LEN("icon=")) == 0) { icon = arg + 5; - } else if (strncmp(arg, "text=", 5) == 0) { + } else if (strncmp(arg, S_LEN("text=")) == 0) { text = arg + 5; - } else if (strncmp(arg, "linehl=", 7) == 0) { + } else if (strncmp(arg, S_LEN("linehl=")) == 0) { linehl = arg + 7; - } else if (strncmp(arg, "texthl=", 7) == 0) { + } else if (strncmp(arg, S_LEN("texthl=")) == 0) { texthl = arg + 7; - } else if (strncmp(arg, "culhl=", 6) == 0) { + } else if (strncmp(arg, S_LEN("culhl=")) == 0) { culhl = arg + 6; - } else if (strncmp(arg, "numhl=", 6) == 0) { + } else if (strncmp(arg, S_LEN("numhl=")) == 0) { numhl = arg + 6; } else { semsg(_(e_invarg2), arg); @@ -676,14 +649,14 @@ static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *name, int id, char * // :sign place // :sign place group={group} // :sign place group=* - if (lnum >= 0 || name != NULL || (group != NULL && *group == '\0')) { + if (lnum >= 0 || name != NULL || (group != NULL && *group == NUL)) { emsg(_(e_invarg)); } else { sign_list_placed(buf, group); } } else { // Place a new sign - if (name == NULL || buf == NULL || (group != NULL && *group == '\0')) { + if (name == NULL || buf == NULL || (group != NULL && *group == NUL)) { emsg(_(e_invarg)); return; } @@ -695,7 +668,7 @@ static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *name, int id, char * /// ":sign unplace" command static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, char *group) { - if (lnum >= 0 || name != NULL || (group != NULL && *group == '\0')) { + if (lnum >= 0 || name != NULL || (group != NULL && *group == NUL)) { emsg(_(e_invarg)); return; } @@ -722,7 +695,7 @@ static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, c return; } - if (buf == NULL || (group != NULL && *group == '\0') || lnum >= 0 || name != NULL) { + if (buf == NULL || (group != NULL && *group == NUL) || lnum >= 0 || name != NULL) { // File or buffer is not specified or an empty group is used // or a line number or a sign name is specified. emsg(_(e_invarg)); @@ -755,19 +728,19 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char ** } while (*arg != NUL) { - if (strncmp(arg, "line=", 5) == 0) { + if (strncmp(arg, S_LEN("line=")) == 0) { arg += 5; *lnum = atoi(arg); arg = skiptowhite(arg); lnum_arg = true; - } else if (strncmp(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { + } else if (strncmp(arg, S_LEN("*")) == 0 && cmd == SIGNCMD_UNPLACE) { if (*id != -1) { emsg(_(e_invarg)); return FAIL; } *id = -2; arg = skiptowhite(arg + 1); - } else if (strncmp(arg, "name=", 5) == 0) { + } else if (strncmp(arg, S_LEN("name=")) == 0) { arg += 5; char *namep = arg; arg = skiptowhite(arg); @@ -778,23 +751,23 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char ** namep++; } *name = namep; - } else if (strncmp(arg, "group=", 6) == 0) { + } else if (strncmp(arg, S_LEN("group=")) == 0) { arg += 6; *group = arg; arg = skiptowhite(arg); if (*arg != NUL) { *arg++ = NUL; } - } else if (strncmp(arg, "priority=", 9) == 0) { + } else if (strncmp(arg, S_LEN("priority=")) == 0) { arg += 9; *prio = atoi(arg); arg = skiptowhite(arg); - } else if (strncmp(arg, "file=", 5) == 0) { + } else if (strncmp(arg, S_LEN("file=")) == 0) { arg += 5; filename = arg; *buf = buflist_findname_exp(arg); break; - } else if (strncmp(arg, "buffer=", 7) == 0) { + } else if (strncmp(arg, S_LEN("buffer=")) == 0) { arg += 7; filename = arg; *buf = buflist_findnr(getdigits_int(&arg, true, 0)); @@ -1172,23 +1145,23 @@ void set_context_in_sign_cmd(expand_T *xp, char *arg) xp->xp_pattern = p + 1; switch (cmd_idx) { case SIGNCMD_DEFINE: - if (strncmp(last, "texthl", 6) == 0 - || strncmp(last, "linehl", 6) == 0 - || strncmp(last, "culhl", 5) == 0 - || strncmp(last, "numhl", 5) == 0) { + if (strncmp(last, S_LEN("texthl")) == 0 + || strncmp(last, S_LEN("linehl")) == 0 + || strncmp(last, S_LEN("culhl")) == 0 + || strncmp(last, S_LEN("numhl")) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - } else if (strncmp(last, "icon", 4) == 0) { + } else if (strncmp(last, S_LEN("icon")) == 0) { xp->xp_context = EXPAND_FILES; } else { xp->xp_context = EXPAND_NOTHING; } break; case SIGNCMD_PLACE: - if (strncmp(last, "name", 4) == 0) { + if (strncmp(last, S_LEN("name")) == 0) { expand_what = EXP_SIGN_NAMES; - } else if (strncmp(last, "group", 5) == 0) { + } else if (strncmp(last, S_LEN("group")) == 0) { expand_what = EXP_SIGN_GROUPS; - } else if (strncmp(last, "file", 4) == 0) { + } else if (strncmp(last, S_LEN("file")) == 0) { xp->xp_context = EXPAND_BUFFERS; } else { xp->xp_context = EXPAND_NOTHING; @@ -1196,9 +1169,9 @@ void set_context_in_sign_cmd(expand_T *xp, char *arg) break; case SIGNCMD_UNPLACE: case SIGNCMD_JUMP: - if (strncmp(last, "group", 5) == 0) { + if (strncmp(last, S_LEN("group")) == 0) { expand_what = EXP_SIGN_GROUPS; - } else if (strncmp(last, "file", 4) == 0) { + } else if (strncmp(last, S_LEN("file")) == 0) { xp->xp_context = EXPAND_BUFFERS; } else { xp->xp_context = EXPAND_NOTHING; @@ -1343,7 +1316,7 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (group == NULL) { return; } - if (*group == '\0') { // empty string means global group + if (*group == NUL) { // empty string means global group group = NULL; } } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index d7a6adef58..8ec28c7f61 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -72,6 +72,7 @@ #include "nvim/decoration.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" @@ -230,14 +231,6 @@ char *repl_to = NULL; /// caller can skip over the word. size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount) { - matchinf_T mi; // Most things are put in "mi" so that it can - // be passed to functions quickly. - size_t nrlen = 0; // found a number first - size_t wrongcaplen = 0; - bool count_word = docount; - bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0; - bool is_camel_case = false; - // A word never starts at a space or a control character. Return quickly // then, skipping over the character. if ((uint8_t)(*ptr) <= ' ') { @@ -249,6 +242,13 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount return 1; } + size_t nrlen = 0; // found a number first + size_t wrongcaplen = 0; + bool count_word = docount; + bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0; + bool is_camel_case = false; + + matchinf_T mi; // Most things are put in "mi" so that it can be passed to functions quickly. CLEAR_FIELD(mi); // A number is always OK. Also skip hexadecimal numbers 0xFF99 and @@ -540,7 +540,6 @@ static void find_word(matchinf_T *mip, int mode) int endlen[MAXWLEN]; // length at possible word endings idx_T endidx[MAXWLEN]; // possible word endings int endidxcnt = 0; - int c; // Repeat advancing in the tree until: // - there is a byte that doesn't match, @@ -582,7 +581,7 @@ static void find_word(matchinf_T *mip, int mode) } // Perform a binary search in the list of accepted bytes. - c = (uint8_t)ptr[wlen]; + int c = (uint8_t)ptr[wlen]; if (c == TAB) { // <Tab> is handled like <Space> c = ' '; } @@ -626,9 +625,6 @@ static void find_word(matchinf_T *mip, int mode) } } - char *p; - bool word_ends; - // Verify that one of the possible endings is valid. Try the longest // first. while (endidxcnt > 0) { @@ -639,6 +635,7 @@ static void find_word(matchinf_T *mip, int mode) if (utf_head_off(ptr, ptr + wlen) > 0) { continue; // not at first byte of character } + bool word_ends; if (spell_iswordp(ptr + wlen, mip->mi_win)) { if (slang->sl_compprog == NULL && !slang->sl_nobreak) { continue; // next char is a word character @@ -655,7 +652,7 @@ static void find_word(matchinf_T *mip, int mode) // Compute byte length in original word, length may change // when folding case. This can be slow, take a shortcut when the // case-folded word is equal to the keep-case word. - p = mip->mi_word; + char *p = mip->mi_word; if (strncmp(ptr, p, (size_t)wlen) != 0) { for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { MB_PTR_ADV(p); @@ -691,10 +688,10 @@ static void find_word(matchinf_T *mip, int mode) // When mode is FIND_PREFIX the word must support the prefix: // check the prefix ID and the condition. Do that for the list at // mip->mi_prefarridx that find_prefix() filled. - c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, - (int)flags, - mip->mi_word + mip->mi_cprefixlen, slang, - false); + int c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, + (int)flags, + mip->mi_word + mip->mi_cprefixlen, slang, + false); if (c == 0) { continue; } @@ -765,6 +762,7 @@ static void find_word(matchinf_T *mip, int mode) if (mode == FIND_COMPOUND) { int capflags; + char *p; // Need to check the caps type of the appended compound // word. @@ -851,7 +849,7 @@ static void find_word(matchinf_T *mip, int mode) // byte length in keep-case word. Length may change when // folding case. This can be slow, take a shortcut when // the case-folded word is equal to the keep-case word. - p = mip->mi_fword; + char *p = mip->mi_fword; if (strncmp(ptr, p, (size_t)wlen) != 0) { for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { MB_PTR_ADV(p); @@ -1296,12 +1294,14 @@ static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) /// @return 0 if not found, length of the badly spelled word otherwise. size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *attrp) { + if (no_spell_checking(wp)) { + return 0; + } + pos_T found_pos; size_t found_len = 0; hlf_T attr = HLF_COUNT; - size_t len; bool has_syntax = syntax_present(wp); - colnr_T col; char *buf = NULL; size_t buflen = 0; int skip = 0; @@ -1309,10 +1309,6 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a bool found_one = false; bool wrapped = false; - if (no_spell_checking(wp)) { - return 0; - } - size_t ret = 0; // Start looking for bad word at the start of the line, because we can't @@ -1342,7 +1338,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a while (!got_int) { char *line = ml_get_buf(wp->w_buffer, lnum); - len = (size_t)ml_get_buf_len(wp->w_buffer, lnum); + size_t len = (size_t)ml_get_buf_len(wp->w_buffer, lnum); if (buflen < len + MAXWLEN + 2) { xfree(buf); buflen = len + MAXWLEN + 2; @@ -1360,7 +1356,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a capcol = (colnr_T)getwhitecols(line); } else if (curline && wp == curwin) { // For spellbadword(): check if first word needs a capital. - col = (colnr_T)getwhitecols(line); + colnr_T col = (colnr_T)getwhitecols(line); if (check_need_cap(curwin, lnum, col)) { capcol = col; } @@ -1409,7 +1405,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a || ((colnr_T)(curline ? p - buf + (ptrdiff_t)len : p - buf) > wp->w_cursor.col)) { - col = (colnr_T)(p - buf); + colnr_T col = (colnr_T)(p - buf); bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; bool can_spell = !no_plain_buffer; @@ -1816,17 +1812,16 @@ void count_common_word(slang_T *lp, char *word, int len, uint8_t count) p = buf; } - wordcount_T *wc; hash_T hash = hash_hash(p); const size_t p_len = strlen(p); hashitem_T *hi = hash_lookup(&lp->sl_wordcount, p, p_len, hash); if (HASHITEM_EMPTY(hi)) { - wc = xmalloc(offsetof(wordcount_T, wc_word) + p_len + 1); + wordcount_T *wc = xmalloc(offsetof(wordcount_T, wc_word) + p_len + 1); memcpy(wc->wc_word, p, p_len + 1); wc->wc_count = count; hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); } else { - wc = HI2WC(hi); + wordcount_T *wc = HI2WC(hi); wc->wc_count = (uint16_t)(wc->wc_count + count); if (wc->wc_count < count) { // check for overflow wc->wc_count = MAXWORDCOUNT; @@ -1882,14 +1877,14 @@ int init_syl_tab(slang_T *slang) static int count_syllables(slang_T *slang, const char *word) FUNC_ATTR_NONNULL_ALL { - int cnt = 0; - bool skip = false; - int len; - if (slang->sl_syllable == NULL) { return 0; } + int cnt = 0; + bool skip = false; + int len; + for (const char *p = word; *p != NUL; p += len) { // When running into a space reset counter. if (*p == ' ') { @@ -1929,25 +1924,14 @@ static int count_syllables(slang_T *slang, const char *word) /// @return NULL if it's OK, an untranslated error message otherwise. char *parse_spelllang(win_T *wp) { - garray_T ga; - char *splp; - char *region; char region_cp[3]; - bool filename; - int region_mask; - slang_T *slang; - int c; char lang[MAXWLEN + 1]; char spf_name[MAXPATHL]; - char *p; - int round; - char *spf; char *use_region = NULL; bool dont_use_region = false; bool nobreak = false; static bool recursive = false; char *ret_msg = NULL; - char *spl_copy; bufref_T bufref; set_bufref(&bufref, wp->w_buffer); @@ -1960,20 +1944,21 @@ char *parse_spelllang(win_T *wp) } recursive = true; + garray_T ga; ga_init(&ga, sizeof(langp_T), 2); clear_midword(wp); // Make a copy of 'spelllang', the SpellFileMissing autocommands may change // it under our fingers. - spl_copy = xstrdup(wp->w_s->b_p_spl); + char *spl_copy = xstrdup(wp->w_s->b_p_spl); wp->w_s->b_cjk = 0; // Loop over comma separated language names. - for (splp = spl_copy; *splp != NUL;) { + for (char *splp = spl_copy; *splp != NUL;) { // Get one language name. copy_option_part(&splp, lang, MAXWLEN, ","); - region = NULL; + char *region = NULL; int len = (int)strlen(lang); if (!valid_spelllang(lang)) { @@ -1985,6 +1970,8 @@ char *parse_spelllang(win_T *wp) continue; } + slang_T *slang; + bool filename; // If the name ends in ".spl" use it as the name of the spell file. // If there is a region name let "region" point to it and remove it // from the name. @@ -1992,7 +1979,7 @@ char *parse_spelllang(win_T *wp) filename = true; // Locate a region and remove it from the file name. - p = vim_strchr(path_tail(lang), '_'); + char *p = vim_strchr(path_tail(lang), '_'); if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) && !ASCII_ISALPHA(p[3])) { xstrlcpy(region_cp, p + 1, 3); @@ -2055,10 +2042,10 @@ char *parse_spelllang(win_T *wp) if (filename ? path_full_compare(lang, slang->sl_fname, false, true) == kEqualFiles : STRICMP(lang, slang->sl_name) == 0) { - region_mask = REGION_ALL; + int region_mask = REGION_ALL; if (!filename && region != NULL) { // find region in sl_regions - c = find_region(slang->sl_regions, region); + int c = find_region(slang->sl_regions, region); if (c == REGION_ALL) { if (slang->sl_add) { if (*slang->sl_regions != NUL) { @@ -2094,8 +2081,8 @@ char *parse_spelllang(win_T *wp) // round 1: load first name in 'spellfile'. // round 2: load second name in 'spellfile. // etc. - spf = curwin->w_s->b_p_spf; - for (round = 0; round == 0 || *spf != NUL; round++) { + char *spf = curwin->w_s->b_p_spf; + for (int round = 0; round == 0 || *spf != NUL; round++) { if (round == 0) { // Internal wordlist, if there is one. if (int_wordlist == NULL) { @@ -2105,11 +2092,12 @@ char *parse_spelllang(win_T *wp) } else { // One entry in 'spellfile'. copy_option_part(&spf, spf_name, MAXPATHL - 5, ","); - STRCAT(spf_name, ".spl"); + strcat(spf_name, ".spl"); + int c; // If it was already found above then skip it. for (c = 0; c < ga.ga_len; c++) { - p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; + char *p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; if (p != NULL && path_full_compare(spf_name, p, false, true) == kEqualFiles) { break; @@ -2120,6 +2108,8 @@ char *parse_spelllang(win_T *wp) } } + slang_T *slang; + // Check if it was loaded already. for (slang = first_lang; slang != NULL; slang = slang->sl_next) { if (path_full_compare(spf_name, slang->sl_fname, false, true) @@ -2135,7 +2125,7 @@ char *parse_spelllang(win_T *wp) STRCPY(lang, "internal wordlist"); } else { xstrlcpy(lang, path_tail(spf_name), MAXWLEN + 1); - p = vim_strchr(lang, '.'); + char *p = vim_strchr(lang, '.'); if (p != NULL) { *p = NUL; // truncate at ".encoding.add" } @@ -2149,10 +2139,10 @@ char *parse_spelllang(win_T *wp) } } if (slang != NULL) { - region_mask = REGION_ALL; + int region_mask = REGION_ALL; if (use_region != NULL && !dont_use_region) { // find region in sl_regions - c = find_region(slang->sl_regions, use_region); + int c = find_region(slang->sl_regions, use_region); if (c != REGION_ALL) { region_mask = 1 << c; } else if (*slang->sl_regions != NUL) { @@ -2687,7 +2677,7 @@ void ex_spellrepall(exarg_T *eap) char *p = xmalloc((size_t)get_cursor_line_len() + (size_t)addlen + 1); memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); - STRCAT(p, line + curwin->w_cursor.col + repl_from_len); + strcat(p, line + curwin->w_cursor.col + repl_from_len); ml_replace(curwin->w_cursor.lnum, p, false); inserted_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col, (int)repl_from_len, (int)repl_to_len); @@ -2902,19 +2892,7 @@ static void spell_soundfold_sofo(slang_T *slang, const char *inword, char *res) // Multi-byte version of spell_soundfold(). static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) { - salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; int word[MAXWLEN] = { 0 }; - int wres[MAXWLEN] = { 0 }; - int *ws; - int *pf; - int j, z; - int reslen; - int k = 0; - int k0; - int n0; - int pri; - int p0 = -333; - int c0; bool did_white = false; // Convert the multi-byte string to a wide-character string. @@ -2942,17 +2920,24 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } word[wordlen] = NUL; + salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; + int wres[MAXWLEN] = { 0 }; + int k = 0; + int p0 = -333; int c; // This algorithm comes from Aspell phonet.cpp. // Converted from C++ to C. Added support for multi-byte chars. // Changed to keep spaces. - int i = reslen = z = 0; + int i = 0; + int reslen = 0; + int z = 0; while ((c = word[i]) != NUL) { // Start with the first rule that has the character in the word. int n = slang->sl_sal_first[c & 0xff]; int z0 = 0; if (n >= 0) { + int *ws; // Check all rules for the same index byte. // If c is 0x300 need extra check for the end of the array, as // (c & 0xff) is NUL. @@ -2969,6 +2954,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) continue; } if (k > 2) { + int j; for (j = 2; j < k; j++) { if (word[i + j] != ws[j]) { break; @@ -2980,6 +2966,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } } + int *pf; if ((pf = smp[n].sm_oneof_w) != NULL) { // Check for match with one of the chars in "sm_oneof". while (*pf != NUL && *pf != word[i + k]) { @@ -2991,10 +2978,10 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) k++; } char *s = smp[n].sm_rules; - pri = 5; // default priority + int pri = 5; // default priority p0 = (uint8_t)(*s); - k0 = k; + int k0 = k; while (*s == '-' && k > 1) { k--; s++; @@ -3022,8 +3009,8 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) && (!spell_iswordp_w(word + i + k0, curwin)))) { // search for followup rules, if: // followup and k > 1 and NO '-' in searchstring - c0 = word[i + k - 1]; - n0 = slang->sl_sal_first[c0 & 0xff]; + int c0 = word[i + k - 1]; + int n0 = slang->sl_sal_first[c0 & 0xff]; if (slang->sl_followup && k > 1 && n0 >= 0 && p0 != '-' && word[i + k] != NUL) { @@ -3042,6 +3029,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } if (k0 > 2) { pf = word + i + k + 1; + int j; for (j = 2; j < k0; j++) { if (*pf++ != ws[j]) { break; @@ -3262,23 +3250,13 @@ void ex_spelldump(exarg_T *eap) /// @param dumpflags_arg DUMPFLAG_* void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) { - langp_T *lp; - slang_T *slang; idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; char word[MAXWLEN]; - int c; - uint8_t *byts; - idx_T *idxs; linenr_T lnum = 0; - int depth; - int n; - int flags; char *region_names = NULL; // region names being used bool do_region = true; // dump region names and numbers - char *p; int dumpflags = dumpflags_arg; - int patlen; // When ignoring case or when the pattern starts with capital pass this on // to dump_word(). @@ -3286,7 +3264,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) if (ic) { dumpflags |= DUMPFLAG_ICASE; } else { - n = captype(pat, NULL); + int n = captype(pat, NULL); if (n == WF_ONECAP) { dumpflags |= DUMPFLAG_ONECAP; } else if (n == WF_ALLCAP @@ -3299,8 +3277,8 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) // Find out if we can support regions: All languages must support the same // regions or none at all. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); - p = lp->lp_slang->sl_regions; + langp_T *lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + char *p = lp->lp_slang->sl_regions; if (p[0] != 0) { if (region_names == NULL) { // first language with regions region_names = p; @@ -3320,8 +3298,8 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) // Loop over all files loaded for the entries in 'spelllang'. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); - slang = lp->lp_slang; + langp_T *lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + slang_T *slang = lp->lp_slang; if (slang->sl_fbyts == NULL) { // reloading failed continue; } @@ -3331,6 +3309,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) ml_append(lnum++, IObuff, 0, false); } + int patlen; // When matching with a pattern and there are no prefixes only use // parts of the tree that match "pat". if (pat != NULL && slang->sl_pbyts == NULL) { @@ -3342,6 +3321,8 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) // round 1: case-folded tree // round 2: keep-case tree for (int round = 1; round <= 2; round++) { + uint8_t *byts; + idx_T *idxs; if (round == 1) { dumpflags &= ~DUMPFLAG_KEEPCASE; byts = slang->sl_fbyts; @@ -3354,7 +3335,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) if (byts == NULL) { continue; // array is empty } - depth = 0; + int depth = 0; arridx[0] = 0; curi[0] = 1; while (depth >= 0 && !got_int @@ -3366,16 +3347,16 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) ins_compl_check_keys(50, false); } else { // Do one more byte at this node. - n = arridx[depth] + curi[depth]; + int n = arridx[depth] + curi[depth]; curi[depth]++; - c = byts[n]; + int c = byts[n]; if (c == 0 || depth >= MAXWLEN - 1) { // End of word or reached maximum length, deal with the // word. // Don't use keep-case words in the fold-case tree, // they will appear in the keep-case tree. // Only use the word when the region matches. - flags = (int)idxs[n]; + int flags = (int)idxs[n]; if ((round == 2 || (flags & WF_KEEPCAP) == 0) && (flags & WF_NEEDCOMP) == 0 && (do_region @@ -3463,14 +3444,14 @@ static void dump_word(slang_T *slang, char *word, char *pat, Direction *dir, int // Add flags and regions after a slash. if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap) { STRCPY(badword, p); - STRCAT(badword, "/"); + strcat(badword, "/"); if (keepcap) { - STRCAT(badword, "="); + strcat(badword, "="); } if (flags & WF_BANNED) { - STRCAT(badword, "!"); + strcat(badword, "!"); } else if (flags & WF_RARE) { - STRCAT(badword, "?"); + strcat(badword, "?"); } if (flags & WF_REGION) { for (int i = 0; i < 7; i++) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 046a0a56e5..3feae980c5 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -240,6 +240,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/ex_cmds_defs.h" #include "nvim/fileio.h" #include "nvim/garray.h" @@ -2139,11 +2140,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) + strlen(items[1]) + 3, false); if (spin->si_info != NULL) { STRCPY(p, spin->si_info); - STRCAT(p, "\n"); + strcat(p, "\n"); } - STRCAT(p, items[0]); - STRCAT(p, " "); - STRCAT(p, items[1]); + strcat(p, items[0]); + strcat(p, " "); + strcat(p, items[1]); spin->si_info = p; } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2) && midword == NULL) { midword = getroom_save(spin, items[1]); @@ -2199,7 +2200,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) // "Na" into "Na+", "1234" into "1234+". p = getroom(spin, strlen(items[1]) + 2, false); STRCPY(p, items[1]); - STRCAT(p, "+"); + strcat(p, "+"); compflags = p; } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULES", 2)) { // We don't use the count, but do check that it's a number and @@ -2220,9 +2221,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) p = getroom(spin, (size_t)l, false); if (compflags != NULL) { STRCPY(p, compflags); - STRCAT(p, "/"); + strcat(p, "/"); } - STRCAT(p, items[1]); + strcat(p, items[1]); compflags = p; } } else if (is_aff_rule(items, itemcnt, "COMPOUNDWORDMAX", 2) @@ -2843,7 +2844,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char *compflags char *p = getroom(spin, (size_t)len, false); if (spin->si_compflags != NULL) { STRCPY(p, spin->si_compflags); - STRCAT(p, "/"); + strcat(p, "/"); } spin->si_compflags = p; uint8_t *tp = (uint8_t *)p + strlen(p); @@ -3385,7 +3386,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ MB_PTR_ADV(p); } } - STRCAT(newword, p); + strcat(newword, p); } else { // suffix: chop/add at the end of the word xstrlcpy(newword, word, MAXWLEN); @@ -3399,7 +3400,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ *p = NUL; } if (ae->ae_add != NULL) { - STRCAT(newword, ae->ae_add); + strcat(newword, ae->ae_add); } } @@ -3614,7 +3615,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) if (*line == '/') { line++; - if (strncmp(line, "encoding=", 9) == 0) { + if (strncmp(line, S_LEN("encoding=")) == 0) { if (spin->si_conv.vc_type != CONV_NONE) { smsg(0, _("Duplicate /encoding= line ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line - 1); @@ -3636,7 +3637,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) continue; } - if (strncmp(line, "regions=", 8) == 0) { + if (strncmp(line, S_LEN("regions=")) == 0) { if (spin->si_region_count > 1) { smsg(0, _("Duplicate /regions= line ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line); @@ -4765,7 +4766,7 @@ void ex_mkspell(exarg_T *eap) char *arg = eap->arg; bool ascii = false; - if (strncmp(arg, "-ascii", 6) == 0) { + if (strncmp(arg, S_LEN("-ascii")) == 0) { ascii = true; arg = skipwhite(arg + 6); } diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index a7de20d14e..436599bb13 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -14,6 +14,7 @@ #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -412,9 +413,9 @@ int spell_check_sps(void) f = SPS_FAST; } else if (strcmp(buf, "double") == 0) { f = SPS_DOUBLE; - } else if (strncmp(buf, "expr:", 5) != 0 - && strncmp(buf, "file:", 5) != 0 - && (strncmp(buf, "timeout:", 8) != 0 + } else if (strncmp(buf, S_LEN("expr:")) != 0 + && strncmp(buf, S_LEN("file:")) != 0 + && (strncmp(buf, S_LEN("timeout:")) != 0 || (!ascii_isdigit(buf[8]) && !(buf[8] == '-' && ascii_isdigit(buf[9]))))) { f = -1; @@ -542,7 +543,7 @@ void spell_suggest(int count) msg_row = Rows - 1; // for when 'cmdheight' > 1 lines_left = Rows; // avoid more prompt char *fmt = _("Change \"%.*s\" to:"); - if (cmdmsg_rl && strncmp(fmt, "Change", 6) == 0) { + if (cmdmsg_rl && strncmp(fmt, S_LEN("Change")) == 0) { // And now the rabbit from the high hat: Avoid showing the // untranslated message rightleft. fmt = ":ot \"%.*s\" egnahC"; @@ -642,7 +643,7 @@ void spell_suggest(int count) int c = (int)(sug.su_badptr - line); memmove(p, line, (size_t)c); STRCPY(p + c, stp->st_word); - STRCAT(p, sug.su_badptr + stp->st_orglen); + strcat(p, sug.su_badptr + stp->st_orglen); // For redo we use a change-word command. ResetRedobuff(); @@ -791,7 +792,7 @@ static void spell_find_suggest(char *badptr, int badlen, suginfo_T *su, int maxc for (char *p = sps_copy; *p != NUL;) { copy_option_part(&p, buf, MAXPATHL, ","); - if (strncmp(buf, "expr:", 5) == 0) { + if (strncmp(buf, S_LEN("expr:")) == 0) { // Evaluate an expression. Skip this when called recursively, // when using spellsuggest() in the expression. if (!expr_busy) { @@ -799,10 +800,10 @@ static void spell_find_suggest(char *badptr, int badlen, suginfo_T *su, int maxc spell_suggest_expr(su, buf + 5); expr_busy = false; } - } else if (strncmp(buf, "file:", 5) == 0) { + } else if (strncmp(buf, S_LEN("file:")) == 0) { // Use list of suggestions in a file. spell_suggest_file(su, buf + 5); - } else if (strncmp(buf, "timeout:", 8) == 0) { + } else if (strncmp(buf, S_LEN("timeout:")) == 0) { // Limit the time searching for suggestions. spell_suggest_timeout = atoi(buf + 8); } else if (!did_intern) { @@ -1637,7 +1638,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun // Append a space to preword when splitting. if (!try_compound && !fword_ends) { - STRCAT(preword, " "); + strcat(preword, " "); } sp->ts_prewordlen = (uint8_t)strlen(preword); sp->ts_splitoff = sp->ts_twordlen; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index ca7083a9e3..bba209b434 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1487,7 +1487,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt; new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length; - new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2; + new_fmt_p = (char *)memcpy(new_fmt_p, S_LEN("%}")) + 2; new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length; *new_fmt_p = 0; new_fmt_p = NULL; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 8fef4ba7fd..bf7af40a38 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -12,6 +12,7 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/charset.h" +#include "nvim/errors.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -332,7 +333,7 @@ void vim_strcpy_up(char *restrict dst, const char *restrict src) while ((c = (uint8_t)(*src++)) != NUL) { *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); } - *dst = '\0'; + *dst = NUL; } // strncpy (NUL-terminated) plus vim_strup. @@ -343,7 +344,7 @@ void vim_strncpy_up(char *restrict dst, const char *restrict src, size_t n) while (n-- && (c = (uint8_t)(*src++)) != NUL) { *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); } - *dst = '\0'; + *dst = NUL; } // memcpy (does not NUL-terminate) plus vim_strup. @@ -793,10 +794,10 @@ static int format_typeof(const char *type) FUNC_ATTR_NONNULL_ALL { // allowed values: \0, h, l, L - char length_modifier = '\0'; + char length_modifier = NUL; // current conversion specifier character - char fmt_spec = '\0'; + char fmt_spec = NUL; // parse 'h', 'l', 'll' and 'z' length modifiers if (*type == 'h' || *type == 'l' || *type == 'z') { @@ -864,7 +865,7 @@ static int format_typeof(const char *type) } else if (fmt_spec == 'd') { // signed switch (length_modifier) { - case '\0': + case NUL: case 'h': // char and short arguments are passed as int. return TYPE_INT; @@ -878,7 +879,7 @@ static int format_typeof(const char *type) } else { // unsigned switch (length_modifier) { - case '\0': + case NUL: case 'h': return TYPE_UNSIGNEDINT; case 'l': @@ -1049,7 +1050,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * p += n; } else { // allowed values: \0, h, l, L - char length_modifier = '\0'; + char length_modifier = NUL; // variable for positional arg int pos_arg = -1; @@ -1440,7 +1441,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st int space_for_positive = 1; // allowed values: \0, h, l, 2 (for ll), z, L - char length_modifier = '\0'; + char length_modifier = NUL; // temporary buffer for simple numeric->string conversion #define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable @@ -1465,7 +1466,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st size_t zero_padding_insertion_ind = 0; // current conversion specifier character - char fmt_spec = '\0'; + char fmt_spec = NUL; // buffer for 's' and 'S' specs char *tofree = NULL; @@ -1668,7 +1669,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case 'o': case 'x': case 'X': - if (tvs && length_modifier == '\0') { + if (tvs && length_modifier == NUL) { length_modifier = 'L'; } } @@ -1789,7 +1790,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st } else if (fmt_spec == 'd') { // signed switch (length_modifier) { - case '\0': + case NUL: arg = (tvs ? (int)tv_nr(tvs, &arg_idx) : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, @@ -1835,7 +1836,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st } else { // unsigned switch (length_modifier) { - case '\0': + case NUL: uarg = (tvs ? (unsigned)tv_nr(tvs, &arg_idx) : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, @@ -2222,7 +2223,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (str_m > 0) { // make sure the string is nul-terminated even at the expense of // overwriting the last character (shouldn't happen, but just in case) - str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0'; + str[str_l <= str_m - 1 ? str_l : str_m - 1] = NUL; } if (tvs != NULL diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b63d2a729d..538863326c 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -15,6 +15,7 @@ #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" @@ -3185,7 +3186,7 @@ static void syn_cmd_onoff(exarg_T *eap, char *name) if (!eap->skip) { did_syntax_onoff = true; char buf[100]; - memcpy(buf, "so ", 4); + memcpy(buf, S_LEN("so ") + 1); vim_snprintf(buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name); do_cmdline_cmd(buf); } @@ -4295,7 +4296,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing) if (item == ITEM_MATCHGROUP) { char *p = skiptowhite(rest); - if ((p - rest == 4 && strncmp(rest, "NONE", 4) == 0) || eap->skip) { + if ((p - rest == 4 && strncmp(rest, S_LEN("NONE")) == 0) || eap->skip) { matchgroup_id = 0; } else { matchgroup_id = syn_check_group(rest, (size_t)(p - rest)); @@ -4814,10 +4815,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) } else if (!eap->skip) { curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment"); } - } else if (strncmp(key, "LINES", 5) == 0 - || strncmp(key, "MINLINES", 8) == 0 - || strncmp(key, "MAXLINES", 8) == 0 - || strncmp(key, "LINEBREAKS", 10) == 0) { + } else if (strncmp(key, S_LEN("LINES")) == 0 + || strncmp(key, S_LEN("MINLINES")) == 0 + || strncmp(key, S_LEN("MAXLINES")) == 0 + || strncmp(key, S_LEN("LINEBREAKS")) == 0) { if (key[4] == 'S') { arg_end = key + 6; } else if (key[0] == 'L') { @@ -4991,7 +4992,7 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list, } else { // Handle match of regexp with group names. *name = '^'; - STRCAT(name, "$"); + strcat(name, "$"); regmatch.regprog = vim_regcomp(name, RE_MAGIC); if (regmatch.regprog == NULL) { failed = true; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index e7f483dd3d..1d318fc3c7 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -18,6 +18,7 @@ #include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" @@ -878,14 +879,14 @@ static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, cha } // skip "file:" without a value (static tag) - if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) { + if (strncmp(p, S_LEN("file:")) == 0 && ascii_isspace(p[5])) { p += 5; continue; } // skip "kind:<kind>" and "<kind>" if (p == tagp.tagkind || (p + 5 == tagp.tagkind - && strncmp(p, "kind:", 5) == 0)) { + && strncmp(p, S_LEN("kind:")) == 0)) { p = tagp.tagkind_end; continue; } @@ -1059,7 +1060,7 @@ static int add_llist_tags(char *tag, int num_matches, char **matches) // Precede the tag pattern with \V to make it very // nomagic. - STRCAT(cmd, "\\V"); + strcat(cmd, "\\V"); len += 2; int cmd_len = (int)(cmd_end - cmd_start + 1); @@ -1613,16 +1614,16 @@ static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch static bool findtags_hdr_parse(findtags_state_T *st) { // Header lines in a tags file start with "!_TAG_" - if (strncmp(st->lbuf, "!_TAG_", 6) != 0) { + if (strncmp(st->lbuf, S_LEN("!_TAG_")) != 0) { // Non-header item before the header, e.g. "!" itself. return true; } // Process the header line. - if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) { + if (strncmp(st->lbuf, S_LEN("!_TAG_FILE_SORTED\t")) == 0) { st->tag_file_sorted = (uint8_t)st->lbuf[18]; } - if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) { + if (strncmp(st->lbuf, S_LEN("!_TAG_FILE_ENCODING\t")) == 0) { // Prepare to convert every line from the specified encoding to // 'encoding'. char *p; @@ -1646,7 +1647,7 @@ static bool findtags_start_state_handler(findtags_state_T *st, bool *sortic, // The header ends when the line sorts below "!_TAG_". When case is // folded lower case letters sort before "_". - if (strncmp(st->lbuf, "!_TAG_", 6) <= 0 + if (strncmp(st->lbuf, S_LEN("!_TAG_")) <= 0 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) { return findtags_hdr_parse(st); } @@ -2669,7 +2670,7 @@ static bool test_for_static(tagptrs_T *tagp) char *p = tagp->command; while ((p = vim_strchr(p, '\t')) != NULL) { p++; - if (strncmp(p, "file:", 5) == 0) { + if (strncmp(p, S_LEN("file:")) == 0) { return true; } } @@ -2727,11 +2728,11 @@ static int parse_match(char *lbuf, tagptrs_T *tagp) // Accept ASCII alphabetic kind characters and any multi-byte // character. while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) { - if (strncmp(p, "kind:", 5) == 0) { + if (strncmp(p, S_LEN("kind:")) == 0) { tagp->tagkind = p + 5; - } else if (strncmp(p, "user_data:", 10) == 0) { + } else if (strncmp(p, S_LEN("user_data:")) == 0) { tagp->user_data = p + 10; - } else if (strncmp(p, "line:", 5) == 0) { + } else if (strncmp(p, S_LEN("line:")) == 0) { tagp->tagline = atoi(p + 5); } if (tagp->tagkind != NULL && tagp->user_data != NULL) { @@ -3173,7 +3174,7 @@ static int find_extra(char **pp) first_char = *str; } - if (str != NULL && strncmp(str, ";\"", 2) == 0) { + if (str != NULL && strncmp(str, S_LEN(";\"")) == 0) { *pp = str; return OK; } @@ -3302,7 +3303,7 @@ int get_tags(list_T *list, char *pat, char *buf_fname) bool is_static = test_for_static(&tp); // Skip pseudo-tag lines. - if (strncmp(tp.tagname, "!_TAG_", 6) == 0) { + if (strncmp(tp.tagname, S_LEN("!_TAG_")) == 0) { xfree(matches[i]); continue; } @@ -3327,10 +3328,10 @@ int get_tags(list_T *list, char *pat, char *buf_fname) *p != NUL && *p != '\n' && *p != '\r'; MB_PTR_ADV(p)) { if (p == tp.tagkind - || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) { + || (p + 5 == tp.tagkind && strncmp(p, S_LEN("kind:")) == 0)) { // skip "kind:<kind>" and "<kind>" p = tp.tagkind_end - 1; - } else if (strncmp(p, "file:", 5) == 0) { + } else if (strncmp(p, S_LEN("file:")) == 0) { // skip "file:" (static tag) p += 4; } else if (!ascii_iswhite(*p)) { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 2b05a8047e..000f750413 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -112,6 +112,9 @@ typedef struct { // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 +#define TEXTBUF_SIZE 0x1fff +#define SELECTIONBUF_SIZE 0x0400 + static TimeWatcher refresh_timer; static bool refresh_pending = false; @@ -127,7 +130,7 @@ struct terminal { // buffer used to: // - convert VTermScreen cell arrays into utf8 strings // - receive data from libvterm as a result of key presses. - char textbuf[0x1fff]; + char textbuf[TEXTBUF_SIZE]; ScrollbackLine **sb_buffer; // Scrollback storage. size_t sb_current; // Lines stored in sb_buffer. @@ -166,6 +169,9 @@ struct terminal { // When there is a pending TermRequest autocommand, block and store input. StringBuilder *pending_send; + char *selection_buffer; /// libvterm selection buffer + StringBuilder selection; /// Growable array containing full selection data + size_t refcount; // reference count }; @@ -179,6 +185,12 @@ static VTermScreenCallbacks vterm_screen_callbacks = { .sb_popline = term_sb_pop, }; +static VTermSelectionCallbacks vterm_selection_callbacks = { + .set = term_selection_set, + // For security reasons we don't support querying the system clipboard from the embedded terminal + .query = NULL, +}; + static Set(ptr_t) invalidated_terminals = SET_INIT; static void emit_termrequest(void **argv) @@ -315,6 +327,11 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(term->vts, 1); vterm_output_set_callback(term->vt, term_output_callback, term); + + term->selection_buffer = xcalloc(SELECTIONBUF_SIZE, 1); + vterm_state_set_selection_callbacks(state, &vterm_selection_callbacks, term, + term->selection_buffer, SELECTIONBUF_SIZE); + // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called term->invalid_start = 0; @@ -326,14 +343,6 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) refresh_screen(term, buf); set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); - // Default settings for terminal buffers - buf->b_p_ma = false; // 'nomodifiable' - buf->b_p_ul = -1; // 'undolevels' - buf->b_p_scbk = // 'scrollback' (initialize local from global) - (p_scbk < 0) ? 10000 : MAX(1, p_scbk); - buf->b_p_tw = 0; // 'textwidth' - set_option_value(kOptWrap, BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value(kOptList, BOOLEAN_OPTVAL(false), OPT_LOCAL); if (buf->b_ffname != NULL) { buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname)); } @@ -769,6 +778,8 @@ void terminal_destroy(Terminal **termpp) } xfree(term->sb_buffer); xfree(term->title); + xfree(term->selection_buffer); + kv_destroy(term->selection); vterm_free(term->vt); xfree(term); *termpp = NULL; // coverity[dead-store] @@ -833,9 +844,9 @@ void terminal_paste(int count, char **y_array, size_t y_size) if (j) { // terminate the previous line #ifdef MSWIN - terminal_send(curbuf->terminal, "\r\n", 2); + terminal_send(curbuf->terminal, S_LEN("\r\n")); #else - terminal_send(curbuf->terminal, "\n", 1); + terminal_send(curbuf->terminal, S_LEN("\n")); #endif } size_t len = strlen(y_array[j]); @@ -845,7 +856,7 @@ void terminal_paste(int count, char **y_array, size_t y_size) } char *dst = buff; char *src = y_array[j]; - while (*src != '\0') { + while (*src != NUL) { len = (size_t)utf_ptr2len(src); int c = utf_ptr2char(src); if (!is_filter_char(c)) { @@ -1198,6 +1209,54 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) return 1; } +static void term_clipboard_set(void **argv) +{ + VTermSelectionMask mask = (VTermSelectionMask)(long)argv[0]; + char *data = argv[1]; + + char regname; + switch (mask) { + case VTERM_SELECTION_CLIPBOARD: + regname = '+'; + break; + case VTERM_SELECTION_PRIMARY: + regname = '*'; + break; + default: + regname = '+'; + break; + } + + list_T *lines = tv_list_alloc(1); + tv_list_append_allocated_string(lines, data); + + list_T *args = tv_list_alloc(3); + tv_list_append_list(args, lines); + + const char regtype = 'v'; + tv_list_append_string(args, ®type, 1); + + tv_list_append_string(args, ®name, 1); + eval_call_provider("clipboard", "set", args, true); +} + +static int term_selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) +{ + Terminal *term = user; + if (frag.initial) { + kv_size(term->selection) = 0; + } + + kv_concat_len(term->selection, frag.str, frag.len); + + if (frag.final) { + char *data = xmemdupz(term->selection.items, kv_size(term->selection)); + multiqueue_put(main_loop.events, term_clipboard_set, (void *)mask, data); + } + + return 1; +} + // }}} // input handling {{{ diff --git a/src/nvim/testing.c b/src/nvim/testing.c index 343568d71e..8041cc2e33 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -7,6 +7,7 @@ #include <string.h> #include "nvim/ascii_defs.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index 1722bcc968..01567c9e01 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -1017,7 +1017,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) // and this line has a line comment after some text, the // paragraph doesn't really end. if (next_leader_flags == NULL - || strncmp(next_leader_flags, "://", 3) != 0 + || strncmp(next_leader_flags, S_LEN("://")) != 0 || check_linecomment(get_cursor_line_ptr()) == MAXCOL) { is_end_par = true; } diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index f1594dfcb9..a5768cfc06 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -15,7 +15,6 @@ #include "nvim/option_vars.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" -#include "nvim/rbuffer.h" #include "nvim/strings.h" #include "nvim/tui/input.h" #include "nvim/tui/input_defs.h" @@ -28,7 +27,6 @@ #include "nvim/msgpack_rpc/channel.h" #define READ_STREAM_SIZE 0xfff -#define KEY_BUFFER_SIZE 0xfff /// Size of libtermkey's internal input buffer. The buffer may grow larger than /// this when processing very long escape sequences, but will shrink back to @@ -132,7 +130,6 @@ void tinput_init(TermInput *input, Loop *loop) input->key_encoding = kKeyEncodingLegacy; input->ttimeout = (bool)p_ttimeout; input->ttimeoutlen = p_ttm; - input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE); for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) { pmap_put(int)(&kitty_key_map, kitty_key_map_entry[i].key, (ptr_t)kitty_key_map_entry[i].name); @@ -155,7 +152,7 @@ void tinput_init(TermInput *input, Loop *loop) termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); // setup input handle - rstream_init_fd(loop, &input->read_stream, input->in_fd, READ_STREAM_SIZE); + rstream_init_fd(loop, &input->read_stream, input->in_fd); // initialize a timer handle for handling ESC with libtermkey uv_timer_init(&loop->uv, &input->timer_handle); @@ -165,9 +162,8 @@ void tinput_init(TermInput *input, Loop *loop) void tinput_destroy(TermInput *input) { map_destroy(int, &kitty_key_map); - rbuffer_free(input->key_buffer); uv_close((uv_handle_t *)&input->timer_handle, NULL); - stream_close(&input->read_stream, NULL, NULL); + rstream_may_close(&input->read_stream); termkey_destroy(input->tk); } @@ -191,44 +187,38 @@ static void tinput_done_event(void **argv) /// Send all pending input in key buffer to Nvim server. static void tinput_flush(TermInput *input) { + String keys = { .data = input->key_buffer, .size = input->key_buffer_len }; if (input->paste) { // produce exactly one paste event - const size_t len = rbuffer_size(input->key_buffer); - String keys = { .data = xmallocz(len), .size = len }; - rbuffer_read(input->key_buffer, keys.data, len); MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, STRING_OBJ(keys)); // 'data' ADD_C(args, BOOLEAN_OBJ(true)); // 'crlf' ADD_C(args, INTEGER_OBJ(input->paste)); // 'phase' rpc_send_event(ui_client_channel_id, "nvim_paste", args); - api_free_string(keys); if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; } - rbuffer_reset(input->key_buffer); } else { // enqueue input - RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { - const String keys = { .data = buf, .size = len }; + if (input->key_buffer_len > 0) { MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, STRING_OBJ(keys)); // NOTE: This is non-blocking and won't check partially processed input, // but should be fine as all big sends are handled with nvim_paste, not nvim_input rpc_send_event(ui_client_channel_id, "nvim_input", args); - rbuffer_consumed(input->key_buffer, len); - rbuffer_reset(input->key_buffer); } } + input->key_buffer_len = 0; } -static void tinput_enqueue(TermInput *input, char *buf, size_t size) +static void tinput_enqueue(TermInput *input, const char *buf, size_t size) { - if (rbuffer_size(input->key_buffer) > - rbuffer_capacity(input->key_buffer) - 0xff) { - // don't ever let the buffer get too full or we risk putting incomplete keys - // into it + if (input->key_buffer_len > KEY_BUFFER_SIZE - size) { + // don't ever let the buffer get too full or we risk putting incomplete keys into it tinput_flush(input); } - rbuffer_write(input->key_buffer, buf, size); + size_t to_copy = MIN(size, KEY_BUFFER_SIZE - input->key_buffer_len); + memcpy(input->key_buffer + input->key_buffer_len, buf, to_copy); + input->key_buffer_len += to_copy; } /// Handle TERMKEY_KEYMOD_* modifiers, i.e. Shift, Alt and Ctrl. @@ -472,8 +462,10 @@ static void tinput_timer_cb(uv_timer_t *handle) TermInput *input = handle->data; // If the raw buffer is not empty, process the raw buffer first because it is // processing an incomplete bracketed paster sequence. - if (rbuffer_size(input->read_stream.buffer)) { - handle_raw_buffer(input, true); + size_t size = rstream_available(&input->read_stream); + if (size) { + size_t consumed = handle_raw_buffer(input, true, input->read_stream.read_pos, size); + rstream_consume(&input->read_stream, consumed); } tk_getkeys(input, true); tinput_flush(input); @@ -487,39 +479,37 @@ static void tinput_timer_cb(uv_timer_t *handle) /// /// @param input the input stream /// @return true iff handle_focus_event consumed some input -static bool handle_focus_event(TermInput *input) +static size_t handle_focus_event(TermInput *input, const char *ptr, size_t size) { - if (rbuffer_size(input->read_stream.buffer) > 2 - && (!rbuffer_cmp(input->read_stream.buffer, "\x1b[I", 3) - || !rbuffer_cmp(input->read_stream.buffer, "\x1b[O", 3))) { - bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; - // Advance past the sequence - rbuffer_consumed(input->read_stream.buffer, 3); + if (size >= 3 + && (!memcmp(ptr, "\x1b[I", 3) + || !memcmp(ptr, "\x1b[O", 3))) { + bool focus_gained = ptr[2] == 'I'; MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, BOOLEAN_OBJ(focus_gained)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args); - return true; + return 3; // Advance past the sequence } - return false; + return 0; } #define START_PASTE "\x1b[200~" #define END_PASTE "\x1b[201~" -static HandleState handle_bracketed_paste(TermInput *input) +static size_t handle_bracketed_paste(TermInput *input, const char *ptr, size_t size, + bool *incomplete) { - size_t buf_size = rbuffer_size(input->read_stream.buffer); - if (buf_size > 5 - && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, 6) - || !rbuffer_cmp(input->read_stream.buffer, END_PASTE, 6))) { - bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; + if (size >= 6 + && (!memcmp(ptr, START_PASTE, 6) + || !memcmp(ptr, END_PASTE, 6))) { + bool enable = ptr[4] == '0'; if (input->paste && enable) { - return kNotApplicable; // Pasting "start paste" code literally. + return 0; // Pasting "start paste" code literally. } + // Advance past the sequence - rbuffer_consumed(input->read_stream.buffer, 6); if (!!input->paste == enable) { - return kComplete; // Spurious "disable paste" code. + return 6; // Spurious "disable paste" code. } if (enable) { @@ -534,15 +524,15 @@ static HandleState handle_bracketed_paste(TermInput *input) // Paste phase: "disabled". input->paste = 0; } - return kComplete; - } else if (buf_size < 6 - && (!rbuffer_cmp(input->read_stream.buffer, START_PASTE, buf_size) - || !rbuffer_cmp(input->read_stream.buffer, - END_PASTE, buf_size))) { + return 6; + } else if (size < 6 + && (!memcmp(ptr, START_PASTE, size) + || !memcmp(ptr, END_PASTE, size))) { // Wait for further input, as the sequence may be split. - return kIncomplete; + *incomplete = true; + return 0; } - return kNotApplicable; + return 0; } /// Handle an OSC or DCS response sequence from the terminal. @@ -653,20 +643,31 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key) } } -static void handle_raw_buffer(TermInput *input, bool force) +static size_t handle_raw_buffer(TermInput *input, bool force, const char *data, size_t size) { - HandleState is_paste = kNotApplicable; + const char *ptr = data; do { - if (!force - && (handle_focus_event(input) - || (is_paste = handle_bracketed_paste(input)) != kNotApplicable)) { - if (is_paste == kIncomplete) { + if (!force) { + size_t consumed = handle_focus_event(input, ptr, size); + if (consumed) { + ptr += consumed; + size -= consumed; + continue; + } + + bool incomplete = false; + consumed = handle_bracketed_paste(input, ptr, size, &incomplete); + if (incomplete) { + assert(consumed == 0); // Wait for the next input, leaving it in the raw buffer due to an // incomplete sequence. - return; + return (size_t)(ptr - data); + } else if (consumed) { + ptr += consumed; + size -= consumed; + continue; } - continue; } // @@ -675,55 +676,47 @@ static void handle_raw_buffer(TermInput *input, bool force) // calls (above) depend on this. // size_t count = 0; - RBUFFER_EACH(input->read_stream.buffer, c, i) { + for (size_t i = 0; i < size; i++) { count = i + 1; - if (c == '\x1b' && count > 1) { + if (ptr[i] == '\x1b' && count > 1) { count--; break; } } // Push bytes directly (paste). if (input->paste) { - RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { - size_t consumed = MIN(count, len); - assert(consumed <= input->read_stream.buffer->size); - tinput_enqueue(input, ptr, consumed); - rbuffer_consumed(input->read_stream.buffer, consumed); - if (!(count -= consumed)) { - break; - } - } + tinput_enqueue(input, ptr, count); + ptr += count; + size -= count; continue; } + // Push through libtermkey (translates to "<keycode>" strings, etc.). - RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { - const size_t size = MIN(count, len); - if (size > termkey_get_buffer_remaining(input->tk)) { + { + const size_t to_use = MIN(count, size); + if (to_use > termkey_get_buffer_remaining(input->tk)) { // We are processing a very long escape sequence. Increase termkey's // internal buffer size. We don't handle out of memory situations so // abort if it fails - const size_t delta = size - termkey_get_buffer_remaining(input->tk); + const size_t delta = to_use - termkey_get_buffer_remaining(input->tk); const size_t bufsize = termkey_get_buffer_size(input->tk); if (!termkey_set_buffer_size(input->tk, MAX(bufsize + delta, bufsize * 2))) { abort(); } } - size_t consumed = termkey_push_bytes(input->tk, ptr, size); + size_t consumed = termkey_push_bytes(input->tk, ptr, to_use); // We resize termkey's buffer when it runs out of space, so this should // never happen - assert(consumed <= rbuffer_size(input->read_stream.buffer)); - rbuffer_consumed(input->read_stream.buffer, consumed); + assert(consumed <= to_use); + ptr += consumed; + size -= consumed; // Process the input buffer now for any keys tk_getkeys(input, false); - - if (!(count -= consumed)) { - break; - } } - } while (rbuffer_size(input->read_stream.buffer)); + } while (size); const size_t tk_size = termkey_get_buffer_size(input->tk); const size_t tk_remaining = termkey_get_buffer_remaining(input->tk); @@ -735,23 +728,25 @@ static void handle_raw_buffer(TermInput *input, bool force) abort(); } } + + return (size_t)(ptr - data); } -static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data, bool eof) +static size_t tinput_read_cb(RStream *stream, const char *buf, size_t count_, void *data, bool eof) { TermInput *input = data; + size_t consumed = handle_raw_buffer(input, false, buf, count_); + tinput_flush(input); + if (eof) { loop_schedule_fast(&main_loop, event_create(tinput_done_event, NULL)); - return; + return consumed; } - handle_raw_buffer(input, false); - tinput_flush(input); - // An incomplete sequence was found. Leave it in the raw buffer and wait for // the next input. - if (rbuffer_size(input->read_stream.buffer)) { + if (consumed < count_) { // If 'ttimeout' is not set, start the timer with a timeout of 0 to process // the next input. int64_t ms = input->ttimeout @@ -759,11 +754,7 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, void *da // Stop the current timer if already running uv_timer_stop(&input->timer_handle); uv_timer_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0); - return; } - // Make sure the next input escape sequence fits into the ring buffer without - // wraparound, else it could be misinterpreted (because rbuffer_read_ptr() - // exposes the underlying buffer to callers unaware of the wraparound). - rbuffer_reset(input->read_stream.buffer); + return consumed; } diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h index bf6d0f2978..8d0c0c20e9 100644 --- a/src/nvim/tui/input.h +++ b/src/nvim/tui/input.h @@ -5,7 +5,6 @@ #include <uv.h> #include "nvim/event/defs.h" -#include "nvim/rbuffer_defs.h" #include "nvim/tui/input_defs.h" // IWYU pragma: keep #include "nvim/tui/tui_defs.h" #include "nvim/types_defs.h" @@ -17,6 +16,7 @@ typedef enum { kKeyEncodingXterm, ///< Xterm's modifyOtherKeys encoding (XTMODKEYS) } KeyEncoding; +#define KEY_BUFFER_SIZE 0x1000 typedef struct { int in_fd; // Phases: -1=all 0=disabled 1=first-chunk 2=continue 3=last-chunk @@ -33,17 +33,12 @@ typedef struct { TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook uv_timer_t timer_handle; Loop *loop; - Stream read_stream; - RBuffer *key_buffer; + RStream read_stream; TUIData *tui_data; + char key_buffer[KEY_BUFFER_SIZE]; + size_t key_buffer_len; } TermInput; -typedef enum { - kIncomplete = -1, - kNotApplicable = 0, - kComplete = 1, -} HandleState; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/input.h.generated.h" #endif diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 3cf9650428..657bd6dd10 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -35,7 +35,7 @@ bool terminfo_is_term_family(const char *term, const char *family) // The screen terminfo may have a terminal name like screen.xterm. By making // the dot(.) a valid separator, such terminal names will also be the // terminal family of the screen. - && ('\0' == term[flen] || '-' == term[flen] || '.' == term[flen]); + && (NUL == term[flen] || '-' == term[flen] || '.' == term[flen]); } bool terminfo_is_bsd_console(const char *term) diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index dc8c8def5b..57696b1839 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1855,20 +1855,12 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) return -1; } -/// Determine if the terminal supports truecolor or not: +/// Determine if the terminal supports truecolor or not. /// -/// 1. If $COLORTERM is "24bit" or "truecolor", return true -/// 2. Else, check terminfo for Tc, RGB, setrgbf, or setrgbb capabilities. If -/// found, return true -/// 3. Else, return false +/// If terminfo contains Tc, RGB, or both setrgbf and setrgbb capabilities, return true. static bool term_has_truecolor(TUIData *tui, const char *colorterm) { - // Check $COLORTERM - if (strequal(colorterm, "truecolor") || strequal(colorterm, "24bit")) { - return true; - } - - // Check for Tc and RGB + // Check for Tc or RGB for (size_t i = 0; i < unibi_count_ext_bool(tui->ut); i++) { const char *n = unibi_get_ext_bool_name(tui->ut, i); if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) { @@ -2508,7 +2500,7 @@ static const char *tui_get_stty_erase(int fd) struct termios t; if (tcgetattr(fd, &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; - stty_erase[1] = '\0'; + stty_erase[1] = NUL; DLOG("stty/termios:erase=%s", stty_erase); } #endif diff --git a/src/nvim/undo.c b/src/nvim/undo.c index ba720c9f6a..ed5c9a508c 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -92,6 +92,7 @@ #include "nvim/decoration.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index e3d9dc5f54..877624b07e 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -422,10 +422,10 @@ char *get_user_cmd_complete(expand_T *xp, int idx) int cmdcomplete_str_to_type(const char *complete_str) { - if (strncmp(complete_str, "custom,", 7) == 0) { + if (strncmp(complete_str, S_LEN("custom,")) == 0) { return EXPAND_USER_DEFINED; } - if (strncmp(complete_str, "customlist,", 11) == 0) { + if (strncmp(complete_str, S_LEN("customlist,")) == 0) { return EXPAND_USER_LIST; } @@ -580,7 +580,7 @@ static void uc_list(char *name, size_t name_len) IObuff[len++] = ' '; } while ((int64_t)len < 25 - over); - IObuff[len] = '\0'; + IObuff[len] = NUL; msg_outtrans(IObuff, 0); if (cmd->uc_luaref != LUA_NOREF) { @@ -831,7 +831,7 @@ invalid_count: } } else { char ch = attr[len]; - attr[len] = '\0'; + attr[len] = NUL; semsg(_("E181: Invalid attribute: %s"), attr); attr[len] = ch; return FAIL; @@ -1056,7 +1056,7 @@ void ex_delcommand(exarg_T *eap) const char *arg = eap->arg; bool buffer_only = false; - if (strncmp(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) { + if (strncmp(arg, S_LEN("-buffer")) == 0 && ascii_iswhite(arg[7])) { buffer_only = true; arg = skipwhite(arg + 7); } @@ -1274,9 +1274,9 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods) if (buf != NULL) { if (*multi_mods) { - STRCAT(buf, " "); + strcat(buf, " "); } - STRCAT(buf, mod_str); + strcat(buf, mod_str); } *multi_mods = true; @@ -1364,7 +1364,7 @@ size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote) if (quote) { *buf++ = '"'; } - *buf = '\0'; + *buf = NUL; } // the modifiers that are simple flags diff --git a/src/nvim/window.c b/src/nvim/window.c index 1a6c3f7263..732ba6566b 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -22,6 +22,7 @@ #include "nvim/diff.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -788,7 +789,7 @@ int win_fdccol_count(win_T *wp) const char *fdc = wp->w_p_fdc; // auto:<NUM> - if (strncmp(fdc, "auto", 4) == 0) { + if (strncmp(fdc, S_LEN("auto")) == 0) { const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1; int needed_fdccols = getDeepestNesting(wp); return MIN(fdccol, needed_fdccols); diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index e3ca0ff139..77bbaed105 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -11,6 +11,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/grid_defs.h" |