diff options
Diffstat (limited to 'src')
103 files changed, 2982 insertions, 2429 deletions
diff --git a/src/clint.py b/src/clint.py index 062901b43a..9bca634171 100755 --- a/src/clint.py +++ b/src/clint.py @@ -1689,7 +1689,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a + # technically should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 047b22edcc..7e22203aba 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -450,20 +450,27 @@ endif() #------------------------------------------------------------------------------- get_target_property(prop main_lib INTERFACE_COMPILE_DEFINITIONS) -foreach(gen_cdef ${prop}) - if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") - list(APPEND gen_cflags "-D${gen_cdef}") - endif() -endforeach() +if(NOT "${prop}" STREQUAL "prop-NOTFOUND") + foreach(gen_cdef ${prop}) + if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") + list(APPEND gen_cflags "-D${gen_cdef}") + endif() + endforeach() +endif() get_directory_property(targets BUILDSYSTEM_TARGETS) foreach(target ${targets}) get_target_property(prop ${target} INTERFACE_INCLUDE_DIRECTORIES) - foreach(gen_include ${prop}) - list(APPEND gen_cflags "-I${gen_include}") - endforeach() + if(NOT "${prop}" STREQUAL "prop-NOTFOUND") + message(STATUS "${target} props '${prop}'") + foreach(gen_include ${prop}) + list(APPEND gen_cflags "-I${gen_include}") + endforeach() + endif() endforeach() +list(REMOVE_DUPLICATES gen_cflags) + if(APPLE AND CMAKE_OSX_SYSROOT) list(APPEND gen_cflags "-isysroot" "${CMAKE_OSX_SYSROOT}") endif() diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 06f21919f9..d71bcc4bcf 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -386,10 +386,10 @@ cleanup: /// - id: (number) autocommand id /// - event: (string) name of the triggered event |autocmd-events| /// - group: (number|nil) autocommand group id, if any -/// - match: (string) expanded value of |<amatch>| -/// - buf: (number) expanded value of |<abuf>| -/// - file: (string) expanded value of |<afile>| -/// - data: (any) arbitrary data passed from |nvim_exec_autocmds()| +/// - match: (string) expanded value of [<amatch>] +/// - buf: (number) expanded value of [<abuf>] +/// - file: (string) expanded value of [<afile>] +/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand @@ -694,7 +694,7 @@ void nvim_del_augroup_by_name(String name, Error *err) /// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with /// {pattern}. /// - modeline (bool) optional: defaults to true. Process the -/// modeline after the autocommands |<nomodeline>|. +/// modeline after the autocommands [<nomodeline>]. /// - data (any): arbitrary data to send to the autocommand callback. See /// |nvim_create_autocmd()| for details. /// @see |:doautocmd| diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7f195de959..42467d1562 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -529,18 +529,18 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // Another call to ml_get_buf() may free the lines, so we make copies char *str_at_start = ml_get_buf(buf, (linenr_T)start_row); - size_t len_at_start = strlen(str_at_start); - str_at_start = arena_memdupz(arena, str_at_start, len_at_start); - start_col = start_col < 0 ? (int64_t)len_at_start + start_col + 1 : start_col; - VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", { + colnr_T len_at_start = ml_get_buf_len(buf, (linenr_T)start_row); + str_at_start = arena_memdupz(arena, str_at_start, (size_t)len_at_start); + start_col = start_col < 0 ? len_at_start + start_col + 1 : start_col; + VALIDATE_RANGE((start_col >= 0 && start_col <= len_at_start), "start_col", { return; }); char *str_at_end = ml_get_buf(buf, (linenr_T)end_row); - size_t len_at_end = strlen(str_at_end); - str_at_end = arena_memdupz(arena, str_at_end, len_at_end); - end_col = end_col < 0 ? (int64_t)len_at_end + end_col + 1 : end_col; - VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", { + colnr_T len_at_end = ml_get_buf_len(buf, (linenr_T)end_row); + str_at_end = arena_memdupz(arena, str_at_end, (size_t)len_at_end); + end_col = end_col < 0 ? len_at_end + end_col + 1 : end_col; + VALIDATE_RANGE((end_col >= 0 && end_col <= len_at_end), "end_col", { return; }); @@ -563,12 +563,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (start_row == end_row) { old_byte = (bcount_t)end_col - start_col; } else { - old_byte += (bcount_t)len_at_start - start_col; + old_byte += len_at_start - start_col; for (int64_t i = 1; i < end_row - start_row; i++) { int64_t lnum = start_row + i; - - const char *bufline = ml_get_buf(buf, (linenr_T)lnum); - old_byte += (bcount_t)(strlen(bufline)) + 1; + old_byte += ml_get_buf_len(buf, (linenr_T)lnum) + 1; } old_byte += (bcount_t)end_col + 1; } @@ -577,7 +575,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In String last_item = replacement.items[replacement.size - 1].data.string; size_t firstlen = (size_t)start_col + first_item.size; - size_t last_part_len = len_at_end - (size_t)end_col; + size_t last_part_len = (size_t)len_at_end - (size_t)end_col; if (replacement.size == 1) { firstlen += last_part_len; } @@ -1271,10 +1269,13 @@ static void fix_cursor(win_T *win, linenr_T lo, linenr_T hi, linenr_T extra) } else if (extra < 0) { check_cursor_lnum(win); } - check_cursor_col_win(win); + check_cursor_col(win); changed_cline_bef_curs(win); + win->w_valid &= ~(VALID_BOTLINE_AP); + update_topline(win); + } else { + invalidate_botline(win); } - invalidate_botline(win); } /// Fix cursor position after replacing text @@ -1309,7 +1310,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l // it's easier to work with a single value here. // col and coladd are fixed by a later call - // to check_cursor_col_win when necessary + // to check_cursor_col when necessary win->w_cursor.col += win->w_cursor.coladd; win->w_cursor.coladd = 0; @@ -1324,7 +1325,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l // it already (in case virtualedit is active) // column might be additionally adjusted below // to keep it inside col range if needed - colnr_T len = (colnr_T)strlen(ml_get_buf(win->w_buffer, new_end_row)); + colnr_T len = ml_get_buf_len(win->w_buffer, new_end_row); if (win->w_cursor.col < len) { win->w_cursor.col = len; } @@ -1345,7 +1346,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l } } - check_cursor_col_win(win); + check_cursor_col(win); changed_cline_bef_curs(win); invalidate_botline(win); } @@ -1424,6 +1425,7 @@ void buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool for (size_t i = 0; i < n; i++) { linenr_T lnum = start + (linenr_T)i; char *bufstr = ml_get_buf(buf, lnum); - push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl, arena); + size_t bufstrlen = (size_t)ml_get_buf_len(buf, lnum); + push_linestr(lstate, l, bufstr, bufstrlen, start_idx + (int)i, replace_nl, arena); } } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index e08035b0eb..779e216c74 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -48,16 +48,16 @@ /// @param[out] err Error details, if any. /// @return Dictionary containing command information, with these keys: /// - cmd: (string) Command name. -/// - range: (array) (optional) Command range (|<line1>| |<line2>|). +/// - range: (array) (optional) Command range ([<line1>] [<line2>]). /// Omitted if command doesn't accept a range. /// Otherwise, has no elements if no range was specified, one element if /// only a single range item was specified, or two elements if both range /// items were specified. -/// - count: (number) (optional) Command |<count>|. +/// - count: (number) (optional) Command [<count>]. /// Omitted if command cannot take a count. -/// - reg: (string) (optional) Command |<register>|. +/// - reg: (string) (optional) Command [<register>]. /// Omitted if command cannot take a register. -/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier. +/// - bang: (boolean) Whether command contains a [<bang>] (!) modifier. /// - args: (array) Command arguments. /// - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines. /// - nargs: (string) Value of |:command-nargs|. @@ -853,17 +853,17 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin /// from Lua, the command can also be a Lua function. The function is called with a /// single table argument that contains the following keys: /// - name: (string) Command name -/// - args: (string) The args passed to the command, if any |<args>| +/// - args: (string) The args passed to the command, if any [<args>] /// - fargs: (table) The args split by unescaped whitespace (when more than one -/// argument is allowed), if any |<f-args>| +/// argument is allowed), if any [<f-args>] /// - nargs: (string) Number of arguments |:command-nargs| -/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>| -/// - line1: (number) The starting line of the command range |<line1>| -/// - line2: (number) The final line of the command range |<line2>| -/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>| -/// - count: (number) Any count supplied |<count>| -/// - reg: (string) The optional register, if specified |<reg>| -/// - mods: (string) Command modifiers, if any |<mods>| +/// - bang: (boolean) "true" if the command was executed with a ! modifier [<bang>] +/// - line1: (number) The starting line of the command range [<line1>] +/// - line2: (number) The final line of the command range [<line2>] +/// - range: (number) The number of items in the command range: 0, 1, or 2 [<range>] +/// - count: (number) Any count supplied [<count>] +/// - reg: (string) The optional register, if specified [<reg>] +/// - mods: (string) Command modifiers, if any [<mods>] /// - smods: (table) Command modifiers in a structured format. Has the same /// structure as the "mods" key of |nvim_parse_cmd()|. /// @param opts Optional |command-attributes|. diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e35840915f..b5f56d270c 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -43,7 +43,7 @@ void api_extmark_free_all_mem(void) map_destroy(String, &namespace_ids); } -/// Creates a new namespace or gets an existing one. \*namespace\* +/// Creates a new namespace or gets an existing one. [namespace]() /// /// Namespaces are used for buffer highlights and virtual text, see /// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. @@ -284,7 +284,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// their start position is less than `start` /// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines" /// @param[out] err Error details, if any -/// @return List of [extmark_id, row, col] tuples in "traversal order". +/// @return List of `[extmark_id, row, col]` tuples in "traversal order". Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end, Dict(get_extmarks) *opts, Arena *arena, Error *err) FUNC_API_SINCE(7) @@ -390,7 +390,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// of the screen line (just like for diff and /// cursorline highlight). /// - virt_text : virtual text to link to this mark. -/// A list of [text, highlight] tuples, each representing a +/// A list of `[text, highlight]` tuples, each representing a /// text chunk with specified highlight. `highlight` element /// can either be a single highlight group, or an array of /// multiple highlight groups that will be stacked @@ -425,7 +425,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// /// - virt_lines : virtual lines to add next to this mark /// This should be an array over lines, where each line in -/// turn is an array over [text, highlight] tuples. In +/// turn is an array over `[text, highlight]` tuples. In /// general, buffer and window options do not affect the /// display of the text. In particular 'wrap' /// and 'linebreak' options do not take effect, so @@ -682,7 +682,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; }); - size_t len = 0; + colnr_T len = 0; if (HAS_KEY(opts, set_extmark, spell)) { hl.flags |= (opts->spell) ? kSHSpellOn : kSHSpellOff; @@ -712,16 +712,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); line = buf->b_ml.ml_line_count; } else if (line < buf->b_ml.ml_line_count) { - len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1)); + len = opts->ephemeral ? MAXCOL : ml_get_buf_len(buf, (linenr_T)line + 1); } if (col == -1) { - col = (Integer)len; - } else if (col > (Integer)len) { + col = len; + } else if (col > len) { VALIDATE_RANGE(!strict, "col", { goto error; }); - col = (Integer)len; + col = len; } else if (col < -1) { VALIDATE_RANGE(false, "col", { goto error; @@ -730,7 +730,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { - len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1)); + len = opts->ephemeral ? MAXCOL : ml_get_buf_len(buf, (linenr_T)line2 + 1); } else if (line2 == buf->b_ml.ml_line_count) { // We are trying to add an extmark past final newline len = 0; @@ -738,11 +738,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // reuse len from before line2 = (int)line; } - if (col2 > (Integer)len) { + if (col2 > len) { VALIDATE_RANGE(!strict, "end_col", { goto error; }); - col2 = (int)len; + col2 = len; } } else if (line2 >= 0) { col2 = 0; @@ -1040,17 +1040,27 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param opts Table of callbacks: /// - on_start: called first on each screen redraw +/// ``` /// ["start", tick] +/// ``` /// - on_buf: called for each buffer being redrawn (before -/// window callbacks) +/// window callbacks) +/// ``` /// ["buf", bufnr, tick] +/// ``` /// - on_win: called when starting to redraw a specific window. +/// ``` /// ["win", winid, bufnr, topline, botline] +/// ``` /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) +/// ``` /// ["line", winid, bufnr, row] +/// ``` /// - on_end: called at the end of a redraw cycle +/// ``` /// ["end", tick] +/// ``` void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY { @@ -1236,7 +1246,7 @@ Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting_win(win); + changed_window_setting(win); return true; } @@ -1281,7 +1291,7 @@ Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting_win(win); + changed_window_setting(win); return true; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 0cdc90e50f..ca088d7a55 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -105,6 +105,14 @@ typedef enum { kObjectTypeTabpage, } ObjectType; +/// Value by which objects represented as EXT type are shifted +/// +/// Subtracted when packing, added when unpacking. Used to allow moving +/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be +/// split or reordered. +#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer +#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT) + struct object { ObjectType type; union { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 97ba15e871..a17e78cc31 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -29,7 +29,6 @@ #include "nvim/memory.h" #include "nvim/memory_defs.h" #include "nvim/message.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" @@ -525,10 +524,10 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col } char *bufstr = ml_get_buf(buf, (linenr_T)lnum); - size_t line_length = strlen(bufstr); + colnr_T line_length = ml_get_buf_len(buf, (linenr_T)lnum); - start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col; - end_col = end_col < 0 ? (int64_t)line_length + end_col + 1 : end_col; + start_col = start_col < 0 ? line_length + start_col + 1 : start_col; + end_col = end_col < 0 ? line_length + end_col + 1 : end_col; if (start_col >= MAXCOL || end_col >= MAXCOL) { api_set_error(err, kErrorTypeValidation, "Column index is too high"); @@ -540,7 +539,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col return rv; } - if ((size_t)start_col >= line_length) { + if (start_col >= line_length) { return rv; } @@ -984,7 +983,7 @@ Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, val = DICTIONARY_OBJ(*(Dictionary *)mem); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { - val.data.integer = *(Integer *)mem; + val.data.integer = *(handle_T *)mem; val.type = field->type; } else if (field->type == kObjectTypeLuaRef) { // do nothing diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 040abb1e3f..56a3f1cf23 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -146,7 +146,11 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err) } if (tp == curtab) { - win_enter(wp, true); + try_start(); + win_goto(wp); + if (!try_end(err) && curwin != wp) { + api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win); + } } else if (tp->tp_curwin != wp) { tp->tp_prevwin = tp->tp_curwin; tp->tp_curwin = wp; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 0f016d2f29..692e3f95fc 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" +#include "nvim/assert_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" #include "nvim/channel.h" @@ -33,12 +34,12 @@ #include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/option.h" #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) +#define BUF_POS(ui) ((size_t)((ui)->packer.ptr - (ui)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -47,55 +48,6 @@ static PMap(uint64_t) connected_uis = MAP_INIT; -#define mpack_w(b, byte) *(*(b))++ = (char)(byte); -static void mpack_w2(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_w4(char **b, uint32_t v) -{ - *(*b)++ = (char)((v >> 24) & 0xff); - *(*b)++ = (char)((v >> 16) & 0xff); - *(*b)++ = (char)((v >> 8) & 0xff); - *(*b)++ = (char)(v & 0xff); -} - -static void mpack_uint(char **buf, uint32_t val) -{ - if (val > 0xffff) { - mpack_w(buf, 0xce); - mpack_w4(buf, val); - } else if (val > 0xff) { - mpack_w(buf, 0xcd); - mpack_w2(buf, val); - } else if (val > 0x7f) { - mpack_w(buf, 0xcc); - mpack_w(buf, val); - } else { - mpack_w(buf, val); - } -} - -static void mpack_bool(char **buf, bool val) -{ - mpack_w(buf, 0xc2 | (val ? 1 : 0)); -} - -static void mpack_array(char **buf, uint32_t len) -{ - if (len < 0x10) { - mpack_w(buf, 0x90 | len); - } else if (len < 0x10000) { - mpack_w(buf, 0xdc); - mpack_w2(buf, len); - } else { - mpack_w(buf, 0xdd); - mpack_w4(buf, len); - } -} - static char *mpack_array_dyn16(char **buf) { mpack_w(buf, 0xdc); @@ -104,26 +56,26 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str, size_t len) +static void mpack_str_small(char **buf, const char *str, size_t len) { - assert(sizeof(schar_T) - 1 < 0x20); + assert(len < 0x20); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; } -static void remote_ui_destroy(UI *ui) +static void remote_ui_destroy(RemoteUI *ui) FUNC_ATTR_NONNULL_ALL { - UIData *data = ui->data; - kv_destroy(data->call_buf); + kv_destroy(ui->call_buf); + xfree(ui->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); } void remote_ui_disconnect(uint64_t channel_id) { - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } @@ -135,7 +87,7 @@ void remote_ui_disconnect(uint64_t channel_id) #ifdef EXITFREE void remote_ui_free_all_mem(void) { - UI *ui; + RemoteUI *ui; map_foreach_value(&connected_uis, ui, { remote_ui_destroy(ui); }); @@ -193,7 +145,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona "Expected width > 0 and height > 0"); return; } - UI *ui = xcalloc(1, sizeof(UI)); + RemoteUI *ui = xcalloc(1, sizeof(RemoteUI)); ui->width = (int)width; ui->height = (int)height; ui->pum_row = -1.0; @@ -220,22 +172,26 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona ui->ui_ext[kUICmdline] = true; } - UIData *data = ui->data; - data->channel_id = channel_id; - data->cur_event = NULL; - data->hl_id = 0; - data->client_col = -1; - data->nevents_pos = NULL; - data->nevents = 0; - data->flushed_events = false; - data->ncalls_pos = NULL; - data->ncalls = 0; - data->ncells_pending = 0; - data->buf_wptr = data->buf; - data->temp_buf = NULL; - data->wildmenu_active = false; - data->call_buf = (Array)ARRAY_DICT_INIT; - kv_ensure_space(data->call_buf, 16); + ui->channel_id = channel_id; + ui->cur_event = NULL; + ui->hl_id = 0; + ui->client_col = -1; + ui->nevents_pos = NULL; + ui->nevents = 0; + ui->flushed_events = false; + ui->ncalls_pos = NULL; + ui->ncalls = 0; + ui->ncells_pending = 0; + ui->packer = (PackerBuffer) { + .startptr = NULL, + .ptr = NULL, + .endptr = NULL, + .packer_flush = ui_flush_callback, + .anydata = ui, + }; + ui->wildmenu_active = false; + ui->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(ui->call_buf, 16); pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); @@ -287,7 +243,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) } // TODO(bfredl): use me to detach a specific ui from the server -void remote_ui_stop(UI *ui) +void remote_ui_stop(RemoteUI *ui) { } @@ -306,7 +262,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Erro return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); @@ -320,12 +276,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui_set_option(ui, false, name, value, error); } -static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err) +static void ui_set_option(RemoteUI *ui, bool init, String name, Object value, Error *err) { if (strequal(name.data, "override")) { VALIDATE_T("override", kObjectTypeBoolean, value.type, { @@ -454,7 +410,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I } /// Tells Nvim the number of elements displaying in the popupmenu, to decide -/// <PageUp> and <PageDown> movement. +/// [<PageUp>] and [<PageDown>] movement. /// /// @param channel_id /// @param height Popupmenu height, must be greater than zero. @@ -473,7 +429,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "It must support the ext_popupmenu option"); @@ -509,7 +465,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "UI must support the ext_popupmenu option"); @@ -558,132 +514,75 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * } } -static void flush_event(UIData *data) -{ - if (data->cur_event) { - mpack_w2(&data->ncalls_pos, data->ncalls); - data->cur_event = NULL; - } - if (!data->nevents_pos) { - assert(BUF_POS(data) == 0); - char **buf = &data->buf_wptr; - // [2, "redraw", [...]] - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - data->nevents_pos = mpack_array_dyn16(buf); - } -} - -static inline int write_cb(void *vdata, const char *buf, size_t len) +static void flush_event(RemoteUI *ui) { - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { - return 0; + if (ui->cur_event) { + mpack_w2(&ui->ncalls_pos, 1 + ui->ncalls); + ui->cur_event = NULL; + ui->ncalls_pos = NULL; + ui->ncalls = 0; } - - memcpy(data->buf_wptr, buf, len); - data->buf_wptr += len; - - return 0; } -static inline int size_cb(void *vdata, const char *buf, size_t len) +static void ui_alloc_buf(RemoteUI *ui) { - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - return 0; + ui->packer.startptr = alloc_block(); + ui->packer.ptr = ui->packer.startptr; + ui->packer.endptr = ui->packer.startptr + UI_BUF_SIZE; } -static void prepare_call(UI *ui, const char *name, size_t size_needed) +static void prepare_call(RemoteUI *ui, const char *name) { - UIData *data = ui->data; - size_t name_len = strlen(name); - const size_t overhead = name_len + 20; - bool oversized_message = size_needed + overhead > UI_BUF_SIZE; - - if (oversized_message || BUF_POS(data) > UI_BUF_SIZE - size_needed - overhead) { - remote_ui_flush_buf(ui); + if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(ui); } - if (oversized_message) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + name_len + size_needed); - data->buf_wptr = data->temp_buf; - char **buf = &data->buf_wptr; - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name, name_len); - return; + if (ui->packer.startptr == NULL) { + ui_alloc_buf(ui); } // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (!data->cur_event || !strequal(data->cur_event, name)) { - flush_event(data); - data->cur_event = name; - char **buf = &data->buf_wptr; - data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name, strlen(name)); - data->nevents++; - data->ncalls = 1; - return; + if (!ui->cur_event || !strequal(ui->cur_event, name)) { + char **buf = &ui->packer.ptr; + if (!ui->nevents_pos) { + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str_small(buf, S_LEN("redraw")); + ui->nevents_pos = mpack_array_dyn16(buf); + assert(ui->cur_event == NULL); + } + flush_event(ui); + ui->cur_event = name; + ui->ncalls_pos = mpack_array_dyn16(buf); + mpack_str_small(buf, name, strlen(name)); + ui->nevents++; + ui->ncalls = 1; + } else { + ui->ncalls++; } } -static void send_oversized_message(UIData *data) +/// Pushes data into RemoteUI, to be consumed later by remote_ui_flush(). +static void push_call(RemoteUI *ui, const char *name, Array args) { - if (data->temp_buf) { - size_t size = (size_t)(data->buf_wptr - data->temp_buf); - WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->temp_buf = NULL; - data->buf_wptr = data->buf; - data->nevents_pos = NULL; - } + prepare_call(ui, name); + mpack_object_array(args, &ui->packer); } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +static void ui_flush_callback(PackerBuffer *packer) { - UIData *data = ui->data; - - msgpack_packer pac; - data->pack_totlen = 0; - // First determine the needed size - msgpack_packer_init(&pac, data, size_cb); - msgpack_rpc_from_array(args, &pac); - // Then send the actual message - prepare_call(ui, name, data->pack_totlen); - msgpack_packer_init(&pac, data, write_cb); - msgpack_rpc_from_array(args, &pac); - - // Oversized messages need to be sent immediately - if (data->temp_buf) { - send_oversized_message(data); - } - - data->ncalls++; + RemoteUI *ui = packer->anydata; + ui_flush_buf(ui); + ui_alloc_buf(ui); } -void remote_ui_grid_clear(UI *ui, Integer grid) +void remote_ui_grid_clear(RemoteUI *ui, Integer grid) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } @@ -691,14 +590,13 @@ void remote_ui_grid_clear(UI *ui, Integer grid) push_call(ui, name, args); } -void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) +void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } else { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); @@ -706,12 +604,11 @@ void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) push_call(ui, name, args); } -void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, +void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { - UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot)); @@ -721,20 +618,20 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot - 1)); ADD_C(args, INTEGER_OBJ(left)); ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(0)); ADD_C(args, INTEGER_OBJ(ui->height - 1)); ADD_C(args, INTEGER_OBJ(0)); @@ -743,14 +640,13 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ } } -void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, +void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) { if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rgb_fg)); ADD_C(args, INTEGER_OBJ(rgb_bg)); ADD_C(args, INTEGER_OBJ(rgb_sp)); @@ -760,29 +656,28 @@ void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Intege // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } -void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, +void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) { if (!ui->ui_ext[kUILinegrid]) { return; } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(id)); MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE); MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); @@ -808,15 +703,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte push_call(ui, "hl_attr_define", args); } -void remote_ui_highlight_set(UI *ui, int id) +void remote_ui_highlight_set(RemoteUI *ui, int id) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; - if (data->hl_id == id) { + if (ui->hl_id == id) { return; } - data->hl_id = id; + ui->hl_id = id; MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); ADD_C(args, DICTIONARY_OBJ(dict)); @@ -824,57 +718,55 @@ void remote_ui_highlight_set(UI *ui, int id) } /// "true" cursor used only for input focus -void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) +void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { - UIData *data = ui->data; - data->cursor_row = row; - data->cursor_col = col; + ui->cursor_row = row; + ui->cursor_col = col; remote_ui_cursor_goto(ui, row, col); } } /// emulated cursor used both for drawing and for input focus -void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) +void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) { - UIData *data = ui->data; - if (data->client_row == row && data->client_col == col) { + if (ui->client_row == row && ui->client_col == col) { return; } - data->client_row = row; - data->client_col = col; - Array args = data->call_buf; + ui->client_row = row; + ui->client_col = col; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } -void remote_ui_put(UI *ui, const char *cell) +void remote_ui_put(RemoteUI *ui, const char *cell) { - UIData *data = ui->data; - data->client_col++; - Array args = data->call_buf; + ui->client_col++; + Array args = ui->call_buf; ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } -void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, +void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { - UIData *data = ui->data; + // If MAX_SCHAR_SIZE is made larger, we need to refactor implementation below + // to not only use FIXSTR (only up to 0x20 bytes) + STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); + if (ui->ui_ext[kUILinegrid]) { - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); - char **buf = &data->buf_wptr; + char **buf = &ui->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -889,7 +781,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { + if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { // close to overflowing the redraw buffer. finish this event, // flush, and start a new "grid_line" event at the current position. // For simplicity leave place for the final "clear" element @@ -898,10 +790,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - remote_ui_flush_buf(ui); + ui_flush_buf(ui); - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -922,7 +813,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_uint(buf, repeat); } } - data->ncells_pending += MIN(repeat, 2); + ui->ncells_pending += MIN(repeat, 2); last_hl = attrs[i]; repeat = 0; was_space = chunk[i] == schar_from_ascii(' '); @@ -932,18 +823,18 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // no more cells to clear, so there is no ambiguity about what to clear. if (endcol < clearcol || was_space) { nelem++; - data->ncells_pending += 1; + ui->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, S_LEN(" ")); + mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } mpack_w2(&lenpos, nelem); mpack_bool(buf, flags & kLineFlagWrap); - if (data->ncells_pending > 500) { + if (ui->ncells_pending > 500) { // pass off cells to UI to let it start processing them - remote_ui_flush_buf(ui); + ui_flush_buf(ui); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -953,7 +844,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int schar_get(sc_buf, chunk[i]); remote_ui_put(ui, sc_buf); if (utf_ambiguous_width(utf_ptr2char(sc_buf))) { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } } if (endcol < clearcol) { @@ -977,49 +868,47 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -void remote_ui_flush_buf(UI *ui) +static void ui_flush_buf(RemoteUI *ui) { - UIData *data = ui->data; - if (!data->nevents_pos) { + if (!ui->packer.startptr || !BUF_POS(ui)) { return; } - if (data->cur_event) { - flush_event(data); + + flush_event(ui); + if (ui->nevents_pos != NULL) { + mpack_w2(&ui->nevents_pos, ui->nevents); + ui->nevents = 0; + ui->nevents_pos = NULL; } - mpack_w2(&data->nevents_pos, data->nevents); - data->nevents = 0; - data->nevents_pos = NULL; - - // TODO(bfredl): elide copy by a length one free-list like the arena - size_t size = BUF_POS(data); - WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); - rpc_write_raw(data->channel_id, buf); - data->buf_wptr = data->buf; - // we have sent events to the client, but possibly not yet the final "flush" - // event. - data->flushed_events = true; - - data->ncells_pending = 0; + + WBuffer *buf = wstream_new_buffer(ui->packer.startptr, BUF_POS(ui), 1, free_block); + rpc_write_raw(ui->channel_id, buf); + + ui->packer.startptr = NULL; + ui->packer.ptr = NULL; + + // we have sent events to the client, but possibly not yet the final "flush" event. + ui->flushed_events = true; + ui->ncells_pending = 0; } /// An intentional flush (vsync) when Nvim is finished redrawing the screen /// /// Clients can know this happened by a final "flush" event at the end of the /// "redraw" batch. -void remote_ui_flush(UI *ui) +void remote_ui_flush(RemoteUI *ui) { - UIData *data = ui->data; - if (data->nevents > 0 || data->flushed_events) { + if (ui->nevents > 0 || ui->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { - remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); + remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - remote_ui_flush_buf(ui); - data->flushed_events = false; + ui_flush_buf(ui); + ui->flushed_events = false; } } -static Array translate_contents(UI *ui, Array contents, Arena *arena) +static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena) { Array new_contents = arena_array(arena, contents.size); for (size_t i = 0; i < contents.size; i++) { @@ -1039,7 +928,7 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) return new_contents; } -static Array translate_firstarg(UI *ui, Array args, Arena *arena) +static Array translate_firstarg(RemoteUI *ui, Array args, Arena *arena) { Array new_args = arena_array(arena, args.size); Array contents = args.items[0].data.array; @@ -1051,10 +940,9 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) return new_args; } -void remote_ui_event(UI *ui, char *name, Array args) +void remote_ui_event(RemoteUI *ui, char *name, Array args) { Arena arena = ARENA_EMPTY; - UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args @@ -1063,7 +951,7 @@ void remote_ui_event(UI *ui, char *name, Array args) push_call(ui, name, new_args); goto free_ret; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = data->call_buf; + Array new_args = ui->call_buf; Array block = args.items[0].data.array; Array new_block = arena_array(&arena, block.size); for (size_t i = 0; i < block.size; i++) { @@ -1082,10 +970,10 @@ void remote_ui_event(UI *ui, char *name, Array args) // Back-compat: translate popupmenu_xx to legacy wildmenu_xx. if (ui->ui_ext[kUIWildmenu]) { if (strequal(name, "popupmenu_show")) { - data->wildmenu_active = (args.items[4].data.integer == -1) - || !ui->ui_ext[kUIPopupmenu]; - if (data->wildmenu_active) { - Array new_args = data->call_buf; + ui->wildmenu_active = (args.items[4].data.integer == -1) + || !ui->ui_ext[kUIPopupmenu]; + if (ui->wildmenu_active) { + Array new_args = ui->call_buf; Array items = args.items[0].data.array; Array new_items = arena_array(&arena, items.size); for (size_t i = 0; i < items.size; i++) { @@ -1094,18 +982,18 @@ void remote_ui_event(UI *ui, char *name, Array args) ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); if (args.items[1].data.integer != -1) { - Array new_args2 = data->call_buf; + Array new_args2 = ui->call_buf; ADD_C(new_args2, args.items[1]); push_call(ui, "wildmenu_select", new_args2); } goto free_ret; } } else if (strequal(name, "popupmenu_select")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_select"; } } else if (strequal(name, "popupmenu_hide")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_hide"; } } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 6a3c6bab67..24ad7d5fbc 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -249,7 +249,7 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically +/// To input sequences like [<C-o>] use |nvim_replace_termcodes()| (typically /// with escape_ks=false) to replace |keycodes|, then pass the result to /// nvim_feedkeys(). /// @@ -337,11 +337,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// @note |keycodes| like <CR> are translated, so "<" is special. -/// To input a literal "<", send <LT>. +/// @note |keycodes| like [<CR>] are translated, so "<" is special. +/// To input a literal "<", send [<LT>]. /// /// @note For mouse events use |nvim_input_mouse()|. The pseudokey form -/// "<LeftMouse><col,row>" is deprecated since |api-level| 6. +/// `<LeftMouse><col,row>` is deprecated since |api-level| 6. /// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than @@ -362,7 +362,7 @@ Integer nvim_input(String keys) /// by calling it multiple times in a loop: the intermediate mouse /// positions will be ignored. It should be used to implement real-time /// mouse input in a GUI. The deprecated pseudokey form -/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation. +/// (`<LeftMouse><col,row>`) of |nvim_input()| has the same limitation. /// /// @param button Mouse button: one of "left", "right", "middle", "wheel", "move", /// "x1", "x2". @@ -451,13 +451,13 @@ error: "invalid button or action"); } -/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with +/// Replaces terminal codes and |keycodes| ([<CR>], [<Esc>], ...) in a string with /// the internal representation. /// /// @param str String to be converted. /// @param from_part Legacy Vim parameter. Usually true. -/// @param do_lt Also translate <lt>. Ignored if `special` is false. -/// @param special Replace |keycodes|, e.g. <CR> becomes a "\r" char. +/// @param do_lt Also translate [<lt>]. Ignored if `special` is false. +/// @param special Replace |keycodes|, e.g. [<CR>] becomes a "\r" char. /// @see replace_termcodes /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) @@ -525,7 +525,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, } /// Calculates the number of display cells occupied by `text`. -/// Control characters including <Tab> count as one cell. +/// Control characters including [<Tab>] count as one cell. /// /// @param text Some text /// @param[out] err Error details, if any @@ -762,7 +762,7 @@ void nvim_set_vvar(String name, Object value, Error *err) /// Echo a message. /// -/// @param chunks A list of [text, hl_group] arrays, each representing a +/// @param chunks A list of `[text, hl_group]` arrays, each representing a /// text chunk with specified highlight. `hl_group` element /// can be omitted for no highlight. /// @param history if true, add to |message-history|. @@ -876,6 +876,11 @@ void nvim_set_current_buf(Buffer buffer, Error *err) return; } + if (curwin->w_p_wfb) { + api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer); + return; + } + try_start(); int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0); if (!try_end(err) && result == FAIL) { @@ -1019,7 +1024,7 @@ fail: /// master end. For instance, a carriage return is sent /// as a "\r", not as a "\n". |textlock| applies. It is possible /// to call |nvim_chan_send()| directly in the callback however. -/// ["input", term, bufnr, data] +/// `["input", term, bufnr, data]` /// - force_crlf: (boolean, default true) Convert "\n" to "\r\n". /// @param[out] err Error details, if any /// @return Channel id, or 0 on error @@ -1471,7 +1476,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) /// To set a buffer-local mapping, use |nvim_buf_set_keymap()|. /// /// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}. -/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual. +/// Empty {rhs} is [<Nop>]. |keycodes| are replaced as usual. /// /// Example: /// @@ -1491,7 +1496,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) /// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. -/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|, +/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except [<buffer>], /// values are booleans (default false). Also: /// - "noremap" disables |recursive_mapping|, like |:noremap| /// - "desc" human-readable description. @@ -1521,7 +1526,7 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err) /// Returns a 2-tuple (Array), where item 0 is the current channel id and item /// 1 is the |api-metadata| map (Dictionary). /// -/// @returns 2-tuple [{channel-id}, {api-metadata}] +/// @returns 2-tuple `[{channel-id}, {api-metadata}]` Array nvim_get_api_info(uint64_t channel_id, Arena *arena) FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY { @@ -1960,7 +1965,7 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) /// If neither |ins-completion| nor |cmdline-completion| popup menu is active /// this API call is silently ignored. /// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse. -/// Can also be used in a mapping; use <Cmd> |:map-cmd| or a Lua mapping to ensure the mapping +/// Can also be used in a mapping; use [<Cmd>] |:map-cmd| or a Lua mapping to ensure the mapping /// doesn't end completion mode. /// /// @param item Index (zero-based) of the item to select. Value of -1 selects nothing diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 8f57e61c76..477cbe2428 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -365,8 +365,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "l" when needing to start parsing with lvalues for /// ":let" or ":for". /// Common flag sets: -/// - "m" to parse like for ":echo". -/// - "E" to parse like for "<C-r>=". +/// - "m" to parse like for `":echo"`. +/// - "E" to parse like for `"<C-r>="`. /// - empty string for ":call". /// - "lm" to parse for ":let". /// @param[in] highlight If true, return value will also include "highlight" @@ -390,7 +390,7 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "ast": AST, either nil or a dictionary with these keys: /// - "type": node type, one of the value names from ExprASTNodeType /// stringified without "kExprNode" prefix. -/// - "start": a pair [line, column] describing where node is "started" +/// - "start": a pair `[line, column]` describing where node is "started" /// where "line" is always 0 (will not be 0 if you will be /// using this API on e.g. ":let", but that is not /// present yet). Both elements are Integers. diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 3cc520dc78..43cff9b2c3 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -12,6 +12,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" +#include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" #include "nvim/decoration_defs.h" @@ -116,7 +117,7 @@ /// - width: Window width (in character cells). Minimum of 1. /// - height: Window height (in character cells). Minimum of 1. /// - bufpos: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed [line, column]. +/// relative="win"). Takes a tuple of zero-indexed `[line, column]`. /// `row` and `col` if given are applied relative to this /// position, else they default to: /// - `row=1` and `col=0` if `anchor` is "NW" or "NE" @@ -198,9 +199,9 @@ /// - footer_pos: Footer position. Must be set with `footer` option. /// Value can be one of "left", "center", or "right". /// Default is `"left"`. -/// - noautocmd: If true then no buffer-related autocommand events such as -/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from -/// calling this function. +/// - noautocmd: If true then autocommands triggered from setting the +/// `buffer` to display are blocked (e.g: |BufEnter|, |BufLeave|, +/// |BufWinEnter|). /// - fixed: If true when anchor is NW or SW, the float window /// would be kept fixed even if the window would be truncated. /// - hide: If true the floating window will be hidden. @@ -245,6 +246,10 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err } } + if (!check_split_disallowed_err(parent ? parent : curwin, err)) { + return 0; // error already set + } + if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) { if (config->vertical) { fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft; @@ -254,18 +259,20 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err } int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; - if (parent == NULL) { - wp = win_split_ins(0, flags, NULL, 0); - } else { - tp = win_find_tabpage(parent); - switchwin_T switchwin; - // `parent` is valid in `tp`, so switch_win should not fail. - const int result = switch_win(&switchwin, parent, tp, true); - (void)result; - assert(result == OK); - wp = win_split_ins(0, flags, NULL, 0); - restore_win(&switchwin, true); - } + TRY_WRAP(err, { + if (parent == NULL || parent == curwin) { + wp = win_split_ins(0, flags, NULL, 0, NULL); + } else { + tp = win_find_tabpage(parent); + switchwin_T switchwin; + // `parent` is valid in `tp`, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, tp, true); + assert(result == OK); + (void)result; + wp = win_split_ins(0, flags, NULL, 0, NULL); + restore_win(&switchwin, true); + } + }); if (wp) { wp->w_config = fconfig; } @@ -273,21 +280,49 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err wp = win_new_float(NULL, false, fconfig, err); } if (!wp) { - api_set_error(err, kErrorTypeException, "Failed to create window"); + if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, "Failed to create window"); + } return 0; } + + // Autocommands may close `wp` or move it to another tabpage, so update and check `tp` after each + // event. In each case, `wp` should already be valid in `tp`, so switch_win should not fail. + // Also, autocommands may free the `buf` to switch to, so store a bufref to check. + bufref_T bufref; + set_bufref(&bufref, buf); switchwin_T switchwin; - if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { - apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); + { + const int result = switch_win_noblock(&switchwin, wp, tp, true); + assert(result == OK); + (void)result; + if (apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf)) { + tp = win_find_tabpage(wp); + } + restore_win_noblock(&switchwin, true); } - restore_win_noblock(&switchwin, true); - if (enter) { + if (tp && enter) { goto_tabpage_win(tp, wp); + tp = win_find_tabpage(wp); } - if (win_valid_any_tab(wp) && buf != wp->w_buffer) { - win_set_buf(wp, buf, !enter || fconfig.noautocmd, err); + if (tp && bufref_valid(&bufref) && buf != wp->w_buffer) { + // win_set_buf temporarily makes `wp` the curwin to set the buffer. + // If not entering `wp`, block Enter and Leave events. (cringe) + const bool au_no_enter_leave = curwin != wp && !fconfig.noautocmd; + if (au_no_enter_leave) { + autocmd_no_enter++; + autocmd_no_leave++; + } + win_set_buf(wp, buf, fconfig.noautocmd, err); + if (!fconfig.noautocmd) { + tp = win_find_tabpage(wp); + } + if (au_no_enter_leave) { + autocmd_no_enter--; + autocmd_no_leave--; + } } - if (!win_valid_any_tab(wp)) { + if (!tp) { api_set_error(err, kErrorTypeException, "Window was closed immediately"); return 0; } @@ -330,11 +365,11 @@ static int win_split_flags(WinSplit split, bool toplevel) return flags; } -/// Configures window layout. Currently only for floating and external windows -/// (including changing a split window to those layouts). +/// Configures window layout. Cannot be used to move the last window in a +/// tabpage to a different one. /// -/// When reconfiguring a floating window, absent option keys will not be -/// changed. `row`/`col` and `relative` must be reconfigured together. +/// When reconfiguring a window, absent option keys will not be changed. +/// `row`/`col` and `relative` must be reconfigured together. /// /// @see |nvim_open_win()| /// @@ -413,17 +448,59 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) return; } - if (was_split) { - win_T *new_curwin = NULL; + if (!check_split_disallowed_err(win, err)) { + return; // error already set + } + // Can't move the cmdwin or its old curwin to a different tabpage. + if ((win == cmdwin_win || win == cmdwin_old_curwin) && parent != NULL + && win_find_tabpage(parent) != win_tp) { + api_set_error(err, kErrorTypeException, "%s", e_cmdwin); + return; + } + + bool to_split_ok = false; + // If we are moving curwin to another tabpage, switch windows *before* we remove it from the + // window list or remove its frame (if non-floating), so it's valid for autocommands. + const bool curwin_moving_tp + = win == curwin && parent != NULL && win_tp != win_find_tabpage(parent); + if (curwin_moving_tp) { + if (was_split) { + int dir; + win_goto(winframe_find_altwin(win, &dir, NULL, NULL)); + } else { + win_goto(win_float_find_altwin(win, NULL)); + } + // Autocommands may have been a real nuisance and messed things up... + if (curwin == win) { + api_set_error(err, kErrorTypeException, "Failed to switch away from window %d", + win->handle); + return; + } + win_tp = win_find_tabpage(win); + if (!win_tp || !win_valid_any_tab(parent)) { + api_set_error(err, kErrorTypeException, "Windows to split were closed"); + goto restore_curwin; + } + if (was_split == win->w_floating || parent->w_floating) { + api_set_error(err, kErrorTypeException, "Floating state of windows to split changed"); + goto restore_curwin; + } + } + + int dir = 0; + frame_T *unflat_altfr = NULL; + win_T *altwin = NULL; + + if (was_split) { // If the window is the last in the tabpage or `fconfig.win` is // a handle to itself, we can't split it. if (win->w_frame->fr_parent == NULL) { // FIXME(willothy): if the window is the last in the tabpage but there is another tabpage // and the target window is in that other tabpage, should we move the window to that // tabpage and close the previous one, or just error? - api_set_error(err, kErrorTypeValidation, "Cannot move last window"); - return; + api_set_error(err, kErrorTypeException, "Cannot move last window"); + goto restore_curwin; } else if (parent != NULL && parent->handle == win->handle) { int n_frames = 0; for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) { @@ -459,83 +536,82 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) } // If the frame doesn't have a parent, the old frame // was the root frame and we need to create a top-level split. - int dir; - new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); } else if (n_frames == 2) { // There are two windows in the frame, we can just rotate it. - int dir; - neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); - new_curwin = neighbor; + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); + neighbor = altwin; } else { // There is only one window in the frame, we can't split it. - api_set_error(err, kErrorTypeValidation, "Cannot split window into itself"); - return; + api_set_error(err, kErrorTypeException, "Cannot split window into itself"); + goto restore_curwin; } - // Set the parent to whatever the correct - // neighbor window was determined to be. + // Set the parent to whatever the correct neighbor window was determined to be. parent = neighbor; } else { - int dir; - new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); } - // move to neighboring window if we're moving the current window to a new tabpage - if (curwin == win && parent != NULL && new_curwin != NULL - && win_tp != win_find_tabpage(parent)) { - win_enter(new_curwin, true); - } - win_remove(win, win_tp == curtab ? NULL : win_tp); } else { - win_remove(win, win_tp == curtab ? NULL : win_tp); - ui_comp_remove_grid(&win->w_grid_alloc); - if (win->w_config.external) { - for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - if (tp == curtab) { - continue; - } - if (tp->tp_curwin == win) { - tp->tp_curwin = tp->tp_firstwin; - } - } - } - win->w_pos_changed = true; + altwin = win_float_find_altwin(win, win_tp == curtab ? NULL : win_tp); } - int flags = win_split_flags(fconfig.split, parent == NULL); + win_remove(win, win_tp == curtab ? NULL : win_tp); + if (win_tp == curtab) { + last_status(false); // may need to remove last status line + win_comp_pos(); // recompute window positions + } - if (parent == NULL) { - if (!win_split_ins(0, flags, win, 0)) { - // TODO(willothy): What should this error message say? - api_set_error(err, kErrorTypeException, "Failed to split window"); - return; - } - } else { - win_execute_T args; + int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; + tabpage_T *const parent_tp = parent ? win_find_tabpage(parent) : curtab; - tabpage_T *tp = win_find_tabpage(parent); - if (!win_execute_before(&args, parent, tp)) { - // TODO(willothy): how should we handle this / what should the message be? - api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle); - win_execute_after(&args); - return; + TRY_WRAP(err, { + const bool need_switch = parent != NULL && parent != curwin; + switchwin_T switchwin; + if (need_switch) { + // `parent` is valid in its tabpage, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, parent_tp, true); + (void)result; + assert(result == OK); } - // This should return the same ptr to `win`, but we check for - // NULL to detect errors. - win_T *res = win_split_ins(0, flags, win, 0); - win_execute_after(&args); - if (!res) { - // TODO(willothy): What should this error message say? - api_set_error(err, kErrorTypeException, "Failed to split window"); - return; + to_split_ok = win_split_ins(0, flags, win, 0, unflat_altfr) != NULL; + if (!to_split_ok) { + // Restore `win` to the window list now, so it's valid for restore_win (if used). + win_append(win->w_prev, win, win_tp == curtab ? NULL : win_tp); } + if (need_switch) { + restore_win(&switchwin, true); + } + }); + if (!to_split_ok) { + if (was_split) { + // win_split_ins doesn't change sizes or layout if it fails to insert an existing window, so + // just undo winframe_remove. + winframe_restore(win, dir, unflat_altfr); + } + if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, "Failed to move window %d into split", win->handle); + } + +restore_curwin: + // If `win` was the original curwin, and autocommands didn't move it outside of curtab, be a + // good citizen and try to return to it. + if (curwin_moving_tp && win_valid(win)) { + win_goto(win); + } + return; + } + + // If `win` moved tabpages and was the curwin of its old one, select a new curwin for it. + if (win_tp != parent_tp && win_tp->tp_curwin == win) { + win_tp->tp_curwin = altwin; } + if (HAS_KEY_X(config, width)) { win_setwidth_win(fconfig.width, win); } if (HAS_KEY_X(config, height)) { win_setheight_win(fconfig.height, win); } - redraw_later(win, UPD_NOT_VALID); - return; } else { win_config_float(win, fconfig); win->w_pos_changed = true; @@ -1071,11 +1147,15 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo fconfig->window = config->win; } } - } else if (has_relative) { - if (HAS_KEY_X(config, win)) { + } else if (HAS_KEY_X(config, win)) { + if (has_relative) { api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win' and relative=''"); return false; + } else if (!is_split) { + api_set_error(err, kErrorTypeValidation, + "non-float with 'win' requires at least 'split' or 'vertical'"); + return false; } } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ed51eedf1b..026d09d9a9 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -61,6 +61,12 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) if (!win || !buf) { return; } + + if (win->w_p_wfb) { + api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer); + return; + } + if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return; @@ -132,7 +138,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) win->w_cursor.col = (colnr_T)col; win->w_cursor.coladd = 0; // When column is out of range silently correct it. - check_cursor_col_win(win); + check_cursor_col(win); // Make sure we stick in this column. win->w_set_curswant = true; @@ -142,7 +148,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) switchwin_T switchwin; switch_win(&switchwin, win, NULL, true); update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); restore_win(&switchwin, true); redraw_later(win, UPD_VALID); diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index a02c22deae..4d493c9d03 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -623,6 +623,8 @@ void ex_argument(exarg_T *eap) /// Edit file "argn" of the argument lists. void do_argfile(exarg_T *eap, int argn) { + bool is_split_cmd = *eap->cmd == 's'; + int old_arg_idx = curwin->w_arg_idx; if (argn < 0 || argn >= ARGCOUNT) { @@ -637,10 +639,16 @@ void do_argfile(exarg_T *eap, int argn) return; } + if (!is_split_cmd + && (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum + && !check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + setpcmark(); // split window or create new tab page first - if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) { + if (is_split_cmd || cmdmod.cmod_tab != 0) { if (win_split(0, 0) == FAIL) { return; } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 3f93906942..285ef538b9 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1333,7 +1333,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) block_autocmds(); // We don't want BufEnter/WinEnter autocommands. if (need_append) { - win_append(lastwin, auc_win); + win_append(lastwin, auc_win, NULL); pmap_put(int)(&window_handles, auc_win->handle, auc_win); win_config_float(auc_win, auc_win->w_config); } @@ -1432,7 +1432,7 @@ win_found: // the buffer contents may have changed VIsual_active = aco->save_VIsual_active; - check_cursor(); + check_cursor(curwin); if (curwin->w_topline > curbuf->b_ml.ml_line_count) { curwin->w_topline = curbuf->b_ml.ml_line_count; curwin->w_topfill = 0; @@ -1464,12 +1464,12 @@ win_found: // In case the autocommand moves the cursor to a position that does not // exist in curbuf VIsual_active = aco->save_VIsual_active; - check_cursor(); + check_cursor(curwin); } } VIsual_active = aco->save_VIsual_active; - check_cursor(); // just in case lines got deleted + check_cursor(curwin); // just in case lines got deleted if (VIsual_active) { check_pos(curbuf, &VIsual); } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f6c7229485..3c2d52e6ad 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -117,7 +117,6 @@ # include "buffer.c.generated.h" #endif -static const char *e_auabort = N_("E855: Autocommands caused command to abort"); static const char e_attempt_to_delete_buffer_that_is_in_use_str[] = N_("E937: Attempt to delete a buffer that is in use: %s"); @@ -569,7 +568,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i } buf->b_locked--; buf->b_locked_split--; - if (abort_if_last && last_nonfloat(win)) { + if (abort_if_last && one_window(win)) { // Autocommands made this the only window. emsg(_(e_auabort)); return false; @@ -588,7 +587,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i } buf->b_locked--; buf->b_locked_split--; - if (abort_if_last && last_nonfloat(win)) { + if (abort_if_last && one_window(win)) { // Autocommands made this the only window. emsg(_(e_auabort)); return false; @@ -1306,6 +1305,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } return FAIL; } + + if (action == DOBUF_GOTO && buf != curbuf && !check_can_set_curbuf_forceit(forceit)) { + // disallow navigating to another buffer when 'winfixbuf' is applied + return FAIL; + } + if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) { // disallow navigating to the dummy buffer semsg(_(e_nobufnr), count); @@ -1747,7 +1752,7 @@ void enter_buffer(buf_T *buf) maketitle(); // when autocmds didn't change it if (curwin->w_topline == 1 && !curwin->w_topline_was_set) { - scroll_cursor_halfway(false, false); // redisplay at correct position + scroll_cursor_halfway(curwin, false, false); // redisplay at correct position } // Change directories when the 'acd' option is set. @@ -2167,7 +2172,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) // cursor is at to BOL and w_cursor.lnum is checked due to getfile() if (!p_sol && col != 0) { curwin->w_cursor.col = col; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } @@ -2192,7 +2197,7 @@ void buflist_getfpos(void) curwin->w_cursor.col = 0; } else { curwin->w_cursor.col = fpos->col; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } @@ -3252,7 +3257,7 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate) (int64_t)curwin->w_cursor.lnum, (int64_t)curbuf->b_ml.ml_line_count, n); - validate_virtcol(); + validate_virtcol(curwin); size_t len = strlen(buffer); col_print(buffer + len, IOSIZE - len, (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 1e5086309c..7f7300706c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -139,6 +139,8 @@ typedef struct { #define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit' OptInt wo_nuw; #define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth' + int wo_wfb; +#define w_p_wfb w_onebuf_opt.wo_wfb // 'winfixbuf' int wo_wfh; #define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight' int wo_wfw; diff --git a/src/nvim/change.c b/src/nvim/change.c index 1c7724f010..b6f2be547f 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -707,14 +707,14 @@ void ins_char(int c) void ins_char_bytes(char *buf, size_t charlen) { // Break tabs if needed. - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); } size_t col = (size_t)curwin->w_cursor.col; linenr_T lnum = curwin->w_cursor.lnum; char *oldp = ml_get(lnum); - size_t linelen = strlen(oldp) + 1; // length of old line including NUL + size_t linelen = (size_t)ml_get_len(lnum) + 1; // length of old line including NUL // The lengths default to the values for when not replacing. size_t oldlen = 0; // nr of bytes inserted @@ -815,13 +815,13 @@ void ins_str(char *s) int newlen = (int)strlen(s); linenr_T lnum = curwin->w_cursor.lnum; - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); } colnr_T col = curwin->w_cursor.col; char *oldp = ml_get(lnum); - int oldlen = (int)strlen(oldp); + int oldlen = ml_get_len(lnum); char *newp = xmalloc((size_t)oldlen + (size_t)newlen + 1); if (col > 0) { @@ -879,7 +879,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) colnr_T col = curwin->w_cursor.col; bool fixpos = fixpos_arg; char *oldp = ml_get(lnum); - colnr_T oldlen = (colnr_T)strlen(oldp); + colnr_T oldlen = ml_get_len(lnum); // Can't do anything when the cursor is on the NUL after the line. if (col >= oldlen) { @@ -918,7 +918,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) // fixpos is true, we don't want to end up positioned at the NUL, // unless "restart_edit" is set or 'virtualedit' contains "onemore". if (col > 0 && fixpos && restart_edit == 0 - && (get_ve_flags() & VE_ONEMORE) == 0) { + && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { curwin->w_cursor.col--; curwin->w_cursor.coladd = 0; curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col); @@ -926,21 +926,24 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) count = oldlen - col; movelen = 1; } + colnr_T newlen = oldlen - count; // If the old line has been allocated the deletion can be done in the // existing line. Otherwise a new line has to be allocated. - bool was_alloced = ml_line_alloced(); // check if oldp was allocated + bool alloc_newp = !ml_line_alloced(); // check if oldp was allocated char *newp; - if (was_alloced) { + if (!alloc_newp) { ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen); newp = oldp; // use same allocated memory } else { // need to allocate a new line - newp = xmalloc((size_t)(oldlen + 1 - count)); + newp = xmalloc((size_t)newlen + 1); memmove(newp, oldp, (size_t)col); } memmove(newp + col, oldp + col + count, (size_t)movelen); - if (!was_alloced) { + if (alloc_newp) { ml_replace(lnum, newp, false); + } else { + curbuf->b_ml.ml_line_len -= count; } // mark the buffer as changed and prepare for displaying @@ -1041,7 +1044,7 @@ bool copy_indent(int size, char *src) if (p == NULL) { // Allocate memory for the result: the copied indent, new indent // and the rest of the line. - line_len = (int)strlen(get_cursor_line_ptr()) + 1; + line_len = get_cursor_line_len() + 1; assert(ind_len + line_len >= 0); size_t line_size; STRICT_ADD(ind_len, line_len, &line_size, size_t); @@ -1114,7 +1117,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) colnr_T mincol = curwin->w_cursor.col + 1; // make a copy of the current line so we can mess with it - char *saved_line = xstrdup(get_cursor_line_ptr()); + char *saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); if (State & VREPLACE_FLAG) { // With MODE_VREPLACE we make a copy of the next line, which we will be @@ -1125,7 +1128,8 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // the line, replacing what was there before and pushing the right // stuff onto the replace stack. -- webb. if (curwin->w_cursor.lnum < orig_line_count) { - next_line = xstrdup(ml_get(curwin->w_cursor.lnum + 1)); + next_line = xstrnsave(ml_get(curwin->w_cursor.lnum + 1), + (size_t)ml_get_len(curwin->w_cursor.lnum + 1)); } else { next_line = xstrdup(""); } @@ -1861,7 +1865,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } if (did_append) { // bail out and just get the final length of the line we just manipulated - bcount_t extra = (bcount_t)strlen(ml_get(curwin->w_cursor.lnum)); + bcount_t extra = ml_get_len(curwin->w_cursor.lnum); extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0, 0, 0, 0, 1, 0, 1 + extra, kExtmarkUndo); changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1, true); @@ -1905,7 +1909,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // stuff onto the replace stack (via ins_char()). if (State & VREPLACE_FLAG) { // Put new line in p_extra - p_extra = xstrdup(get_cursor_line_ptr()); + p_extra = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); // Put back original line ml_replace(curwin->w_cursor.lnum, next_line, false); @@ -1932,19 +1936,16 @@ theend: /// If "fixpos" is true fix the cursor position when done. void truncate_line(int fixpos) { - char *newp; linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; + char *old_line = ml_get(lnum); + char *newp = col == 0 ? xstrdup("") : xstrnsave(old_line, (size_t)col); + int deleted = ml_get_len(lnum) - col; - if (col == 0) { - newp = xstrdup(""); - } else { - newp = xstrnsave(ml_get(lnum), (size_t)col); - } ml_replace(lnum, newp, false); // mark the buffer as changed and prepare for displaying - changed_bytes(lnum, curwin->w_cursor.col); + inserted_bytes(lnum, curwin->w_cursor.col, deleted, 0); // If "fixpos" is true we don't want to end up positioned at the NUL. if (fixpos && curwin->w_cursor.col > 0) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 20bd364c7e..2e6f24b2d5 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1457,10 +1457,20 @@ bool rem_backslash(const char *str) /// @param p void backslash_halve(char *p) { - for (; *p; p++) { - if (rem_backslash(p)) { - STRMOVE(p, p + 1); + for (; *p && !rem_backslash(p); p++) {} + if (*p != NUL) { + char *dst = p; + goto start; + while (*p != NUL) { + if (rem_backslash(p)) { +start: + *dst++ = *(p + 1); + p += 2; + } else { + *dst++ = *p++; + } } + *dst = '\0'; } } @@ -1472,8 +1482,16 @@ void backslash_halve(char *p) char *backslash_halve_save(const char *p) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - // TODO(philix): simplify and improve backslash_halve_save algorithm - char *res = xstrdup(p); - backslash_halve(res); + char *res = xmalloc(strlen(p) + 1); + char *dst = res; + while (*p != NUL) { + if (rem_backslash(p)) { + *dst++ = *(p + 1); + p += 2; + } else { + *dst++ = *p++; + } + } + *dst = '\0'; return res; } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index e93e658f1e..91bd217ae8 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -57,7 +57,7 @@ int getviscol2(colnr_T col, colnr_T coladd) /// The caller must have saved the cursor line for undo! int coladvance_force(colnr_T wcol) { - int rc = coladvance2(&curwin->w_cursor, true, false, wcol); + int rc = coladvance2(curwin, &curwin->w_cursor, true, false, wcol); if (wcol == MAXCOL) { curwin->w_valid &= ~VALID_VIRTCOL; @@ -76,25 +76,26 @@ int coladvance_force(colnr_T wcol) /// beginning at coladd 0. /// /// @return OK if desired column is reached, FAIL if not -int coladvance(colnr_T wcol) +int coladvance(win_T *wp, colnr_T wcol) { - int rc = getvpos(&curwin->w_cursor, wcol); + int rc = getvpos(wp, &wp->w_cursor, wcol); if (wcol == MAXCOL || rc == FAIL) { - curwin->w_valid &= ~VALID_VIRTCOL; - } else if (*get_cursor_pos_ptr() != TAB) { + wp->w_valid &= ~VALID_VIRTCOL; + } else if (*(ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col) != TAB) { // Virtcol is valid when not on a TAB - curwin->w_valid |= VALID_VIRTCOL; - curwin->w_virtcol = wcol; + wp->w_valid |= VALID_VIRTCOL; + wp->w_virtcol = wcol; } return rc; } -/// @param addspaces change the text to achieve our goal? +/// @param addspaces change the text to achieve our goal? only for wp=curwin! /// @param finetune change char offset for the exact column /// @param wcol_arg column to move to (can be negative) -static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) +static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) { + assert(wp == curwin || !addspaces); colnr_T wcol = wcol_arg; int idx; colnr_T col = 0; @@ -104,30 +105,31 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a || (State & MODE_TERMINAL) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') - || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL); + || ((get_ve_flags(wp) & VE_ONEMORE) && wcol < MAXCOL); - char *line = ml_get_buf(curbuf, pos->lnum); + char *line = ml_get_buf(wp->w_buffer, pos->lnum); + int linelen = ml_get_buf_len(wp->w_buffer, pos->lnum); if (wcol >= MAXCOL) { - idx = (int)strlen(line) - 1 + one_more; + idx = linelen - 1 + one_more; col = wcol; if ((addspaces || finetune) && !VIsual_active) { - curwin->w_curswant = linetabsize(curwin, pos->lnum) + one_more; - if (curwin->w_curswant > 0) { - curwin->w_curswant--; + wp->w_curswant = linetabsize(wp, pos->lnum) + one_more; + if (wp->w_curswant > 0) { + wp->w_curswant--; } } } else { - int width = curwin->w_width_inner - win_col_off(curwin); + int width = wp->w_width_inner - win_col_off(wp); int csize = 0; if (finetune - && curwin->w_p_wrap - && curwin->w_width_inner != 0 + && wp->w_p_wrap + && wp->w_width_inner != 0 && wcol >= (colnr_T)width && width > 0) { - csize = linetabsize(curwin, pos->lnum); + csize = linetabsize(wp, pos->lnum); if (csize > 0) { csize--; } @@ -143,7 +145,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } CharsizeArg csarg; - CSType cstype = init_charsize_arg(&csarg, curwin, pos->lnum, line); + CSType cstype = init_charsize_arg(&csarg, wp, pos->lnum, line); StrCharInfo ci = utf_ptr2StrCharInfo(line); col = 0; while (col <= wcol && *ci.ptr != NUL) { @@ -159,14 +161,14 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a // is needed to ensure that a virtual position off the end of // a line has the correct indexing. The one_more comparison // replaces an explicit add of one_more later on. - if (col > wcol || (!virtual_active() && one_more == 0)) { + if (col > wcol || (!virtual_active(wp) && one_more == 0)) { idx -= 1; // Don't count the chars from 'showbreak'. csize -= head; col -= csize; } - if (virtual_active() + if (virtual_active(wp) && addspaces && wcol >= 0 && ((col != wcol && col != wcol + 1) || csize > 1)) { @@ -187,7 +189,6 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a col = wcol; } else { // Break a tab - int linelen = (int)strlen(line); int correct = wcol - col - csize + 1; // negative!! char *newline; @@ -229,14 +230,14 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a if (!one_more) { colnr_T scol, ecol; - getvcol(curwin, pos, &scol, NULL, &ecol); + getvcol(wp, pos, &scol, NULL, &ecol); pos->coladd = ecol - scol; } } else { int b = (int)wcol - (int)col; // The difference between wcol and col is used to set coladd. - if (b > 0 && b < (MAXCOL - 2 * curwin->w_width_inner)) { + if (b > 0 && b < (MAXCOL - 2 * wp->w_width_inner)) { pos->coladd = b; } @@ -245,7 +246,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } // Prevent from moving onto a trail byte. - mark_mb_adjustpos(curbuf, pos); + mark_mb_adjustpos(wp->w_buffer, pos); if (wcol < 0 || col < wcol) { return FAIL; @@ -256,9 +257,9 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a /// Return in "pos" the position of the cursor advanced to screen column "wcol". /// /// @return OK if desired column is reached, FAIL if not -int getvpos(pos_T *pos, colnr_T wcol) +int getvpos(win_T *wp, pos_T *pos, colnr_T wcol) { - return coladvance2(pos, false, virtual_active(), wcol); + return coladvance2(wp, pos, false, virtual_active(wp), wcol); } /// Increment the cursor position. See inc() for return values. @@ -294,7 +295,7 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) // Loop until we reach to_line, skipping folds. for (; from_line < to_line; from_line++, retval++) { // If from_line is in a fold, set it to the last line of that fold. - hasFoldingWin(wp, from_line, NULL, &from_line, true, NULL); + hasFolding(wp, from_line, NULL, &from_line); } // If to_line is in a closed fold, the line count is off by +1. Correct it. @@ -314,8 +315,7 @@ void check_pos(buf_T *buf, pos_T *pos) } if (pos->col > 0) { - char *line = ml_get_buf(buf, pos->lnum); - colnr_T len = (colnr_T)strlen(line); + colnr_T len = ml_get_buf_len(buf, pos->lnum); if (pos->col > len) { pos->col = len; } @@ -329,7 +329,7 @@ void check_cursor_lnum(win_T *win) if (win->w_cursor.lnum > buf->b_ml.ml_line_count) { // If there is a closed fold at the end of the file, put the cursor in // its first line. Otherwise in the last line. - if (!hasFolding(buf->b_ml.ml_line_count, &win->w_cursor.lnum, NULL)) { + if (!hasFolding(win, buf->b_ml.ml_line_count, &win->w_cursor.lnum, NULL)) { win->w_cursor.lnum = buf->b_ml.ml_line_count; } } @@ -338,21 +338,15 @@ void check_cursor_lnum(win_T *win) } } -/// Make sure curwin->w_cursor.col is valid. -void check_cursor_col(void) -{ - check_cursor_col_win(curwin); -} - /// Make sure win->w_cursor.col is valid. Special handling of insert-mode. /// @see mb_check_adjust_col -void check_cursor_col_win(win_T *win) +void check_cursor_col(win_T *win) { colnr_T oldcol = win->w_cursor.col; colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd; - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(win); - colnr_T len = (colnr_T)strlen(ml_get_buf(win->w_buffer, win->w_cursor.lnum)); + colnr_T len = ml_get_buf_len(win->w_buffer, win->w_cursor.lnum); if (len == 0) { win->w_cursor.col = 0; } else if (win->w_cursor.col >= len) { @@ -363,7 +357,7 @@ void check_cursor_col_win(win_T *win) if ((State & MODE_INSERT) || restart_edit || (VIsual_active && *p_sel != 'o') || (cur_ve_flags & VE_ONEMORE) - || virtual_active()) { + || virtual_active(win)) { win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; @@ -403,10 +397,10 @@ void check_cursor_col_win(win_T *win) } /// Make sure curwin->w_cursor in on a valid character -void check_cursor(void) +void check_cursor(win_T *wp) { - check_cursor_lnum(curwin); - check_cursor_col(); + check_cursor_lnum(wp); + check_cursor_col(wp); } /// Check if VIsual position is valid, correct it if not. @@ -418,7 +412,7 @@ void check_visual_pos(void) VIsual.col = 0; VIsual.coladd = 0; } else { - int len = (int)strlen(ml_get(VIsual.lnum)); + int len = ml_get_len(VIsual.lnum); if (VIsual.col > len) { VIsual.col = len; @@ -453,8 +447,8 @@ bool set_leftcol(colnr_T leftcol) changed_cline_bef_curs(curwin); // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. // Perhaps we can change p_siso to int. - int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; - validate_virtcol(); + int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - win_col_off(curwin) - 1; + validate_virtcol(curwin); bool retval = false; // If the cursor is right or left of the screen, move it to last or first @@ -462,10 +456,10 @@ bool set_leftcol(colnr_T leftcol) int siso = get_sidescrolloff_value(curwin); if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { retval = true; - coladvance((colnr_T)(lastcol - siso)); + coladvance(curwin, (colnr_T)(lastcol - siso)); } else if (curwin->w_virtcol < curwin->w_leftcol + siso) { retval = true; - coladvance((colnr_T)(curwin->w_leftcol + siso)); + coladvance(curwin, (colnr_T)(curwin->w_leftcol + siso)); } // If the start of the character under the cursor is not on the screen, @@ -475,10 +469,10 @@ bool set_leftcol(colnr_T leftcol) getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_T)lastcol) { retval = true; - coladvance(s - 1); + coladvance(curwin, s - 1); } else if (s < curwin->w_leftcol) { retval = true; - if (coladvance(e + 1) == FAIL) { // there isn't another character + if (coladvance(curwin, e + 1) == FAIL) { // there isn't another character curwin->w_leftcol = s; // adjust w_leftcol instead changed_cline_bef_curs(curwin); } @@ -514,3 +508,15 @@ char *get_cursor_pos_ptr(void) { return ml_get_buf(curbuf, curwin->w_cursor.lnum) + curwin->w_cursor.col; } + +/// @return length (excluding the NUL) of the cursor line. +colnr_T get_cursor_line_len(void) +{ + return ml_get_buf_len(curbuf, curwin->w_cursor.lnum); +} + +/// @return length (excluding the NUL) of the cursor position. +colnr_T get_cursor_pos_len(void) +{ + return ml_get_buf_len(curbuf, curwin->w_cursor.lnum) - curwin->w_cursor.col; +} diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 51d5d08f78..41ef1aceaf 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -890,9 +890,9 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo } assert(lnum > 0); - bool below_fold = lnum > 1 && hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL); + bool below_fold = lnum > 1 && hasFolding(wp, lnum - 1, NULL, NULL); if (has_fold == kNone) { - has_fold = hasFoldingWin(wp, lnum, NULL, NULL, true, NULL); + has_fold = hasFolding(wp, lnum, NULL, NULL); } const int row = lnum - 1; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2b3010e063..c680600d39 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -756,7 +756,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T e // xdiff requires one big block of memory with all the text. for (linenr_T lnum = start; lnum <= end; lnum++) { - len += strlen(ml_get_buf(buf, lnum)) + 1; + len += (size_t)ml_get_buf_len(buf, lnum) + 1; } char *ptr = try_malloc(len); if (ptr == NULL) { @@ -1347,7 +1347,7 @@ void ex_diffsplit(exarg_T *eap) set_bufref(&old_curbuf, curbuf); // Need to compute w_fraction when no redraw happened yet. - validate_cursor(); + validate_cursor(curwin); set_fraction(curwin); // don't use a new tab page, each tab page has its own diffs @@ -1457,7 +1457,7 @@ void diff_win_options(win_T *wp, bool addbuf) foldUpdateAll(wp); // make sure topline is not halfway through a fold - changed_window_setting_win(wp); + changed_window_setting(wp); if (vim_strchr(p_sbo, 'h') == NULL) { do_cmdline_cmd("set sbo+=hor"); } @@ -1522,7 +1522,7 @@ void ex_diffoff(exarg_T *eap) // make sure topline is not halfway a fold and cursor is // invalidated - changed_window_setting_win(wp); + changed_window_setting(wp); // Note: 'sbo' is not restored, it's a global option. diff_buf_adjust(wp); @@ -2137,7 +2137,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus) } // A closed fold never has filler lines. - if (hasFoldingWin(wp, lnum, NULL, NULL, true, NULL)) { + if (hasFolding(wp, lnum, NULL, NULL)) { return 0; } @@ -2451,8 +2451,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) changed_line_abv_curs_win(towin); check_topfill(towin, false); - hasFoldingWin(towin, towin->w_topline, &towin->w_topline, - NULL, true, NULL); + hasFolding(towin, towin->w_topline, &towin->w_topline, NULL); } /// This is called when 'diffopt' is changed. @@ -2988,7 +2987,7 @@ theend: // Check that the cursor is on a valid character and update its // position. When there were filler lines the topline has become // invalid. - check_cursor(); + check_cursor(curwin); changed_line_abv_curs(); if (diff_need_update) { diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 137039b5e2..90bf6eda51 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -73,7 +73,7 @@ typedef struct { int col; ///< visual column on screen, after wrapping int boguscols; ///< nonexistent columns added to "col" to force wrapping int old_boguscols; ///< bogus boguscols - int vcol_off; ///< offset for concealed characters + int vcol_off_co; ///< offset for concealed characters int off; ///< offset relative start of line @@ -288,26 +288,23 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int if (item->draw_col < 0) { continue; } - int col = 0; if (item->kind == kDecorKindUIWatched) { // send mark position to UI - col = item->draw_col; - WinExtmark m = { (NS)item->data.ui.ns_id, item->data.ui.mark_id, win_row, col }; + WinExtmark m = { (NS)item->data.ui.ns_id, item->data.ui.mark_id, win_row, item->draw_col }; kv_push(win_extmark_arr, m); } if (vt) { int vcol = item->draw_col - col_off; - col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, - vt->hl_mode, max_col, vcol); + int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, + vt->hl_mode, max_col, vcol); if (vt->pos == kVPosEndOfLine && do_eol) { state->eol_col = col + 1; } + *end_col = MAX(*end_col, col); } if (!vt || !(vt->flags & kVTRepeatLinebreak)) { item->draw_col = INT_MIN; // deactivate } - - *end_col = MAX(*end_col, col); } } @@ -581,7 +578,7 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int } /// Build and draw the 'statuscolumn' string for line "lnum" in window "wp". -static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int virtnum, +static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int virtnum, int col_rows, statuscol_T *stcp) { // When called for the first non-filler row of line "lnum" set num v:vars @@ -597,9 +594,14 @@ static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int vir int width = build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, buf, stcp); if (width > stcp->width) { int addwidth = MIN(width - stcp->width, MAX_STCWIDTH - stcp->width); - stcp->width += addwidth; wp->w_nrwidth += addwidth; wp->w_nrwidth_width = wp->w_nrwidth; + if (col_rows > 0) { + // If only column is being redrawn, we now need to redraw the text as well + wp->w_redr_statuscol = true; + return; + } + stcp->width += addwidth; wp->w_valid &= ~VALID_WCOL; } } @@ -848,43 +850,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t } } -static colnr_T get_trailcol(win_T *wp, const char *ptr, const char *line) -{ - colnr_T trailcol = MAXCOL; - // find start of trailing whitespace - if (wp->w_p_lcs_chars.trail) { - trailcol = (colnr_T)strlen(ptr); - while (trailcol > 0 && ascii_iswhite(ptr[trailcol - 1])) { - trailcol--; - } - trailcol += (colnr_T)(ptr - line); - } - - return trailcol; -} - -static colnr_T get_leadcol(win_T *wp, const char *ptr, const char *line) -{ - colnr_T leadcol = 0; - - // find end of leading whitespace - if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) { - leadcol = 0; - while (ascii_iswhite(ptr[leadcol])) { - leadcol++; - } - if (ptr[leadcol] == NUL) { - // in a line full of spaces all of them are treated as trailing - leadcol = 0; - } else { - // keep track of the first column not filled with spaces - leadcol += (colnr_T)(ptr - line + 1); - } - } - - return leadcol; -} - /// Start a screen line at column zero. static void win_line_start(win_T *wp, winlinevars_T *wlv) { @@ -893,16 +858,16 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv) wlv->need_lbr = false; for (int i = 0; i < wp->w_grid.cols; i++) { linebuf_char[i] = schar_from_ascii(' '); - linebuf_attr[i] = -1; + linebuf_attr[i] = 0; linebuf_vcol[i] = -1; } } static void fix_for_boguscols(winlinevars_T *wlv) { - wlv->n_extra += wlv->vcol_off; - wlv->vcol -= wlv->vcol_off; - wlv->vcol_off = 0; + wlv->n_extra += wlv->vcol_off_co; + wlv->vcol -= wlv->vcol_off_co; + wlv->vcol_off_co = 0; wlv->col -= wlv->boguscols; wlv->old_boguscols = wlv->boguscols; wlv->boguscols = 0; @@ -1022,7 +987,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s int conceal_attr = win_hl_attr(wp, HLF_CONCEAL); bool is_concealing = false; bool did_wcol = false; -#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off) +#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off_co) assert(startrow < endrow); @@ -1296,17 +1261,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s nextlinecol = MAXCOL; nextline_idx = 0; } else { - const size_t line_len = strlen(line); + const colnr_T line_len = ml_get_buf_len(wp->w_buffer, lnum); if (line_len < SPWORDLEN) { // Short line, use it completely and append the start of the // next line. nextlinecol = 0; - memmove(nextline, line, line_len); + memmove(nextline, line, (size_t)line_len); STRMOVE(nextline + line_len, nextline + SPWORDLEN); - nextline_idx = (int)line_len + 1; + nextline_idx = line_len + 1; } else { // Long line, use only the last SPWORDLEN bytes. - nextlinecol = (int)line_len - SPWORDLEN; + nextlinecol = line_len - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); nextline_idx = SPWORDLEN + 1; } @@ -1334,8 +1299,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s || wp->w_p_lcs_chars.nbsp) { extra_check = true; } - trailcol = get_trailcol(wp, ptr, line); - leadcol = get_leadcol(wp, ptr, line); + // find start of trailing whitespace + if (wp->w_p_lcs_chars.trail) { + trailcol = ml_get_buf_len(wp->w_buffer, lnum); + while (trailcol > 0 && ascii_iswhite(ptr[trailcol - 1])) { + trailcol--; + } + trailcol += (colnr_T)(ptr - line); + } + // find end of leading whitespace + if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) { + leadcol = 0; + while (ascii_iswhite(ptr[leadcol])) { + leadcol++; + } + if (ptr[leadcol] == NUL) { + // in a line full of spaces all of them are treated as trailing + leadcol = 0; + } else { + // keep track of the first column not filled with spaces + leadcol += (colnr_T)(ptr - line + 1); + } + } } // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the @@ -1391,7 +1376,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // the end of the line may be before the start of the displayed part. if (wlv.vcol < start_col && (wp->w_p_cuc || wlv.color_cols - || virtual_active() + || virtual_active(wp) || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { wlv.vcol = start_col; } @@ -1539,7 +1524,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s statuscol.num_attr = get_line_number_attr(wp, &wlv); } const int v = (int)(ptr - line); - draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); + draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, col_rows, &statuscol); if (wp->w_redr_statuscol) { break; } @@ -1564,7 +1549,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // When only updating the columns and that's done, stop here. if (col_rows > 0) { - win_put_linebuf(wp, wlv.row, 0, wlv.off, wlv.off, bg_attr, false); + win_put_linebuf(wp, wlv.row, wlv.off, wlv.off, bg_attr, false); // Need to update more screen lines if: // - 'statuscolumn' needs to be drawn, or // - LineNrAbove or LineNrBelow is used, or @@ -1620,7 +1605,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && lnum == wp->w_cursor.lnum && wlv.vcol >= wp->w_virtcol) { draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); // don't clear anything after wlv.col - win_put_linebuf(wp, wlv.row, 0, wlv.col, wlv.col, bg_attr, false); + win_put_linebuf(wp, wlv.row, wlv.col, wlv.col, bg_attr, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { @@ -2231,9 +2216,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } else { int saved_nextra = wlv.n_extra; - if (wlv.vcol_off > 0) { + if (wlv.vcol_off_co > 0) { // there are characters to conceal - tab_len += wlv.vcol_off; + tab_len += wlv.vcol_off_co; } // boguscols before fix_for_boguscols() from above. if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0 @@ -2275,27 +2260,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } // n_extra will be increased by fix_for_boguscols() - // macro below, so need to adjust for that here - if (wlv.vcol_off > 0) { - wlv.n_extra -= wlv.vcol_off; + // below, so need to adjust for that here + if (wlv.vcol_off_co > 0) { + wlv.n_extra -= wlv.vcol_off_co; } } } { - int vc_saved = wlv.vcol_off; + int vc_saved = wlv.vcol_off_co; // Tab alignment should be identical regardless of // 'conceallevel' value. So tab compensates of all // previous concealed characters, and thus resets - // vcol_off and boguscols accumulated so far in the + // vcol_off_co and boguscols accumulated so far in the // line. Note that the tab can be longer than // 'tabstop' when there are concealed characters. fix_for_boguscols(&wlv); // Make sure, the highlighting for the tab char will be // correctly set further below (effectively reverts the - // FIX_FOR_BOGSUCOLS macro). + // fix_for_boguscols() call). if (wlv.n_extra == tab_len + vc_saved && wp->w_p_list && wp->w_p_lcs_chars.tab1) { tab_len += vc_saved; @@ -2337,7 +2322,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && wlv.line_attr == 0 && wlv.line_attr_lowprio == 0) { // In virtualedit, visual selections may extend beyond end of line - if (!(area_highlighting && virtual_active() + if (!(area_highlighting && virtual_active(wp) && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { wlv.p_extra = ""; } @@ -2380,7 +2365,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s mb_schar = schar_from_ascii(mb_c); } else if (VIsual_active && (VIsual_mode == Ctrl_V || VIsual_mode == 'v') - && virtual_active() + && virtual_active(wp) && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol && wlv.col < grid->cols) { @@ -2418,12 +2403,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } else { mb_schar = schar_from_ascii(' '); } + + if (utf_char2cells(mb_c) > 1) { + // When the first char to be concealed is double-width, + // need to advance one more virtual column. + wlv.n_extra++; + } + mb_c = schar_get_first_codepoint(mb_schar); prev_syntax_id = syntax_seqnr; if (wlv.n_extra > 0) { - wlv.vcol_off += wlv.n_extra; + wlv.vcol_off_co += wlv.n_extra; } wlv.vcol += wlv.n_extra; if (is_wrapped && wlv.n_extra > 0) { @@ -2449,11 +2441,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // In the cursor line and we may be concealing characters: correct // the cursor column when we reach its position. + // With 'virtualedit' we may never reach cursor position, but we still + // need to correct the cursor column, so do that at end of line. if (!did_wcol && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) - && (int)wp->w_virtcol <= wlv.vcol + wlv.skip_cells) { + && (wlv.vcol + wlv.skip_cells >= wp->w_virtcol || mb_schar == NUL)) { wp->w_wcol = wlv.col - wlv.boguscols; + if (wlv.vcol + wlv.skip_cells < wp->w_virtcol) { + // Cursor beyond end of the line with 'virtualedit'. + wp->w_wcol += wp->w_virtcol - wlv.vcol - wlv.skip_cells; + } wp->w_wrow = wlv.row; did_wcol = true; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; @@ -2564,13 +2562,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s advance_color_col(&wlv, vcol_hlc(wlv)); - bool has_virttext = false; // Make sure alignment is the same regardless // if listchars=eol:X is used or not. const int eol_skip = (lcs_eol_todo && eol_hl_off == 0 ? 1 : 0); if (has_decor) { - has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); + decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); } if (((wp->w_p_cuc @@ -2578,7 +2575,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + start_col && lnum != wp->w_cursor.lnum) || wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr - || wlv.diff_hlf != 0 || has_virttext)) { + || wlv.diff_hlf != 0)) { int rightmost_vcol = get_rightmost_vcol(wp, wlv.color_cols); const int cuc_attr = win_hl_attr(wp, HLF_CUC); const int mc_attr = win_hl_attr(wp, HLF_MC); @@ -2592,7 +2589,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s : 0; const int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); - if (base_attr || wlv.line_attr || has_virttext) { + if (base_attr || wlv.line_attr) { rightmost_vcol = INT_MAX; } @@ -2604,10 +2601,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s int col_attr = base_attr; - if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol) { - col_attr = cuc_attr; + if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol + && lnum != wp->w_cursor.lnum) { + col_attr = hl_combine_attr(col_attr, cuc_attr); } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) { - col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr); + col_attr = hl_combine_attr(col_attr, mc_attr); } col_attr = hl_combine_attr(col_attr, wlv.line_attr); @@ -2619,7 +2617,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s break; } - wlv.vcol += 1; + wlv.vcol++; } } @@ -2640,7 +2638,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0); } draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); - win_put_linebuf(wp, wlv.row, 0, wlv.col, grid->cols, bg_attr, false); + win_put_linebuf(wp, wlv.row, wlv.col, grid->cols, bg_attr, false); wlv.row++; // Update w_cline_height and w_cline_folded if the cursor line was @@ -2748,11 +2746,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.off++; wlv.col++; } else if (wp->w_p_cole > 0 && is_concealing) { + bool concealed_wide = utf_char2cells(mb_c) > 1; + wlv.skip_cells--; - wlv.vcol_off++; + wlv.vcol_off_co++; + if (concealed_wide) { + // When a double-width char is concealed, + // need to advance one more virtual column. + wlv.vcol++; + wlv.vcol_off_co++; + } + if (wlv.n_extra > 0) { - wlv.vcol_off += wlv.n_extra; + wlv.vcol_off_co += wlv.n_extra; } + if (is_wrapped) { // Special voodoo required if 'wrap' is on. // @@ -2773,7 +2781,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.n_attr = 0; } - if (utf_char2cells(mb_c) > 1) { + if (concealed_wide) { // Need to fill two screen columns. wlv.boguscols++; wlv.col++; @@ -2808,7 +2816,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.char_attr = vcol_save_attr; } - // restore attributes after "predeces" in 'listchars' + // restore attributes after "precedes" in 'listchars' if (n_attr3 > 0 && --n_attr3 == 0) { wlv.char_attr = saved_attr3; } @@ -2850,6 +2858,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && !wp->w_p_rl; // Not right-to-left. int draw_col = wlv.col - wlv.boguscols; + + // Apply 'cursorline' highlight. + if (wlv.boguscols != 0 && (wlv.line_attr_lowprio != 0 || wlv.line_attr != 0)) { + int attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.line_attr); + while (draw_col < grid->cols) { + linebuf_char[wlv.off] = schar_from_char(' '); + linebuf_attr[wlv.off] = attr; + linebuf_vcol[wlv.off] = MAXCOL; // TODO(zeertzjq): this is wrong + wlv.off++; + draw_col++; + } + } + if (virt_line_offset >= 0) { draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line, kHlModeReplace, grid->cols, 0); @@ -2857,7 +2878,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row); } - win_put_linebuf(wp, wlv.row, 0, draw_col, grid->cols, bg_attr, wrap); + win_put_linebuf(wp, wlv.row, draw_col, grid->cols, bg_attr, wrap); if (wrap) { ScreenGrid *current_grid = grid; int current_row = wlv.row; @@ -2869,7 +2890,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } wlv.boguscols = 0; - wlv.vcol_off = 0; + wlv.vcol_off_co = 0; wlv.row++; // When not wrapping and finished diff lines, break here. @@ -2917,15 +2938,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s return wlv.row; } -static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clear_width, - int bg_attr, bool wrap) +static void win_put_linebuf(win_T *wp, int row, int endcol, int clear_width, int bg_attr, bool wrap) { ScreenGrid *grid = &wp->w_grid; - int start_col = 0; + int startcol = 0; if (wp->w_p_rl) { - linebuf_mirror(&start_col, &endcol, &clear_width, grid->cols); + linebuf_mirror(&startcol, &endcol, &clear_width, grid->cols); } // Take care of putting "<<<" on the first line for 'smoothscroll'. @@ -2954,6 +2974,7 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea } } + int coloff = 0; grid_adjust(&grid, &row, &coloff); - grid_put_linebuf(grid, row, coloff, start_col, endcol, clear_width, wp->w_p_rl, bg_attr, wrap); + grid_put_linebuf(grid, row, coloff, startcol, endcol, clear_width, wp->w_p_rl, bg_attr, wrap); } diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index ea6be5d6d3..1fb42af786 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -421,7 +421,14 @@ bool redrawing(void) /// and redraw_all_later() to mark parts of the screen as needing a redraw. int update_screen(void) { - static bool did_intro = false; + static bool still_may_intro = true; + if (still_may_intro) { + if (!may_show_intro()) { + must_redraw = UPD_NOT_VALID; + still_may_intro = false; + } + } + bool is_stl_global = global_stl_height() > 0; // Don't do anything if the screen structures are (not yet) valid. @@ -673,10 +680,9 @@ int update_screen(void) } // May put up an introductory message when not editing a file - if (!did_intro) { - maybe_intro_message(); + if (still_may_intro) { + intro_message(false); } - did_intro = true; decor_providers_invoke_end(); @@ -823,7 +829,7 @@ void setcursor(void) void setcursor_mayforce(bool force) { if (force || redrawing()) { - validate_cursor(); + validate_cursor(curwin); ScreenGrid *grid = &curwin->w_grid; int row = curwin->w_wrow; @@ -851,7 +857,7 @@ void show_cursor_info_later(bool force) && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; // Only draw when something changed. - validate_virtcol_win(curwin); + validate_virtcol(curwin); if (force || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum || curwin->w_cursor.col != curwin->w_stl_cursor.col @@ -1178,7 +1184,7 @@ void comp_col(void) sc_col = ru_col; } } - if (p_sc) { + if (p_sc && *p_sloc == 'l') { sc_col += SHOWCMD_COLS; if (!p_ru || last_has_status) { // no need for separating space sc_col++; @@ -1513,7 +1519,7 @@ static void win_update(win_T *wp) // Make sure skipcol is valid, it depends on various options and the window // width. - if (wp->w_skipcol > 0) { + if (wp->w_skipcol > 0 && wp->w_width_inner > win_col_off(wp)) { int w = 0; int width1 = wp->w_width_inner - win_col_off(wp); int width2 = width1 + win_col_off2(wp); @@ -1611,14 +1617,14 @@ static void win_update(win_T *wp) } } - hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); + hasFolding(wp, mod_top, &mod_top, NULL); if (mod_top > lnumt) { mod_top = lnumt; } // Now do the same for the bottom line (one above mod_bot). mod_bot--; - hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL); + hasFolding(wp, mod_bot, NULL, &mod_bot); mod_bot++; if (mod_bot < lnumb) { mod_bot = lnumb; @@ -1691,7 +1697,7 @@ static void win_update(win_T *wp) if (j >= wp->w_grid.rows - 2) { break; } - hasFoldingWin(wp, ln, NULL, &ln, true, NULL); + hasFolding(wp, ln, NULL, &ln); } } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; @@ -1903,7 +1909,7 @@ static void win_update(win_T *wp) // Highlight to the end of the line, unless 'virtualedit' has // "block". if (curwin->w_curswant == MAXCOL) { - if (get_ve_flags() & VE_BLOCK) { + if (get_ve_flags(curwin) & VE_BLOCK) { pos_T pos; int cursor_above = curwin->w_cursor.lnum < VIsual.lnum; @@ -2148,7 +2154,7 @@ static void win_update(win_T *wp) // rows, and may insert/delete lines int j = idx; for (l = lnum; l < mod_bot; l++) { - if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) { + if (hasFolding(wp, l, NULL, &l)) { new_rows++; } else if (l == wp->w_topline) { int n = plines_win_nofill(wp, l, false) + wp->w_topfill; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b7b32883c2..df0c075306 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -185,7 +185,7 @@ static void insert_enter(InsertState *s) curwin->w_cursor = save_cursor; State = MODE_INSERT; - check_cursor_col(); + check_cursor_col(curwin); State = save_state; } } @@ -282,7 +282,7 @@ static void insert_enter(InsertState *s) // correct in very rare cases). // Also do this if curswant is greater than the current virtual // column. Eg after "^O$" or "^O80|". - validate_virtcol(); + validate_virtcol(curwin); update_curswant(); if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum) || curwin->w_curswant > curwin->w_virtcol) @@ -468,7 +468,7 @@ static int insert_check(VimState *state) && curwin->w_topline == s->old_topline && curwin->w_topfill == s->old_topfill) { s->mincol = curwin->w_wcol; - validate_cursor_col(); + validate_cursor_col(curwin); if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, @@ -478,7 +478,7 @@ static int insert_check(VimState *state) || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; - } else if (hasFolding(curwin->w_topline, NULL, &s->old_topline)) { + } else if (hasFolding(curwin, curwin->w_topline, NULL, &s->old_topline)) { set_topline(curwin, s->old_topline + 1); } else { set_topline(curwin, curwin->w_topline + 1); @@ -491,7 +491,7 @@ static int insert_check(VimState *state) s->did_backspace = false; - validate_cursor(); // may set must_redraw + validate_cursor(curwin); // may set must_redraw // Redraw the display when no characters are waiting. // Also shows mode, ruler and positions cursor. @@ -743,7 +743,7 @@ static int insert_handle_key(InsertState *s) ins_ctrl_o(); // don't move the cursor left when 'virtualedit' has "onemore". - if (get_ve_flags() & VE_ONEMORE) { + if (get_ve_flags(curwin) & VE_ONEMORE) { ins_at_eol = false; s->nomove = true; } @@ -1451,7 +1451,7 @@ void edit_putchar(int c, bool highlight) int attr; update_topline(curwin); // just in case w_topline isn't valid - validate_cursor(); + validate_cursor(curwin); if (highlight) { attr = HL_ATTR(HLF_8); } else { @@ -1521,7 +1521,7 @@ static void init_prompt(int cmdchar_todo) ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false); } curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); inserted_bytes(curbuf->b_ml.ml_line_count, 0, 0, (colnr_T)strlen(prompt)); } @@ -1536,13 +1536,13 @@ static void init_prompt(int cmdchar_todo) } if (cmdchar_todo == 'A') { - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } if (curwin->w_cursor.col < (colnr_T)strlen(prompt)) { curwin->w_cursor.col = (colnr_T)strlen(prompt); } // Make sure the cursor is in a valid position. - check_cursor(); + check_cursor(curwin); } /// @return true if the cursor is in the editable position of the prompt line. @@ -2394,7 +2394,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) pos_T tpos = curwin->w_cursor; curwin->w_cursor = *end_insert_pos; - check_cursor_col(); // make sure it is not past the line + check_cursor_col(curwin); // make sure it is not past the line while (true) { if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) { curwin->w_cursor.col--; @@ -2471,7 +2471,7 @@ void free_last_insert(void) void beginline(int flags) { if ((flags & BL_SOL) && !p_sol) { - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } else { curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -2497,13 +2497,13 @@ int oneright(void) { char *ptr; - if (virtual_active()) { + if (virtual_active(curwin)) { pos_T prevpos = curwin->w_cursor; // Adjust for multi-wide char (excluding TAB) ptr = get_cursor_pos_ptr(); - coladvance(getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) - ? ptr2cells(ptr) : 1)); + coladvance(curwin, getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) + ? ptr2cells(ptr) : 1)); curwin->w_set_curswant = true; // Return OK if the cursor moved, FAIL otherwise (at window edge). return (prevpos.col != curwin->w_cursor.col @@ -2519,7 +2519,7 @@ int oneright(void) // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit' // contains "onemore". - if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0) { + if (ptr[l] == NUL && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { return FAIL; } curwin->w_cursor.col += l; @@ -2531,7 +2531,7 @@ int oneright(void) int oneleft(void) { - if (virtual_active()) { + if (virtual_active(curwin)) { int v = getviscol(); if (v == 0) { @@ -2541,7 +2541,7 @@ int oneleft(void) // We might get stuck on 'showbreak', skip over it. int width = 1; while (true) { - coladvance(v - width); + coladvance(curwin, v - width); // getviscol() is slow, skip it when 'showbreak' is empty, // 'breakindent' is not set and there are no multi-byte // characters @@ -2590,7 +2590,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Count each sequence of folded lines as one logical line. // go to the start of the current fold - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); while (n--) { // move up one line @@ -2602,7 +2602,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Insert mode or when 'foldopen' contains "all": it will open // in a moment. if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) { - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); } } if (lnum < 1) { @@ -2625,7 +2625,7 @@ int cursor_up(linenr_T n, bool upd_topline) cursor_up_inner(curwin, n); // try to advance to the column we want to be at - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (upd_topline) { update_topline(curwin); // make sure curwin->w_topline is valid @@ -2678,7 +2678,7 @@ int cursor_down(int n, bool upd_topline) cursor_down_inner(curwin, n); // try to advance to the column we want to be at - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (upd_topline) { update_topline(curwin); // make sure curwin->w_topline is valid @@ -2968,7 +2968,7 @@ static void replace_do_bs(int limit_col) } del_char_after_col(limit_col); if (l_State & VREPLACE_FLAG) { - orig_len = (int)strlen(get_cursor_pos_ptr()); + orig_len = get_cursor_pos_len(); } replace_push(cc); replace_pop_ins(); @@ -2976,7 +2976,7 @@ static void replace_do_bs(int limit_col) if (l_State & VREPLACE_FLAG) { // Get the number of screen cells used by the inserted characters char *p = get_cursor_pos_ptr(); - int ins_len = (int)strlen(p) - orig_len; + int ins_len = get_cursor_pos_len() - orig_len; int vcol = start_vcol; for (int i = 0; i < ins_len; i++) { vcol += win_chartabsize(curwin, p + i, vcol); @@ -3274,7 +3274,7 @@ static void ins_reg(void) // Cursor may be moved back a column. curwin->w_cursor = curpos; - check_cursor(); + check_cursor(curwin); } if (regname == NUL || !valid_yank_reg(regname, false)) { vim_beep(BO_REG); @@ -3466,7 +3466,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove) && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0) && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active)) && !revins_on) { - if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) { + if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == VE_ALL) { oneleft(); if (restart_edit != NUL) { curwin->w_cursor.coladd++; @@ -3598,7 +3598,7 @@ static void ins_ctrl_o(void) } else { restart_edit = 'I'; } - if (virtual_active()) { + if (virtual_active(curwin)) { ins_at_eol = false; // cursor always keeps its column } else { ins_at_eol = (gchar_cursor() == NUL); @@ -3760,7 +3760,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) return false; } Insstart.lnum--; - Insstart.col = (colnr_T)strlen(ml_get(Insstart.lnum)); + Insstart.col = ml_get_len(Insstart.lnum); } // In replace mode: // cc < 0: NL was inserted, delete it @@ -3785,9 +3785,10 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (has_format_option(FO_AUTO) && has_format_option(FO_WHITE_PAR)) { char *ptr = ml_get_buf_mut(curbuf, curwin->w_cursor.lnum); - int len = (int)strlen(ptr); + int len = get_cursor_line_len(); if (len > 0 && ptr[len - 1] == ' ') { ptr[len - 1] = NUL; + curbuf->b_ml.ml_line_len--; } } @@ -4027,7 +4028,7 @@ static void ins_left(void) // always break undo when moving upwards/downwards, else undo may break start_arrow(&tpos); curwin->w_cursor.lnum--; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_set_curswant = true; // so we stay at the end } else { vim_beep(BO_CRSR); @@ -4061,7 +4062,7 @@ static void ins_end(int c) if (c == K_C_END) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; } - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_curswant = MAXCOL; start_arrow(&tpos); @@ -4095,13 +4096,13 @@ static void ins_right(void) foldOpenCursor(); } undisplay_dollar(); - if (gchar_cursor() != NUL || virtual_active()) { + if (gchar_cursor() != NUL || virtual_active(curwin)) { start_arrow_with_change(&curwin->w_cursor, end_change); if (!end_change) { AppendCharToRedobuff(K_RIGHT); } curwin->w_set_curswant = true; - if (virtual_active()) { + if (virtual_active(curwin)) { oneright(); } else { curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); @@ -4156,7 +4157,7 @@ static void ins_up(bool startcol) pos_T tpos = curwin->w_cursor; if (cursor_up(1, true) == OK) { if (startcol) { - coladvance(getvcol_nolist(&Insstart)); + coladvance(curwin, getvcol_nolist(&Insstart)); } if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill) { @@ -4201,7 +4202,7 @@ static void ins_down(bool startcol) pos_T tpos = curwin->w_cursor; if (cursor_down(1, true) == OK) { if (startcol) { - coladvance(getvcol_nolist(&Insstart)); + coladvance(curwin, getvcol_nolist(&Insstart)); } if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill) { @@ -4330,7 +4331,7 @@ static bool ins_tab(void) if (State & VREPLACE_FLAG) { pos = curwin->w_cursor; cursor = &pos; - saved_line = xstrdup(get_cursor_line_ptr()); + saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); ptr = saved_line + pos.col; } else { ptr = get_cursor_pos_ptr(); @@ -4411,13 +4412,13 @@ static bool ins_tab(void) if (i > 0) { STRMOVE(ptr, ptr + i); // correct replace stack. - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG)) { + 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); } @@ -4462,8 +4463,7 @@ bool ins_eol(int c) // Strange Vi behaviour: In Replace mode, typing a NL will not delete the // character under the cursor. Only push a NUL on the replace stack, // nothing to put back when the NL is deleted. - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG)) { + if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { replace_push(NUL); } @@ -4474,13 +4474,13 @@ bool ins_eol(int c) // Put cursor on NUL if on the last char and coladd is 1 (happens after // CTRL-O). - if (virtual_active() && curwin->w_cursor.coladd > 0) { - coladvance(getviscol()); + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { + coladvance(curwin, getviscol()); } // NL in reverse insert will always start in the end of current line. if (revins_on) { - curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr()); + curwin->w_cursor.col += get_cursor_pos_len(); } AppendToRedobuff(NL_STR); @@ -4574,7 +4574,7 @@ int ins_copychar(linenr_T lnum) } // try to advance to the cursor column - validate_virtcol(); + validate_virtcol(curwin); int const end_vcol = curwin->w_virtcol; char *line = ml_get(lnum); @@ -4720,7 +4720,7 @@ colnr_T get_nolist_virtcol(void) if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { return getvcol_nolist(&curwin->w_cursor); } - validate_virtcol(); + validate_virtcol(curwin); return curwin->w_virtcol; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3d224bfa0f..e4ee254193 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6699,7 +6699,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { len = mb_charlen(ml_get(pos.lnum)); } else { - len = (int)strlen(ml_get(pos.lnum)); + len = ml_get_len(pos.lnum); } // We accept "$" for the column number: last column. @@ -6789,7 +6789,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); } else { - pos.col = (colnr_T)strlen(get_cursor_line_ptr()); + pos.col = get_cursor_line_len(); } } return &pos; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 8dc282cd43..7df4ab71ef 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -3973,7 +3973,7 @@ M.funcs = { |getbufoneline()| ]=], name = 'getline', - params = { { 'lnum', 'integer' }, { 'end', 'nil|false' } }, + params = { { 'lnum', 'integer|string' }, { 'end', 'nil|false' } }, signature = 'getline({lnum} [, {end}])', returns = 'string', }, @@ -4359,43 +4359,49 @@ M.funcs = { args = { 2, 3 }, base = 1, desc = [=[ - Returns the list of strings from {pos1} to {pos2} in current + Returns the list of strings from {pos1} to {pos2} from a buffer. {pos1} and {pos2} must both be |List|s with four numbers. - See |getpos()| for the format of the list. + See |getpos()| for the format of the list. It's possible + to specify positions from a different buffer, but please + note the limitations at |getregion-notes|. The optional argument {opts} is a Dict and supports the following items: - type Specify the selection type + type Specify the region's selection type (default: "v"): "v" for |charwise| mode "V" for |linewise| mode "<CTRL-V>" for |blockwise-visual| mode exclusive If |TRUE|, use exclusive selection - for the end position 'selection'. + for the end position + (default: follow 'selection') You can get the last selection type by |visualmode()|. If Visual mode is active, use |mode()| to get the Visual mode (e.g., in a |:vmap|). - This function uses the line and column number from the - specified position. - It is useful to get text starting and ending in different - columns, such as |charwise-visual| selection. + This function is useful to get text starting and ending in + different columns, such as a |charwise-visual| selection. + *getregion-notes* Note that: - Order of {pos1} and {pos2} doesn't matter, it will always return content from the upper left position to the lower right position. - - If 'virtualedit' is enabled and selection is past the end of - line, resulting lines are filled with blanks. - - If the selection starts or ends in the middle of a multibyte - character, it is not included but its selected part is - substituted with spaces. - - If {pos1} or {pos2} is not current in the buffer, an empty + - If 'virtualedit' is enabled and the region is past the end + of the lines, resulting lines are padded with spaces. + - If the region is blockwise and it starts or ends in the + middle of a multi-cell character, it is not included but + its selected part is substituted with spaces. + - If {pos1} and {pos2} are not in the same buffer, an empty list is returned. + - {pos1} and {pos2} must belong to a |bufloaded()| buffer. + - It is evaluated in current window context, which makes a + difference if the buffer is displayed in a window with + different 'virtualedit' or 'list' values. Examples: > :xnoremap <CR> @@ -5220,7 +5226,7 @@ M.funcs = { ]=], name = 'indent', - params = { { 'lnum', 'integer' } }, + params = { { 'lnum', 'integer|string' } }, returns = 'integer', signature = 'indent({lnum})', }, @@ -7830,6 +7836,9 @@ M.funcs = { echo printf("%1$*2$.*3$f", 1.4142135, 6, 2) < 1.41 + You will get an overflow error |E1510|, when the field-width + or precision will result in a string longer than 6400 chars. + *E1500* You cannot mix positional and non-positional arguments: >vim echo printf("%s%1$s", "One", "Two") @@ -8740,6 +8749,7 @@ M.funcs = { When a match has been found its line number is returned. If there is no match a 0 is returned and the cursor doesn't move. No error message is given. + To get the matched string, use |matchbufline()|. {flags} is a String, which can contain these character flags: 'b' search Backward instead of forward @@ -12681,9 +12691,7 @@ M.funcs = { [1, 1], unless there is a tabline, then it is [2, 1]. {nr} can be the window number or the |window-ID|. Use zero for the current window. - Returns [0, 0] if the window cannot be found in the current - tabpage. - + Returns [0, 0] if the window cannot be found. ]=], name = 'win_screenpos', params = { { 'nr', 'integer' } }, @@ -12693,10 +12701,10 @@ M.funcs = { args = { 2, 3 }, base = 1, desc = [=[ - Move the window {nr} to a new split of the window {target}. - This is similar to moving to {target}, creating a new window - using |:split| but having the same contents as window {nr}, and - then closing {nr}. + Temporarily switch to window {target}, then move window {nr} + to a new split adjacent to {target}. + Unlike commands such as |:split|, no new windows are created + (the |window-ID| of window {nr} is unchanged after the move). Both {nr} and {target} can be window numbers or |window-ID|s. Both must be in the current tab page. @@ -12850,7 +12858,9 @@ M.funcs = { # the number of the last accessed window (where |CTRL-W_p| goes to). If there is no previous window or it is in another tab page 0 is - returned. + returned. May refer to the current window in + some cases (e.g. when evaluating 'statusline' + expressions). {N}j the number of the Nth window below the current window (where |CTRL-W_j| goes to). {N}k the number of the Nth window above the current diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c index 7b8f71ef3f..73bfd6db2a 100644 --- a/src/nvim/eval/buffer.c +++ b/src/nvim/eval/buffer.c @@ -197,7 +197,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_ && ml_replace(lnum, line, true) == OK) { inserted_bytes(lnum, 0, old_len, (int)strlen(line)); if (is_curbuf && lnum == curwin->w_cursor.lnum) { - check_cursor_col(); + check_cursor_col(curwin); } rettv->vval.v_number = 0; // OK } @@ -229,7 +229,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_ wp->w_cursor.lnum += (linenr_T)added; } } - check_cursor_col(); + check_cursor_col(curwin); update_topline(curwin); } @@ -469,7 +469,7 @@ void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } } - check_cursor_col(); + check_cursor_col(curwin); deleted_lines_mark(first, count); rettv->vval.v_number = 0; // OK diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3b387e3fd6..99da15ddd7 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -727,7 +727,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) return; } - check_cursor(); + check_cursor(curwin); winchanged = true; } @@ -738,7 +738,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) if (fp->col == MAXCOL) { // '> can be MAXCOL, get the length of the line then if (fp->lnum <= curbuf->b_ml.ml_line_count) { - col = (colnr_T)strlen(ml_get(fp->lnum)) + 1; + col = ml_get_len(fp->lnum) + 1; } else { col = MAXCOL; } @@ -746,7 +746,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) col = fp->col + 1; // col(".") when the cursor is on the NUL at the end of the line // because of "coladd" can be seen as an extra column. - if (virtual_active() && fp == &curwin->w_cursor) { + if (virtual_active(curwin) && fp == &curwin->w_cursor) { char *p = get_cursor_pos_ptr(); if (curwin->w_cursor.coladd >= (colnr_T)win_chartabsize(curwin, p, @@ -1191,7 +1191,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) curwin->w_cursor.coladd = coladd; // Make sure the cursor is in a valid position. - check_cursor(); + check_cursor(curwin); // Correct cursor for multi-byte character. mb_adjust_cursor(); @@ -2827,16 +2827,12 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - int fnum = -1; - pos_T p1; - if (list2fpos(&argvars[0], &p1, &fnum, NULL, false) != OK - || (fnum >= 0 && fnum != curbuf->b_fnum)) { - return; - } - - pos_T p2; - if (list2fpos(&argvars[1], &p2, &fnum, NULL, false) != OK - || (fnum >= 0 && fnum != curbuf->b_fnum)) { + int fnum1 = -1; + int fnum2 = -1; + pos_T p1, p2; + if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK + || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK + || fnum1 != fnum2) { return; } @@ -2863,11 +2859,38 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } else if (type[0] == Ctrl_V && type[1] == NUL) { region_type = kMTBlockWise; } else { + semsg(_(e_invargNval), "type", type); + return; + } + + buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf; + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); + return; + } + + if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p1.lnum); + return; + } + if (p1.col < 1 || p1.col > ml_get_buf_len(findbuf, p1.lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p1.col); + return; + } + if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p2.lnum); + return; + } + if (p2.col < 1 || p2.col > ml_get_buf_len(findbuf, p2.lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p2.col); return; } + buf_T *const save_curbuf = curbuf; + curbuf = findbuf; + curwin->w_buffer = curbuf; const TriState save_virtual = virtual_op; - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); // NOTE: Adjust is needed. p1.col--; @@ -2893,7 +2916,7 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) mark_mb_adjustpos(curbuf, &p2); } else if (p2.lnum > 1) { p2.lnum--; - p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + p2.col = ml_get_len(p2.lnum); if (p2.col > 0) { p2.col--; mark_mb_adjustpos(curbuf, &p2); @@ -2948,6 +2971,8 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_allocated_string(rettv->vval.v_list, akt); } + curbuf = save_curbuf; + curwin->w_buffer = curbuf; virtual_op = save_virtual; } @@ -3315,7 +3340,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "path_extra", "persistent_undo", "profile", - "pythonx", "reltime", "quickfix", "rightleft", @@ -3406,6 +3430,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = syntax_present(curwin); } else if (STRICMP(name, "clipboard_working") == 0) { n = eval_has_provider("clipboard"); + } else if (STRICMP(name, "pythonx") == 0) { + n = eval_has_provider("python3"); } else if (STRICMP(name, "wsl") == 0) { n = has_wsl(); #ifdef UNIX @@ -4617,7 +4643,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (wp != NULL && tp != NULL) { switchwin_T switchwin; if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { - check_cursor(); + check_cursor(curwin); fp = var2fpos(&argvars[0], true, &fnum, false); } restore_win_noblock(&switchwin, true); @@ -6005,6 +6031,12 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } } + if (blob) { + tv_blob_alloc_ret(rettv); + } else { + tv_list_alloc_ret(rettv, kListLenUnknown); + } + // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -6019,7 +6051,6 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } if (blob) { - tv_blob_alloc_ret(rettv); if (read_blob(fd, rettv, offset, size) == FAIL) { semsg(_(e_notread), fname); } @@ -6027,7 +6058,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl return; } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *const l = rettv->vval.v_list; while (maxline < 0 || tv_list_len(l) < maxline) { int readlen = (int)fread(buf, 1, (size_t)io_size, fd); @@ -6998,7 +7029,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } // "/$" will put the cursor after the end of the line, may need to // correct that here - check_cursor(); + check_cursor(curwin); } // If 'n' flag is used: restore cursor position. @@ -7760,7 +7791,7 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) curwin->w_curswant = curswant - 1; curwin->w_set_curswant = false; } - check_cursor(); + check_cursor(curwin); rettv->vval.v_number = 0; } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark @@ -8657,7 +8688,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && (size_t)col < strlen(ml_get(lnum))) { + && col >= 0 && col < ml_get_len(lnum)) { id = syn_get_id(curwin, lnum, col, trans, NULL, false); } @@ -8780,8 +8811,8 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr CLEAR_FIELD(str); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= ml_get_len(lnum) && curwin->w_p_cole > 0) { syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); @@ -8814,10 +8845,8 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const linenr_T lnum = tv_get_lnum(argvars); const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; - if (lnum >= 1 - && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 - && (size_t)col <= strlen(ml_get(lnum))) { + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= ml_get_len(lnum)) { tv_list_alloc_ret(rettv, kListLenMayKnow); syn_get_id(curwin, lnum, col, false, NULL, true); @@ -9175,7 +9204,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto theend; } - check_cursor(); + check_cursor(curwin); winchanged = true; } @@ -9187,9 +9216,9 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (fp->col < 0) { fp->col = 0; } else { - const size_t len = strlen(ml_get(fp->lnum)); - if (fp->col > (colnr_T)len) { - fp->col = (colnr_T)len; + const colnr_T len = ml_get_len(fp->lnum); + if (fp->col > len) { + fp->col = len; } } getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end); diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index b8aa0c9641..68de40f983 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -14,6 +14,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" +#include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" #include "nvim/gettext_defs.h" @@ -515,7 +516,7 @@ bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp) } if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) { - check_cursor(); + check_cursor(curwin); return true; } return false; @@ -539,7 +540,7 @@ void win_execute_after(win_execute_T *args) // In case the command moved the cursor or changed the Visual area, // check it is valid. - check_cursor(); + check_cursor(curwin); if (VIsual_active) { check_pos(curbuf, &VIsual); } @@ -583,9 +584,13 @@ void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int id = (int)tv_get_number(&argvars[0]); + if (curwin->handle == id) { + // Nothing to do. + rettv->vval.v_number = 1; + return; + } - if (cmdwin_type != 0) { - emsg(_(e_cmdwin)); + if (text_or_buf_locked()) { return; } FOR_ALL_TAB_WINDOWS(tp, wp) { @@ -659,55 +664,19 @@ void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); } -/// Move the window wp into a new split of targetwin in a given direction -static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) -{ - int height = wp->w_height; - win_T *oldwin = curwin; - - if (wp == targetwin || is_aucmd_win(wp)) { - return; - } - - // Jump to the target window - if (curwin != targetwin) { - win_goto(targetwin); - } - - // Remove the old window and frame from the tree of frames - int dir; - winframe_remove(wp, &dir, NULL); - win_remove(wp, NULL); - last_status(false); // may need to remove last status line - win_comp_pos(); // recompute window positions - - // Split a window on the desired side and put the old window there - win_split_ins(size, flags, wp, dir); - - // If splitting horizontally, try to preserve height - if (size == 0 && !(flags & WSP_VERT)) { - win_setheight_win(height, wp); - if (p_ea) { - win_equal(wp, true, 'v'); - } - } - - if (oldwin != curwin) { - win_goto(oldwin); - } -} - /// "win_splitmove()" function void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { win_T *wp = find_win_by_nr_or_id(&argvars[0]); win_T *targetwin = find_win_by_nr_or_id(&argvars[1]); + win_T *oldwin = curwin; + + rettv->vval.v_number = -1; if (wp == NULL || targetwin == NULL || wp == targetwin || !win_valid(wp) || !win_valid(targetwin) - || win_float_valid(wp) || win_float_valid(targetwin)) { + || targetwin->w_floating) { emsg(_(e_invalwindow)); - rettv->vval.v_number = -1; return; } @@ -732,7 +701,27 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) size = (int)tv_dict_get_number(d, "size"); } - win_move_into_split(wp, targetwin, size, flags); + // Check if we're allowed to continue before we bother switching windows. + if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(wp) == FAIL) { + return; + } + + if (curwin != targetwin) { + win_goto(targetwin); + } + + // Autocommands may have sent us elsewhere or closed "wp" or "oldwin". + if (curwin == targetwin && win_valid(wp)) { + if (win_splitmove(wp, size, flags) == OK) { + rettv->vval.v_number = 0; + } + } else { + emsg(_(e_auabort)); + } + + if (oldwin != curwin && win_valid(oldwin)) { + win_goto(oldwin); + } } /// "win_gettype(nr)" function @@ -785,7 +774,7 @@ void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "wincol()" function void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - validate_cursor(); + validate_cursor(curwin); rettv->vval.v_number = curwin->w_wcol + 1; } @@ -822,7 +811,7 @@ void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "winline()" function void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - validate_cursor(); + validate_cursor(curwin); rettv->vval.v_number = curwin->w_wrow + 1; } @@ -894,10 +883,10 @@ void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); } - check_cursor(); + check_cursor(curwin); win_new_height(curwin, curwin->w_height); win_new_width(curwin, curwin->w_width); - changed_window_setting(); + changed_window_setting(curwin); if (curwin->w_topline <= 0) { curwin->w_topline = 1; @@ -967,11 +956,8 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n if (tp != NULL) { switchwin->sw_curtab = curtab; if (no_display) { - curtab->tp_firstwin = firstwin; - curtab->tp_lastwin = lastwin; - curtab = tp; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; + unuse_tabpage(curtab); + use_tabpage(tp); } else { goto_tabpage_tp(tp, false, false); } @@ -998,11 +984,12 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) { if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { if (no_display) { - curtab->tp_firstwin = firstwin; - curtab->tp_lastwin = lastwin; - curtab = switchwin->sw_curtab; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; + win_T *const old_tp_curwin = curtab->tp_curwin; + + unuse_tabpage(curtab); + // Don't change the curwin of the tabpage we temporarily visited. + curtab->tp_curwin = old_tp_curwin; + use_tabpage(switchwin->sw_curtab); } else { goto_tabpage_tp(switchwin->sw_curtab, false, false); } diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 93948d3eaa..e1ebcecbd6 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -39,10 +39,8 @@ void loop_init(Loop *loop, void *data) /// @param ms 0: non-blocking poll. /// > 0: timeout after `ms`. /// < 0: wait forever. -/// @param once true: process at most one `Loop.uv` event. -/// false: process until `ms` timeout (only has effect if `ms` > 0). /// @return true if `ms` > 0 and was reached -bool loop_uv_run(Loop *loop, int64_t ms, bool once) +static bool loop_uv_run(Loop *loop, int64_t ms) { if (loop->recursive++) { abort(); // Should not re-enter uv_run @@ -60,9 +58,7 @@ bool loop_uv_run(Loop *loop, int64_t ms, bool once) mode = UV_RUN_NOWAIT; } - do { - uv_run(&loop->uv, mode); - } while (ms > 0 && !once && !*timeout_expired); + uv_run(&loop->uv, mode); if (ms > 0) { uv_timer_stop(&loop->poll_timer); @@ -83,7 +79,7 @@ bool loop_uv_run(Loop *loop, int64_t ms, bool once) /// @return true if `ms` > 0 and was reached bool loop_poll_events(Loop *loop, int64_t ms) { - bool timeout_expired = loop_uv_run(loop, ms, true); + bool timeout_expired = loop_uv_run(loop, ms); multiqueue_process_events(loop->fast_events); return timeout_expired; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 74ad8e95a2..7c49189602 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -541,7 +541,7 @@ void ex_sort(exarg_T *eap) // Also get the longest line length for allocating "sortbuf". for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) { char *s = ml_get(lnum); - int len = (int)strlen(s); + int len = ml_get_len(lnum); if (maxlen < len) { maxlen = len; } @@ -643,8 +643,8 @@ void ex_sort(exarg_T *eap) } char *s = ml_get(get_lnum); - size_t bytelen = strlen(s) + 1; // include EOL in bytelen - old_count += (bcount_t)bytelen; + colnr_T bytelen = ml_get_len(get_lnum) + 1; // include EOL in bytelen + old_count += bytelen; if (!unique || i == 0 || string_compare(s, sortbuf1) != 0) { // Copy the line into a buffer, it may become invalid in // ml_append(). And it's needed for "unique". @@ -652,7 +652,7 @@ void ex_sort(exarg_T *eap) if (ml_append(lnum++, sortbuf1, 0, false) == FAIL) { break; } - new_count += (bcount_t)bytelen; + new_count += bytelen; } fast_breakcheck(); if (got_int) { @@ -740,7 +740,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) return FAIL; } for (extra = 0, l = line1; l <= line2; l++) { - char *str = xstrdup(ml_get(l + extra)); + char *str = xstrnsave(ml_get(l + extra), (size_t)ml_get_len(l + extra)); ml_append(dest + l - line1, str, 0, false); xfree(str); if (dest < line1) { @@ -876,9 +876,8 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) curwin->w_cursor.lnum = n; while (line1 <= line2) { - // need to use xstrdup() because the line will be unlocked within - // ml_append() - char *p = xstrdup(ml_get(line1)); + // need to make a copy because the line will be unlocked within ml_append() + char *p = xstrnsave(ml_get(line1), (size_t)ml_get_len(line1)); ml_append(curwin->w_cursor.lnum, p, 0, false); xfree(p); @@ -2008,6 +2007,10 @@ static int check_readonly(int *forceit, buf_T *buf) /// GETFILE_OPEN_OTHER for successfully opening another file. int getfile(int fnum, char *ffname_arg, char *sfname_arg, bool setpm, linenr_T lnum, bool forceit) { + if (!check_can_set_curbuf_forceit(forceit)) { + return GETFILE_ERROR; + } + char *ffname = ffname_arg; char *sfname = sfname_arg; bool other; @@ -2634,14 +2637,14 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum if (newcol >= 0) { // position set by autocommands curwin->w_cursor.lnum = newlnum; curwin->w_cursor.col = newcol; - check_cursor(); + check_cursor(curwin); } else if (newlnum > 0) { // line number from caller or old position curwin->w_cursor.lnum = newlnum; check_cursor_lnum(curwin); if (solcol >= 0 && !p_sol) { // 'sol' is off: Use last known column. curwin->w_cursor.col = solcol; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } else { @@ -3296,7 +3299,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n if (nmatch > 1) { \ sub_firstlnum += (linenr_T)nmatch - 1; \ xfree(sub_firstline); \ - sub_firstline = xstrdup(ml_get(sub_firstlnum)); \ + sub_firstline = xstrnsave(ml_get(sub_firstlnum), \ + (size_t)ml_get_len(sub_firstlnum)); \ /* When going beyond the last line, stop substituting. */ \ if (sub_firstlnum <= line2) { \ do_again = true; \ @@ -3624,7 +3628,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n break; } if (sub_firstline == NULL) { - sub_firstline = xstrdup(ml_get(sub_firstlnum)); + sub_firstline = xstrnsave(ml_get(sub_firstlnum), + (size_t)ml_get_len(sub_firstlnum)); } // Save the line number of the last change for the final @@ -3761,7 +3766,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n // really update the line, it would change // what matches. Temporarily replace the line // and change it back afterwards. - orig_line = xstrdup(ml_get(lnum)); + orig_line = xstrnsave(ml_get(lnum), (size_t)ml_get_len(lnum)); char *new_line = concat_str(new_start, sub_firstline + copycol); // Position the cursor relative to the end of the line, the @@ -3783,7 +3788,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n highlight_match = true; update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_SOME_VALID); show_cursor_info_later(true); update_screen(); @@ -4243,7 +4248,7 @@ skip: // when interactive leave cursor on the match if (!subflags.do_ask) { if (endcolumn) { - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { beginline(BL_WHITE | BL_FIX); } @@ -4274,7 +4279,7 @@ skip: if (subflags.do_ask && hasAnyFolding(curwin)) { // Cursor position may require updating - changed_window_setting(); + changed_window_setting(curwin); } vim_regfree(regmatch.regprog); @@ -4510,7 +4515,7 @@ void global_exe(char *cmd) if (global_need_beginline) { beginline(BL_WHITE | BL_FIX); } else { - check_cursor(); // cursor may be beyond the end of the line + check_cursor(curwin); // cursor may be beyond the end of the line } // the cursor may not have moved in the text but a change in a previous @@ -4622,8 +4627,8 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i } char *str = NULL; // construct the line to show in here - size_t old_line_size = 0; - size_t line_size = 0; + colnr_T old_line_size = 0; + colnr_T line_size = 0; linenr_T linenr_preview = 0; // last line added to preview buffer linenr_T linenr_origbuf = 0; // last line added to original buffer linenr_T next_linenr = 0; // next line to show for the match @@ -4662,21 +4667,21 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i line = ""; } else { line = ml_get_buf(orig_buf, next_linenr); - line_size = strlen(line) + (size_t)col_width + 1; + line_size = ml_get_buf_len(orig_buf, next_linenr) + col_width + 1; // Reallocate if line not long enough if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); + str = xrealloc(str, (size_t)line_size * sizeof(char)); old_line_size = line_size; } } // Put "|lnum| line" into `str` and append it to the preview buffer. - snprintf(str, line_size, "|%*" PRIdLINENR "| %s", col_width - 3, + snprintf(str, (size_t)line_size, "|%*" PRIdLINENR "| %s", col_width - 3, next_linenr, line); if (linenr_preview == 0) { ml_replace_buf(cmdpreview_buf, 1, str, true, false); } else { - ml_append_buf(cmdpreview_buf, linenr_preview, str, (colnr_T)line_size, false); + ml_append_buf(cmdpreview_buf, linenr_preview, str, line_size, false); } linenr_preview += 1; } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 1318eda5eb..e2196f99ec 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -812,7 +812,7 @@ module.cmds = { }, { command = 'drop', - flags = bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), + flags = bit.bor(BANG, FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), addr_type = 'ADDR_NONE', func = 'ex_drop', }, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 8016e37ca7..a34eb0232b 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -444,6 +444,30 @@ int buf_write_all(buf_T *buf, bool forceit) /// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" void ex_listdo(exarg_T *eap) { + if (curwin->w_p_wfb) { + if ((eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) && !eap->forceit) { + // Disallow :ldo if 'winfixbuf' is applied + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return; + } + + if (win_valid(prevwin) && !prevwin->w_p_wfb) { + // 'winfixbuf' is set; attempt to change to a window without it. + win_goto(prevwin); + } + if (curwin->w_p_wfb) { + // Split the window, which will be 'nowinfixbuf', and set curwin to that + (void)win_split(0, 0); + + if (curwin->w_p_wfb) { + // Autocommands set 'winfixbuf' or sent us to another window + // with it set, or we failed to split the window. Give up. + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return; + } + } + } + char *save_ei = NULL; // Temporarily override SHM_OVER and SHM_OVERALL to avoid that file @@ -630,7 +654,7 @@ void ex_listdo(exarg_T *eap) } if (eap->cmdidx == CMD_windo && execute) { - validate_cursor(); // cursor may have moved + validate_cursor(curwin); // cursor may have moved // required when 'scrollbind' has been set if (curwin->w_p_scb) { do_check_scrollbind(true); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2913f6d4e9..6db72ff2d1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1743,8 +1743,8 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) && eap->addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. - hasFolding(eap->line1, &eap->line1, NULL); - hasFolding(eap->line2, NULL, &eap->line2); + hasFolding(curwin, eap->line1, &eap->line1, NULL); + hasFolding(curwin, eap->line2, NULL, &eap->line2); } // Use first argument as count when possible @@ -2213,8 +2213,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter && ea.addr_type == ADDR_LINES) { // Put the first line at the start of a closed fold, put the last line // at the end of a closed fold. - hasFolding(ea.line1, &ea.line1, NULL); - hasFolding(ea.line2, NULL, &ea.line2); + hasFolding(curwin, ea.line1, &ea.line1, NULL); + hasFolding(curwin, ea.line2, NULL, &ea.line2); } // For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' @@ -2875,9 +2875,9 @@ int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) // (where zero usually means to use the first line). // Check the cursor position before returning. if (eap->line2 > 0) { - check_cursor(); + check_cursor(curwin); } else { - check_cursor_col(); + check_cursor_col(curwin); } need_check_cursor = true; } @@ -2899,7 +2899,7 @@ int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) theend: if (need_check_cursor) { - check_cursor(); + check_cursor(curwin); } return ret; } @@ -3596,7 +3596,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool // closed fold after the first address. if (addr_type == ADDR_LINES && (i == '-' || i == '+') && address_count >= 2) { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); } if (i == '-') { lnum -= n; @@ -5334,6 +5334,10 @@ static void ex_resize(exarg_T *eap) /// ":find [+command] <file>" command. static void ex_find(exarg_T *eap) { + if (!check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + char *file_to_find = NULL; char *search_ctx = NULL; char *fname = find_file_in_path(eap->arg, strlen(eap->arg), @@ -5364,6 +5368,14 @@ static void ex_find(exarg_T *eap) /// ":edit", ":badd", ":balt", ":visual". static void ex_edit(exarg_T *eap) { + // Exclude commands which keep the window's current buffer + if (eap->cmdidx != CMD_badd + && eap->cmdidx != CMD_balt + // All other commands must obey 'winfixbuf' / ! rules + && !check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + do_exedit(eap, NULL); } @@ -5516,8 +5528,6 @@ static void ex_swapname(exarg_T *eap) /// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>) static void ex_syncbind(exarg_T *eap) { - win_T *save_curwin = curwin; - buf_T *save_curbuf = curbuf; linenr_T topline; int y; linenr_T old_linenr = curwin->w_cursor.lnum; @@ -5544,23 +5554,19 @@ static void ex_syncbind(exarg_T *eap) // Set all scrollbind windows to the same topline. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - curwin = wp; - if (curwin->w_p_scb) { - curbuf = curwin->w_buffer; - y = topline - curwin->w_topline; + if (wp->w_p_scb) { + y = topline - wp->w_topline; if (y > 0) { - scrollup(y, true); + scrollup(wp, y, true); } else { - scrolldown(-y, true); + scrolldown(wp, -y, true); } - curwin->w_scbind_pos = topline; - redraw_later(curwin, UPD_VALID); - cursor_correct(); - curwin->w_redr_status = true; + wp->w_scbind_pos = topline; + redraw_later(wp, UPD_VALID); + cursor_correct(wp); + wp->w_redr_status = true; } } - curwin = save_curwin; - curbuf = save_curbuf; if (curwin->w_p_scb) { did_syncbind = true; checkpcmark(); @@ -5842,7 +5848,7 @@ static void ex_equal(exarg_T *eap) static void ex_sleep(exarg_T *eap) { - if (cursor_valid()) { + if (cursor_valid(curwin)) { setcursor_mayforce(true); } @@ -5978,7 +5984,7 @@ static void ex_put(exarg_T *eap) eap->forceit = true; } curwin->w_cursor.lnum = eap->line2; - check_cursor_col(); + check_cursor_col(curwin); do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1, PUT_LINE|PUT_CURSLINE); } @@ -6072,7 +6078,7 @@ static void ex_at(exarg_T *eap) int prev_len = typebuf.tb_len; curwin->w_cursor.lnum = eap->line2; - check_cursor_col(); + check_cursor_col(curwin); // Get the register name. No name means use the previous one. int c = (uint8_t)(*eap->arg); @@ -6294,7 +6300,7 @@ static void ex_redraw(exarg_T *eap) RedrawingDisabled = 0; p_lz = false; - validate_cursor(); + validate_cursor(curwin); update_topline(curwin); if (eap->forceit) { redraw_all_later(UPD_NOT_VALID); @@ -6447,10 +6453,10 @@ static void ex_mark(exarg_T *eap) /// Update w_topline, w_leftcol and the cursor position. void update_topline_cursor(void) { - check_cursor(); // put cursor on valid line + check_cursor(curwin); // put cursor on valid line update_topline(curwin); if (!curwin->w_p_wrap) { - validate_cursor(); + validate_cursor(curwin); } update_curswant(); } @@ -6670,7 +6676,7 @@ static void ex_checkpath(exarg_T *eap) { find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1, eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW, - 1, (linenr_T)MAXLNUM); + 1, (linenr_T)MAXLNUM, eap->forceit); } /// ":psearch" @@ -6729,7 +6735,7 @@ static void ex_findpat(exarg_T *eap) if (!eap->skip) { find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit, *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, - n, action, eap->line1, eap->line2); + n, action, eap->line1, eap->line2, eap->forceit); } } @@ -6754,7 +6760,7 @@ static void ex_pedit(exarg_T *eap) if (curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } @@ -7396,7 +7402,7 @@ static void ex_folddo(exarg_T *eap) { // First set the marks for all lines closed/open. for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) { - if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) { + if (hasFolding(curwin, lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) { ml_setmarked(lnum); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 44a78711d2..303337ae98 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -510,7 +510,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state s->match_start = curwin->w_cursor; set_search_match(&curwin->w_cursor); - validate_cursor(); + validate_cursor(curwin); end_pos = curwin->w_cursor; s->match_end = end_pos; curwin->w_cursor = save_pos; @@ -530,7 +530,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state ccline.cmdbuff[skiplen + patlen] = next_char; } - validate_cursor(); + validate_cursor(curwin); // May redraw the status line to show the cursor position. if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { @@ -626,7 +626,7 @@ static void finish_incsearch_highlighting(bool gotesc, incsearch_state_T *s, magic_overruled = s->magic_overruled_save; - validate_cursor(); // needed for TAB + validate_cursor(curwin); // needed for TAB status_redraw_all(); redraw_all_later(UPD_SOME_VALID); if (call_update_screen) { @@ -1483,7 +1483,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s curwin->w_cursor = s->match_start; changed_cline_bef_curs(curwin); update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); highlight_match = true; save_viewstate(curwin, &s->old_viewstate); redraw_later(curwin, UPD_NOT_VALID); @@ -4623,6 +4623,6 @@ static void set_search_match(pos_T *t) t->col = search_match_endcol; if (t->lnum > curbuf->b_ml.ml_line_count) { t->lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 2c96e4bd87..f032604a59 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -957,7 +957,7 @@ retry: int tlen = 0; while (true) { p = (uint8_t *)ml_get(read_buf_lnum) + read_buf_col; - int n = (int)strlen((char *)p); + int n = ml_get_len(read_buf_lnum) - read_buf_col; if (tlen + n + 1 > size) { // Filled up to "size", append partial line. // Change NL to NUL to reverse the effect done @@ -2629,7 +2629,7 @@ static int rename_with_tmp(const char *const from, const char *const to) STRCPY(tempname, from); for (int n = 123; n < 99999; n++) { char *tail = path_tail(tempname); - snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname - 1)), "%d", n); + snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - tempname)), "%d", n); if (!os_path_exists(tempname)) { if (os_rename(from, tempname) == OK) { @@ -3153,7 +3153,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) curbuf->b_flags |= BF_CHECK_RO; // check for RO again keep_filetype = true; // don't detect 'filetype' if (readfile(buf->b_ffname, buf->b_fname, 0, 0, - (linenr_T)MAXLNUM, &ea, flags, false) != OK) { + (linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) { if (!aborting()) { semsg(_("E321: Could not reload \"%s\""), buf->b_fname); } @@ -3197,7 +3197,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) curwin->w_topline = old_topline; } curwin->w_cursor = old_cursor; - check_cursor(); + check_cursor(curwin); update_topline(curwin); keep_filetype = false; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index c571aaf0a4..be3295c44c 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -143,7 +143,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to) } // hasAnyFolding() {{{2 -/// @return true if there may be folded lines in the current window. +/// @return true if there may be folded lines in window "win". int hasAnyFolding(win_T *win) { // very simple now, but can become more complex later @@ -155,10 +155,10 @@ int hasAnyFolding(win_T *win) /// When returning true, *firstp and *lastp are set to the first and last /// lnum of the sequence of folded lines (skipped when NULL). /// -/// @return true if line "lnum" in the current window is part of a closed fold. -bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp) +/// @return true if line "lnum" in window "win" is part of a closed fold. +bool hasFolding(win_T *win, linenr_T lnum, linenr_T *firstp, linenr_T *lastp) { - return hasFoldingWin(curwin, lnum, firstp, lastp, true, NULL); + return hasFoldingWin(win, lnum, firstp, lastp, true, NULL); } // hasFoldingWin() {{{2 @@ -398,13 +398,13 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, bool h // Opening one level only: next fold to open is after the one going to // be opened. if (opening && !recurse) { - hasFolding(lnum, NULL, &lnum_next); + hasFolding(curwin, lnum, NULL, &lnum_next); } setManualFold(temp, opening, recurse, &done); // Closing one level only: next line to close a fold is after just // closed fold. if (!opening && !recurse) { - hasFolding(lnum, NULL, &lnum_next); + hasFolding(curwin, lnum, NULL, &lnum_next); } } if (done == DONE_NOTHING) { @@ -477,7 +477,7 @@ static void newFoldLevelWin(win_T *wp) } wp->w_fold_manual = false; } - changed_window_setting_win(wp); + changed_window_setting(wp); } // foldCheckClose() {{{2 @@ -492,7 +492,7 @@ void foldCheckClose(void) checkupdate(curwin); if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum, (int)curwin->w_p_fdl)) { - changed_window_setting(); + changed_window_setting(curwin); } } @@ -518,7 +518,7 @@ static bool checkCloseRec(garray_T *gap, linenr_T lnum, int level) return retval; } -// foldCreateAllowed() {{{2 +// foldManualAllowed() {{{2 /// @return true if it's allowed to manually create or delete a fold or, /// give an error message and return false if not. int foldManualAllowed(bool create) @@ -661,7 +661,7 @@ void foldCreate(win_T *wp, pos_T start, pos_T end) fp->fd_small = kNone; // redraw - changed_window_setting_win(wp); + changed_window_setting(wp); } } @@ -735,7 +735,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const did_one = true; // redraw window - changed_window_setting_win(wp); + changed_window_setting(wp); } } if (!did_one) { @@ -746,7 +746,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const } } else { // Deleting markers may make cursor column invalid - check_cursor_col_win(wp); + check_cursor_col(wp); } if (last_lnum > 0) { @@ -1009,16 +1009,15 @@ void foldAdjustVisual(void) start = &curwin->w_cursor; end = &VIsual; } - if (hasFolding(start->lnum, &start->lnum, NULL)) { + if (hasFolding(curwin, start->lnum, &start->lnum, NULL)) { start->col = 0; } - if (!hasFolding(end->lnum, NULL, &end->lnum)) { + if (!hasFolding(curwin, end->lnum, NULL, &end->lnum)) { return; } - char *ptr = ml_get(end->lnum); - end->col = (colnr_T)strlen(ptr); + end->col = ml_get_len(end->lnum); if (end->col > 0 && *p_sel == 'o') { end->col--; } @@ -1026,11 +1025,11 @@ void foldAdjustVisual(void) mb_adjust_cursor(); } -// cursor_foldstart() {{{2 +// foldAdjustCursor() {{{2 /// Move the cursor to the first line of a closed fold. -void foldAdjustCursor(void) +void foldAdjustCursor(win_T *wp) { - hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); + hasFolding(wp, wp->w_cursor.lnum, &wp->w_cursor.lnum, NULL); } // Internal functions for "fold_T" {{{1 @@ -1269,7 +1268,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, bool opening, bool re } wp->w_fold_manual = true; if (done & DONE_ACTION) { - changed_window_setting_win(wp); + changed_window_setting(wp); } done |= DONE_FOLD; } else if (donep == NULL && wp == curwin) { @@ -1605,7 +1604,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark // Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end char *line = ml_get_buf(buf, lnum); - size_t line_len = strlen(line); + size_t line_len = (size_t)ml_get_buf_len(buf, lnum); size_t added = 0; if (u_save(lnum - 1, lnum + 1) != OK) { @@ -1686,7 +1685,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker } if (u_save(lnum - 1, lnum + 1) == OK) { // Make new line: text-before-marker + text-after-marker - char *newline = xmalloc(strlen(line) - len + 1); + char *newline = xmalloc((size_t)ml_get_buf_len(buf, lnum) - len + 1); assert(p >= line); memcpy(newline, line, (size_t)(p - line)); STRCPY(newline + (p - line), p + len); @@ -2117,7 +2116,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) // If some fold changed, need to redraw and position cursor. if (fold_changed && wp->w_p_fen) { - changed_window_setting_win(wp); + changed_window_setting(wp); } // If we updated folds past "bot", need to redraw more lines. Don't do diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 78a83445af..e9bc5e5fe3 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -259,7 +259,7 @@ put('version') fixdict(1 + #version) for _, item in ipairs(version) do -- NB: all items are mandatory. But any error will be less confusing - -- with placholder vim.NIL (than invalid mpack data) + -- with placeholder vim.NIL (than invalid mpack data) put(item[1], item[2] or vim.NIL) end put('build', version_build) @@ -307,7 +307,6 @@ output:write([[ #include "nvim/globals.h" #include "nvim/log.h" #include "nvim/map_defs.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/api/autocmd.h" #include "nvim/api/buffer.h" diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index a4f59c7209..0808f71daa 100644 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -110,10 +110,9 @@ for i = 1, #events do if not ev.remote_only then if not ev.remote_impl and not ev.noexport then remote_output:write('void remote_ui_' .. ev.name) - write_signature(remote_output, ev, 'UI *ui') + write_signature(remote_output, ev, 'RemoteUI *ui') remote_output:write('\n{\n') - remote_output:write(' UIData *data = ui->data;\n') - remote_output:write(' Array args = data->call_buf;\n') + remote_output:write(' Array args = ui->call_buf;\n') write_arglist(remote_output, ev) remote_output:write(' push_call(ui, "' .. ev.name .. '", args);\n') remote_output:write('}\n\n') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 64c9c5a8c3..f68bd7098b 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2508,7 +2508,7 @@ static int vgetorpeek(bool advance) unshowmode(true); mode_deleted = true; } - validate_cursor(); + validate_cursor(curwin); int old_wcol = curwin->w_wcol; int old_wrow = curwin->w_wrow; @@ -2541,7 +2541,7 @@ static int vgetorpeek(bool advance) curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner; - curwin->w_wcol += curwin_col_off(); + curwin->w_wcol += win_col_off(curwin); col = 0; // no correction needed } else { curwin->w_wcol--; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 22f7daa823..bb9aca38b7 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -363,7 +363,7 @@ EXTERN bool sys_menu INIT( = false); // currently active window. EXTERN win_T *firstwin; // first window EXTERN win_T *lastwin; // last window -EXTERN win_T *prevwin INIT( = NULL); // previous window +EXTERN win_T *prevwin INIT( = NULL); // previous window (may equal curwin) #define ONE_WINDOW (firstwin == lastwin) #define FOR_ALL_FRAMES(frp, first_frame) \ for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next) @@ -939,6 +939,7 @@ EXTERN const char e_using_float_as_string[] INIT(= N_("E806: Using a Float as a 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")); @@ -957,6 +958,7 @@ EXTERN const char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invali 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[] @@ -969,6 +971,9 @@ 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")); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 5723ea1198..8729c74ce8 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -127,7 +127,7 @@ retry: {} } /// When a UI connects, we need to send it the table of highlights used so far. -void ui_send_all_hls(UI *ui) +void ui_send_all_hls(RemoteUI *ui) { for (size_t i = 1; i < set_size(&attr_entries); i++) { Arena arena = ARENA_EMPTY; diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 75c23c5bc4..1474a2ba06 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1149,9 +1149,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) error = true; break; } - memcpy(key, key_start, key_len); - key[key_len] = NUL; - vim_strup(key); + vim_memcpy_up(key, key_start, key_len); + key[key_len] = '\0'; linep = skipwhite(linep); if (strcmp(key, "NONE") == 0) { @@ -1943,9 +1942,8 @@ 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. - memcpy(name_u, name, len); + vim_memcpy_up(name_u, name, len); name_u[len] = '\0'; - vim_strup(name_u); // 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 14247b6d86..6cbb86866e 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -1116,7 +1116,7 @@ void ex_retab(exarg_T *eap) } xfree(new_ts_str); } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); u_clearline(curbuf); } @@ -1160,7 +1160,7 @@ int get_expr_indent(void) curwin->w_cursor = save_pos; curwin->w_curswant = save_curswant; curwin->w_set_curswant = save_set_curswant; - check_cursor(); + check_cursor(curwin); State = save_State; // Reset did_throw, unless 'debug' has "throw" and inside a try/catch. diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 9d3b400496..526a17cfd4 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -2436,7 +2436,7 @@ static void expand_by_function(int type, char *base) textlock--; curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); + validate_cursor(curwin); if (!equalpos(curwin->w_cursor, pos)) { emsg(_(e_compldel)); goto theend; @@ -2937,7 +2937,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar // buffer, so that word at start of buffer is found // correctly. st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count; - st->first_match_pos.col = (colnr_T)strlen(ml_get(st->first_match_pos.lnum)); + st->first_match_pos.col = ml_get_len(st->first_match_pos.lnum); } st->last_match_pos = st->first_match_pos; compl_type = 0; @@ -3027,7 +3027,7 @@ static void get_next_include_file_completion(int compl_type) ((compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY), - 1, ACTION_EXPAND, 1, MAXLNUM); + 1, ACTION_EXPAND, 1, MAXLNUM, false); } /// Get the next set of words matching "compl_pattern" in dictionary or @@ -3557,7 +3557,12 @@ void ins_compl_delete(void) /// "in_compl_func" is true when called from complete_check(). void ins_compl_insert(bool in_compl_func) { - ins_bytes(compl_shown_match->cp_str + get_compl_len()); + int compl_len = get_compl_len(); + // Make sure we don't go over the end of the string, this can happen with + // illegal bytes. + if (compl_len < (int)strlen(compl_shown_match->cp_str)) { + ins_bytes(compl_shown_match->cp_str + compl_len); + } compl_used_match = !match_at_original_text(compl_shown_match); dict_T *dict = ins_compl_dict_alloc(compl_shown_match); @@ -4091,7 +4096,7 @@ static int get_userdefined_compl_info(colnr_T curs_col) State = save_State; curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); + validate_cursor(curwin); if (!equalpos(curwin->w_cursor, pos)) { emsg(_(e_compldel)); return FAIL; diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 0b7f58cec6..bba771f8a5 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -1381,9 +1381,11 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { nlua_push_Object(L, (Object *)mem, false); - } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer - || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { + } else if (field->type == kObjectTypeInteger) { lua_pushinteger(L, *(Integer *)mem); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + lua_pushinteger(L, *(handle_T *)mem); } else if (field->type == kObjectTypeFloat) { lua_pushnumber(L, *(Float *)mem); } else if (field->type == kObjectTypeBoolean) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 1a9bd026b5..78c746d169 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1767,7 +1767,7 @@ void ex_luado(exarg_T *const eap) lua_pushvalue(lstate, -1); const char *const old_line = ml_get_buf(curbuf, l); // Get length of old_line here as calling Lua code may free it. - const size_t old_line_len = strlen(old_line); + const colnr_T old_line_len = ml_get_buf_len(curbuf, l); lua_pushstring(lstate, old_line); lua_pushnumber(lstate, (lua_Number)l); if (nlua_pcall(lstate, 2, 1)) { @@ -1791,13 +1791,13 @@ void ex_luado(exarg_T *const eap) } } ml_replace(l, new_line_transformed, false); - inserted_bytes(l, 0, (int)old_line_len, (int)new_line_len); + inserted_bytes(l, 0, old_line_len, (int)new_line_len); } lua_pop(lstate, 1); } lua_pop(lstate, 1); - check_cursor(); + check_cursor(curwin); redraw_curbuf_later(UPD_NOT_VALID); } diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 8f58fd1a1a..a5262efcfa 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -107,15 +107,15 @@ static int regex_match_line(lua_State *lstate) } char *line = ml_get_buf(buf, rownr + 1); - size_t len = strlen(line); + colnr_T len = ml_get_buf_len(buf, rownr + 1); - if (start < 0 || (size_t)start > len) { + if (start < 0 || start > len) { return luaL_error(lstate, "invalid start"); } char save = NUL; if (end >= 0) { - if ((size_t)end > len || end < start) { + if (end > len || end < start) { return luaL_error(lstate, "invalid end"); } save = line[end]; diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 25a753b179..6d6ef6c7b9 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -371,7 +371,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position return ""; } char *line = ml_get_buf(bp, (linenr_T)position.row + 1); - size_t len = strlen(line); + size_t len = (size_t)ml_get_buf_len(bp, (linenr_T)position.row + 1); if (position.column > len) { *bytes_read = 0; return ""; diff --git a/src/nvim/main.c b/src/nvim/main.c index 6b862f2ed9..ea189aaa0c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -70,7 +70,6 @@ #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -152,11 +151,9 @@ void event_init(void) loop_init(&main_loop, NULL); resize_events = multiqueue_new_child(main_loop.events); - // early msgpack-rpc initialization - msgpack_rpc_helpers_init(); input_init(); signal_init(); - // finish mspgack-rpc initialization + // mspgack-rpc initialization channel_init(); terminal_init(); ui_init(); @@ -366,7 +363,7 @@ int main(int argc, char **argv) setbuf(stdout, NULL); // NOLINT(bugprone-unsafe-functions) - full_screen = !silent_mode; + full_screen = !silent_mode || exmode_active; // Set the default values for the options that use Rows and Columns. win_init_size(); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 34e35a8277..6ce42bb7fe 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -588,7 +588,7 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags) } if (res & kMarkSwitchedBuf || res & kMarkChangedCursor) { - check_cursor(); + check_cursor(curwin); } end: return res; @@ -1243,11 +1243,11 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount if (win != curwin || by_api) { if (win->w_topline >= line1 && win->w_topline <= line2) { if (amount == MAXLNUM) { // topline is deleted - if (line1 <= 1) { - win->w_topline = 1; + if (by_api && amount_after > line1 - line2 - 1) { + // api: if the deleted region was replaced with new contents, topline will + // get adjusted later as an effect of the adjusted cursor in fix_cursor() } else { - // api: if the deleted region was replaced with new contents, display that - win->w_topline = (by_api && amount_after > line1 - line2 - 1) ? line1 : line1 - 1; + win->w_topline = MAX(line1 - 1, 1); } } else if (win->w_topline > line1) { // keep topline on the same line, unless inserting just @@ -1715,7 +1715,7 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) { if (lp->col > 0 || lp->coladd > 1) { const char *const p = ml_get_buf(buf, lp->lnum); - if (*p == NUL || (int)strlen(p) < lp->col) { + if (*p == NUL || ml_get_buf_len(buf, lp->lnum) < lp->col) { lp->col = 0; } else { lp->col -= utf_head_off(p, p + lp->col); diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 0ebebf409e..34d6cd118f 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -460,7 +460,7 @@ static void meta_describe_key(uint32_t *meta_inc, MTKey k) meta_describe_key_inc(meta_inc, &k); } -// if x is internal, asumes x->meta[..] of children are correct +// if x is internal, assumes x->meta[..] of children are correct static void meta_describe_node(uint32_t *meta_node, MTNode *x) { memset(meta_node, 0, kMTMetaCount * sizeof(meta_node[0])); @@ -1425,7 +1425,7 @@ bool marktree_itr_get_ext(MarkTree *b, MTPos p, MarkTreeIter *itr, bool last, bo } if (meta_filter) { if (!meta_has(itr->x->meta[itr->i], meta_filter)) { - // this takes us to the interal position after the first rejected node + // this takes us to the internal position after the first rejected node break; } } diff --git a/src/nvim/match.c b/src/nvim/match.c index c8837969b6..ea8a1a05f4 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -533,7 +533,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) for (shl->first_lnum = lnum; shl->first_lnum > wp->w_topline; shl->first_lnum--) { - if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { + if (hasFolding(wp, shl->first_lnum - 1, NULL, NULL)) { break; } } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 6b2f26b2d8..5acf4f0c37 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -30,6 +30,10 @@ // changed (lines appended/deleted/changed) or when it is flushed it gets a // positive number. Use mf_trans_del() to get the new number, before calling // mf_get(). +// +// "Mom, can we get ropes?" +// "We have ropes at home." +// Ropes at home: #include <assert.h> #include <errno.h> @@ -1174,7 +1178,7 @@ void ml_recover(bool checkext) } else { for (idx = 1; idx <= lnum; idx++) { // Need to copy one line, fetching the other one may flush it. - p = xstrdup(ml_get(idx)); + p = xstrnsave(ml_get(idx), (size_t)ml_get_len(idx)); int i = strcmp(p, ml_get(idx + lnum)); xfree(p); if (i != 0) { @@ -1192,7 +1196,7 @@ void ml_recover(bool checkext) ml_delete(curbuf->b_ml.ml_line_count, false); } curbuf->b_flags |= BF_RECOVERED; - check_cursor(); + check_cursor(curwin); recoverymode = false; if (got_int) { @@ -1834,6 +1838,28 @@ char *ml_get_pos(const pos_T *pos) return ml_get_buf(curbuf, pos->lnum) + pos->col; } +/// @return length (excluding the NUL) of the given line. +colnr_T ml_get_len(linenr_T lnum) +{ + return ml_get_buf_len(curbuf, lnum); +} + +/// @return length (excluding the NUL) of the text after position "pos". +colnr_T ml_get_pos_len(pos_T *pos) +{ + return ml_get_buf_len(curbuf, pos->lnum) - pos->col; +} + +/// @return length (excluding the NUL) of the given line in the given buffer. +colnr_T ml_get_buf_len(buf_T *buf, linenr_T lnum) +{ + if (*ml_get_buf(buf, lnum) == NUL) { + return 0; + } + + return buf->b_ml.ml_line_len - 1; +} + /// @return codepoint at pos. pos must be either valid or have col set to MAXCOL! int gchar_pos(pos_T *pos) FUNC_ATTR_NONNULL_ARG(1) @@ -1865,6 +1891,7 @@ static char *ml_get_buf_impl(buf_T *buf, linenr_T lnum, bool will_change) ml_flush_line(buf, false); errorret: STRCPY(questions, "???"); + buf->b_ml.ml_line_len = 4; buf->b_ml.ml_line_lnum = lnum; return questions; } @@ -1873,6 +1900,7 @@ errorret: } if (buf->b_ml.ml_mfp == NULL) { // there are no lines + buf->b_ml.ml_line_len = 1; return ""; } @@ -1903,8 +1931,14 @@ errorret: DataBlock *dp = hp->bh_data; - char *ptr = (char *)dp + (dp->db_index[lnum - buf->b_ml.ml_locked_low] & DB_INDEX_MASK); - buf->b_ml.ml_line_ptr = ptr; + int idx = lnum - buf->b_ml.ml_locked_low; + unsigned start = (dp->db_index[idx] & DB_INDEX_MASK); + // The text ends where the previous line starts. The first line ends + // at the end of the block. + unsigned end = idx == 0 ? dp->db_txt_end : (dp->db_index[idx - 1] & DB_INDEX_MASK); + + buf->b_ml.ml_line_ptr = (char *)dp + start; + buf->b_ml.ml_line_len = (colnr_T)(end - start); buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); } @@ -1922,7 +1956,8 @@ errorret: #ifdef ML_GET_ALLOC_LINES if ((buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0) { // make sure the text is in allocated memory - buf->b_ml.ml_line_ptr = xstrdup(buf->b_ml.ml_line_ptr); + buf->b_ml.ml_line_ptr = xmemdup(buf->b_ml.ml_line_ptr, + (size_t)buf->b_ml.ml_line_len); buf->b_ml.ml_flags |= ML_ALLOCATED; if (will_change) { // can't make the change in the data block @@ -2468,6 +2503,7 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy, bool noallo } buf->b_ml.ml_line_ptr = line; + buf->b_ml.ml_line_len = (colnr_T)strlen(line) + 1; buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags = (buf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; if (noalloc) { @@ -2765,7 +2801,7 @@ static void ml_flush_line(buf_T *buf, bool noalloc) } else { // text of previous line follows old_len = (int)(dp->db_index[idx - 1] & DB_INDEX_MASK) - start; } - colnr_T new_len = (colnr_T)strlen(new_line) + 1; + colnr_T new_len = buf->b_ml.ml_line_len; int extra = new_len - old_len; // negative if lines gets smaller // if new line fits in data block, replace directly @@ -3456,7 +3492,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ char *const name = xmalloc(name_len); memcpy(name, sw_msg_1, sw_msg_1_len + 1); - home_replace(NULL, fname, &name[sw_msg_1_len], fname_len, true); + home_replace(NULL, fname, name + sw_msg_1_len, fname_len, true); xstrlcat(name, sw_msg_2, name_len); int dialog_result = do_dialog(VIM_WARNING, @@ -3734,7 +3770,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, int len, int updtype) // First line in empty buffer from ml_flush_line() -- reset buf->b_ml.ml_usedchunks = 1; buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; - buf->b_ml.ml_chunksize[0].mlcs_totalsize = (int)strlen(buf->b_ml.ml_line_ptr) + 1; + buf->b_ml.ml_chunksize[0].mlcs_totalsize = buf->b_ml.ml_line_len; return; } @@ -4050,14 +4086,14 @@ void goto_byte(int cnt) if (lnum < 1) { // past the end curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_curswant = MAXCOL; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = (colnr_T)boff; curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } - check_cursor(); + check_cursor(curwin); // Make sure the cursor is on the first byte of a multi-byte char. mb_adjust_cursor(); @@ -4107,7 +4143,7 @@ int dec(pos_T *lp) if (lp->col == MAXCOL) { // past end of line char *p = ml_get(lp->lnum); - lp->col = (colnr_T)strlen(p); + lp->col = ml_get_len(lp->lnum); lp->col -= utf_head_off(p, p + lp->col); return 0; } @@ -4123,7 +4159,7 @@ int dec(pos_T *lp) // there is a prior line lp->lnum--; char *p = ml_get(lp->lnum); - lp->col = (colnr_T)strlen(p); + lp->col = ml_get_len(lp->lnum); lp->col -= utf_head_off(p, p + lp->col); return 1; } diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index 1a217c96d4..f675a2b15f 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -56,6 +56,7 @@ typedef struct { #define ML_ALLOCATED 0x10 // ml_line_ptr is an allocated copy int ml_flags; + colnr_T ml_line_len; // length of the cached line + NUL linenr_T ml_line_lnum; // line number of cached line, 0 if not valid char *ml_line_ptr; // pointer to cached line size_t ml_line_offset; // cached byte offset of ml_line_lnum diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 4520635137..37e53e4453 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -554,7 +554,6 @@ void time_to_bytes(time_t time_, uint8_t buf[8]) } } -#define ARENA_BLOCK_SIZE 4096 #define REUSE_MAX 4 static struct consumed_blk *arena_reuse_blk; @@ -583,17 +582,26 @@ ArenaMem arena_finish(Arena *arena) return res; } -void alloc_block(Arena *arena) +/// allocate a block of ARENA_BLOCK_SIZE +/// +/// free it with free_block +void *alloc_block(void) { - struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk; if (arena_reuse_blk_count > 0) { - arena->cur_blk = (char *)arena_reuse_blk; + void *retval = (char *)arena_reuse_blk; arena_reuse_blk = arena_reuse_blk->prev; arena_reuse_blk_count--; + return retval; } else { arena_alloc_count++; - arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE); + return xmalloc(ARENA_BLOCK_SIZE); } +} + +void arena_alloc_block(Arena *arena) +{ + struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk; + arena->cur_blk = alloc_block(); arena->pos = 0; arena->size = ARENA_BLOCK_SIZE; struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true); @@ -615,7 +623,7 @@ void *arena_alloc(Arena *arena, size_t size, bool align) return xmalloc(size); } if (!arena->cur_blk) { - alloc_block(arena); + arena_alloc_block(arena); } size_t alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos; if (alloc_pos + size > arena->size) { @@ -637,7 +645,7 @@ void *arena_alloc(Arena *arena, size_t size, bool align) cur_blk->prev = fix_blk; return alloc + aligned_hdr_size; } else { - alloc_block(arena); // resets arena->pos + arena_alloc_block(arena); // resets arena->pos alloc_pos = align ? arena_align_offset(arena->pos) : arena->pos; } } @@ -647,17 +655,27 @@ void *arena_alloc(Arena *arena, size_t size, bool align) return mem; } +void free_block(void *block) +{ + if (arena_reuse_blk_count < REUSE_MAX) { + struct consumed_blk *reuse_blk = block; + reuse_blk->prev = arena_reuse_blk; + arena_reuse_blk = reuse_blk; + arena_reuse_blk_count++; + } else { + xfree(block); + } +} + void arena_mem_free(ArenaMem mem) { struct consumed_blk *b = mem; // peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE, // not a custom fix_blk - if (arena_reuse_blk_count < REUSE_MAX && b != NULL) { + if (b != NULL) { struct consumed_blk *reuse_blk = b; b = b->prev; - reuse_blk->prev = arena_reuse_blk; - arena_reuse_blk = reuse_blk; - arena_reuse_blk_count++; + free_block(reuse_blk); } while (b) { @@ -695,7 +713,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size) # include "nvim/grid.h" # include "nvim/mark.h" # include "nvim/msgpack_rpc/channel.h" -# include "nvim/msgpack_rpc/helpers.h" # include "nvim/ops.h" # include "nvim/option.h" # include "nvim/os/os.h" @@ -862,7 +879,6 @@ void free_all_mem(void) ui_comp_free_all_mem(); nlua_free_all_mem(); rpc_free_all_mem(); - msgpack_rpc_helpers_free_all_mem(); // should be last, in case earlier free functions deallocates arenas arena_free_reuse_blks(); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index e98a5dc228..0788670142 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -45,6 +45,8 @@ EXTERN size_t arena_alloc_count INIT( = 0); ((v).capacity = (s), \ (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) +#define ARENA_BLOCK_SIZE 4096 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 4ca2a61ab1..ab28eeca1c 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1478,11 +1478,11 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx) // Activate visual mode VIsual_active = true; VIsual_reselect = true; - check_cursor(); + check_cursor(curwin); VIsual = curwin->w_cursor; curwin->w_cursor = tpos; - check_cursor(); + check_cursor(curwin); // Adjust the cursor to make sure it is in the correct pos // for exclusive mode diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 506a428243..d82ba58918 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -771,7 +771,7 @@ popupexit: // move VIsual to the right column start_visual = curwin->w_cursor; // save the cursor pos curwin->w_cursor = end_visual; - coladvance(end_visual.col); + coladvance(curwin, end_visual.col); VIsual = curwin->w_cursor; curwin->w_cursor = start_visual; // restore the cursor } else { @@ -1430,7 +1430,7 @@ retnomove: break; } first = false; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { curwin->w_topfill++; } else { @@ -1460,7 +1460,7 @@ retnomove: if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { - if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline) + if (hasFolding(curwin, curwin->w_topline, NULL, &curwin->w_topline) && curwin->w_topline == curbuf->b_ml.ml_line_count) { break; } @@ -1515,7 +1515,7 @@ retnomove: curwin->w_curswant = col; curwin->w_set_curswant = false; // May still have been true - if (coladvance(col) == FAIL) { // Mouse click beyond end of line + if (coladvance(curwin, col) == FAIL) { // Mouse click beyond end of line if (inclusive != NULL) { *inclusive = true; } @@ -1548,7 +1548,7 @@ static bool do_mousescroll_horiz(colnr_T leftcol) // When the line of the cursor is too short, move the cursor to the // longest visible line. - if (!virtual_active() + if (!virtual_active(curwin) && leftcol > scroll_line_len(curwin->w_cursor.lnum)) { curwin->w_cursor.lnum = find_longest_lnum(); curwin->w_cursor.col = 0; @@ -1637,7 +1637,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) break; // Position is in this buffer line. } - hasFoldingWin(win, lnum, NULL, &lnum, true, NULL); + hasFolding(win, lnum, NULL, &lnum); if (lnum == win->w_buffer->b_ml.ml_line_count) { retval = true; diff --git a/src/nvim/move.c b/src/nvim/move.c index 551aa1bd4d..3c4da7f8ac 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -182,7 +182,7 @@ static void redraw_for_cursorcolumn(win_T *wp) // When current buffer's cursor moves in Visual mode, redraw it with UPD_INVERTED. if (VIsual_active && wp->w_buffer == curbuf) { - redraw_curbuf_later(UPD_INVERTED); + redraw_buf_later(curbuf, UPD_INVERTED); } } @@ -239,7 +239,7 @@ static void reset_skipcol(win_T *wp) redraw_later(wp, UPD_SOME_VALID); } -// Update curwin->w_topline to move the cursor onto the screen. +// Update wp->w_topline to move the cursor onto the screen. void update_topline(win_T *wp) { bool check_botline = false; @@ -332,7 +332,7 @@ void update_topline(win_T *wp) if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) { break; } - hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFolding(wp, lnum, NULL, &lnum); } } else { n = wp->w_topline + *so_ptr - wp->w_cursor.lnum; @@ -342,14 +342,14 @@ void update_topline(win_T *wp) // cursor in the middle of the window. Otherwise put the cursor // near the top of the window. if (n >= halfheight) { - scroll_cursor_halfway(false, false); + scroll_cursor_halfway(wp, false, false); } else { - scroll_cursor_top(scrolljump_value(), false); + scroll_cursor_top(wp, scrolljump_value(), false); check_botline = true; } } else { // Make sure topline is the first line of a fold. - hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); check_botline = true; } } @@ -377,7 +377,7 @@ void update_topline(win_T *wp) int n = wp->w_empty_rows; loff.lnum = wp->w_cursor.lnum; // In a fold go to its last line. - hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); + hasFolding(wp, loff.lnum, NULL, &loff.lnum); loff.fill = 0; n += wp->w_filler_rows; loff.height = 0; @@ -411,15 +411,15 @@ void update_topline(win_T *wp) if (lnum <= 0 || line_count > wp->w_height_inner + 1) { break; } - hasFolding(lnum, &lnum, NULL); + hasFolding(wp, lnum, &lnum, NULL); } } else { line_count = wp->w_cursor.lnum - wp->w_botline + 1 + (int)(*so_ptr); } if (line_count <= wp->w_height_inner + 1) { - scroll_cursor_bot(scrolljump_value(), false); + scroll_cursor_bot(wp, scrolljump_value(), false); } else { - scroll_cursor_halfway(false, false); + scroll_cursor_halfway(wp, false, false); } } } @@ -443,7 +443,7 @@ void update_topline(win_T *wp) // May need to set w_skipcol when cursor in w_topline. if (wp->w_cursor.lnum == wp->w_topline) { - validate_cursor(); + validate_cursor(wp); } } @@ -491,7 +491,7 @@ static bool check_top_offset(void) /// Update w_curswant. void update_curswant_force(void) { - validate_virtcol(); + validate_virtcol(curwin); curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = false; } @@ -536,12 +536,7 @@ void check_cursor_moved(win_T *wp) // Call this function when some window settings have changed, which require // the cursor position, botline and topline to be recomputed and the window to // be redrawn. E.g, when changing the 'wrap' option or folding. -void changed_window_setting(void) -{ - changed_window_setting_win(curwin); -} - -void changed_window_setting_win(win_T *wp) +void changed_window_setting(win_T *wp) { wp->w_lines_valid = 0; changed_line_abv_curs_win(wp); @@ -555,7 +550,7 @@ void set_topline(win_T *wp, linenr_T lnum) linenr_T prev_topline = wp->w_topline; // go to first of folded lines - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); // Approximate the value of w_botline wp->w_botline += lnum - wp->w_topline; wp->w_topline = lnum; @@ -595,7 +590,7 @@ void changed_line_abv_curs_win(win_T *wp) |VALID_CHEIGHT|VALID_TOPLINE); } -// Make sure the value of curwin->w_botline is valid. +// Make sure the value of wp->w_botline is valid. void validate_botline(win_T *wp) { if (!(wp->w_valid & VALID_BOTLINE)) { @@ -614,21 +609,21 @@ void approximate_botline_win(win_T *wp) wp->w_valid &= ~VALID_BOTLINE; } -// Return true if curwin->w_wrow and curwin->w_wcol are valid. -int cursor_valid(void) +// Return true if wp->w_wrow and wp->w_wcol are valid. +int cursor_valid(win_T *wp) { - check_cursor_moved(curwin); - return (curwin->w_valid & (VALID_WROW|VALID_WCOL)) == (VALID_WROW|VALID_WCOL); + check_cursor_moved(wp); + return (wp->w_valid & (VALID_WROW|VALID_WCOL)) == (VALID_WROW|VALID_WCOL); } // Validate cursor position. Makes sure w_wrow and w_wcol are valid. // w_topline must be valid, you may need to call update_topline() first! -void validate_cursor(void) +void validate_cursor(win_T *wp) { - check_cursor(); - check_cursor_moved(curwin); - if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { - curs_columns(curwin, true); + check_cursor(wp); + check_cursor_moved(wp); + if ((wp->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { + curs_columns(wp, true); } } @@ -692,26 +687,19 @@ static void curs_rows(win_T *wp) } else if (i > wp->w_lines_valid) { // a line that is too long to fit on the last screen line wp->w_cline_height = 0; - wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum, NULL, - NULL, true, NULL); + wp->w_cline_folded = hasFolding(wp, wp->w_cursor.lnum, NULL, NULL); } else { wp->w_cline_height = wp->w_lines[i].wl_size; wp->w_cline_folded = wp->w_lines[i].wl_folded; } } - redraw_for_cursorline(curwin); + redraw_for_cursorline(wp); wp->w_valid |= VALID_CROW|VALID_CHEIGHT; } -// Validate curwin->w_virtcol only. -void validate_virtcol(void) -{ - validate_virtcol_win(curwin); -} - // Validate wp->w_virtcol only. -void validate_virtcol_win(win_T *wp) +void validate_virtcol(win_T *wp) { check_cursor_moved(wp); @@ -724,49 +712,48 @@ void validate_virtcol_win(win_T *wp) wp->w_valid |= VALID_VIRTCOL; } -// Validate curwin->w_cline_height only. -void validate_cheight(void) +// Validate wp->w_cline_height only. +void validate_cheight(win_T *wp) { - check_cursor_moved(curwin); + check_cursor_moved(wp); - if (curwin->w_valid & VALID_CHEIGHT) { + if (wp->w_valid & VALID_CHEIGHT) { return; } - curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum, - NULL, &curwin->w_cline_folded, - true, true); - curwin->w_valid |= VALID_CHEIGHT; + wp->w_cline_height = plines_win_full(wp, wp->w_cursor.lnum, + NULL, &wp->w_cline_folded, + true, true); + wp->w_valid |= VALID_CHEIGHT; } // Validate w_wcol and w_virtcol only. -void validate_cursor_col(void) +void validate_cursor_col(win_T *wp) { - validate_virtcol(); + validate_virtcol(wp); - if (curwin->w_valid & VALID_WCOL) { + if (wp->w_valid & VALID_WCOL) { return; } - colnr_T col = curwin->w_virtcol; - colnr_T off = curwin_col_off(); + colnr_T col = wp->w_virtcol; + colnr_T off = win_col_off(wp); col += off; - int width = curwin->w_width_inner - off + curwin_col_off2(); + int width = wp->w_width_inner - off + win_col_off2(wp); - // long line wrapping, adjust curwin->w_wrow - if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner - && width > 0) { + // long line wrapping, adjust wp->w_wrow + if (wp->w_p_wrap && col >= (colnr_T)wp->w_width_inner && width > 0) { // use same formula as what is used in curs_columns() - col -= ((col - curwin->w_width_inner) / width + 1) * width; + col -= ((col - wp->w_width_inner) / width + 1) * width; } - if (col > (int)curwin->w_leftcol) { - col -= curwin->w_leftcol; + if (col > (int)wp->w_leftcol) { + col -= wp->w_leftcol; } else { col = 0; } - curwin->w_wcol = col; + wp->w_wcol = col; - curwin->w_valid |= VALID_WCOL; + wp->w_valid |= VALID_WCOL; } // Compute offset of a window, occupied by absolute or relative line number, @@ -779,11 +766,6 @@ int win_col_off(win_T *wp) + win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH); } -int curwin_col_off(void) -{ - return win_col_off(curwin); -} - // Return the difference in column offset for the second screen line of a // wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n' // is in 'cpoptions'. @@ -796,11 +778,6 @@ int win_col_off2(win_T *wp) return 0; } -int curwin_col_off2(void) -{ - return win_col_off2(curwin); -} - // Compute wp->w_wcol and wp->w_virtcol. // Also updates wp->w_wrow and wp->w_cline_row. // Also updates wp->w_leftcol. @@ -896,7 +873,7 @@ void curs_columns(win_T *wp, int may_scroll) // middle of window. int new_leftcol; if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left) { - new_leftcol = curwin->w_wcol - extra - width1 / 2; + new_leftcol = wp->w_wcol - extra - width1 / 2; } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); @@ -984,9 +961,9 @@ void curs_columns(win_T *wp, int may_scroll) n = plines - wp->w_height_inner + 1; } if (n > 0) { - curwin->w_skipcol = width1 + (n - 1) * width2; + wp->w_skipcol = width1 + (n - 1) * width2; } else { - curwin->w_skipcol = 0; + wp->w_skipcol = 0; } } else if (extra == 1) { // less than 'scrolloff' lines above, decrease skipcol @@ -1063,7 +1040,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, linenr_T lnum = pos->lnum; if (lnum >= wp->w_topline && lnum <= wp->w_botline) { - is_folded = hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + is_folded = hasFolding(wp, lnum, &lnum, NULL); row = plines_m_win(wp, wp->w_topline, lnum - 1, false); // "row" should be the screen line where line "lnum" begins, which can // be negative if "lnum" is "w_topline" and "w_skipcol" is non-zero. @@ -1207,128 +1184,128 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = virtcol2col(wp, lnum, screencol); } -/// Scroll the current window down by "line_count" logical lines. "CTRL-Y" +/// Scroll a window down by "line_count" logical lines. "CTRL-Y" /// /// @param line_count number of lines to scroll /// @param byfold if true, count a closed fold as one line -bool scrolldown(linenr_T line_count, int byfold) +bool scrolldown(win_T *wp, linenr_T line_count, int byfold) { int done = 0; // total # of physical lines done int width1 = 0; int width2 = 0; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (do_sms) { - width1 = curwin->w_width_inner - curwin_col_off(); - width2 = width1 + curwin_col_off2(); + width1 = wp->w_width_inner - win_col_off(wp); + width2 = width1 + win_col_off2(wp); } // Make sure w_topline is at the first of a sequence of folded lines. - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - validate_cursor(); // w_wrow needs to be valid + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); + validate_cursor(wp); // w_wrow needs to be valid for (int todo = line_count; todo > 0; todo--) { - if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) - && curwin->w_topfill < curwin->w_height_inner - 1) { - curwin->w_topfill++; + if (wp->w_topfill < win_get_fill(wp, wp->w_topline) + && wp->w_topfill < wp->w_height_inner - 1) { + wp->w_topfill++; done++; } else { // break when at the very top - if (curwin->w_topline == 1 && (!do_sms || curwin->w_skipcol < width1)) { + if (wp->w_topline == 1 && (!do_sms || wp->w_skipcol < width1)) { break; } - if (do_sms && curwin->w_skipcol >= width1) { + if (do_sms && wp->w_skipcol >= width1) { // scroll a screen line down - if (curwin->w_skipcol >= width1 + width2) { - curwin->w_skipcol -= width2; + if (wp->w_skipcol >= width1 + width2) { + wp->w_skipcol -= width2; } else { - curwin->w_skipcol -= width1; + wp->w_skipcol -= width1; } - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); done++; } else { // scroll a text line down - curwin->w_topline--; - curwin->w_skipcol = 0; - curwin->w_topfill = 0; + wp->w_topline--; + wp->w_skipcol = 0; + wp->w_topfill = 0; // A sequence of folded lines only counts for one logical line linenr_T first; - if (hasFolding(curwin->w_topline, &first, NULL)) { + if (hasFolding(wp, wp->w_topline, &first, NULL)) { done++; if (!byfold) { - todo -= curwin->w_topline - first - 1; + todo -= wp->w_topline - first - 1; } - curwin->w_botline -= curwin->w_topline - first; - curwin->w_topline = first; + wp->w_botline -= wp->w_topline - first; + wp->w_topline = first; } else { if (do_sms) { - int size = win_linetabsize(curwin, curwin->w_topline, - ml_get(curwin->w_topline), MAXCOL); + int size = win_linetabsize(wp, wp->w_topline, + ml_get(wp->w_topline), MAXCOL); if (size > width1) { - curwin->w_skipcol = width1; + wp->w_skipcol = width1; size -= width1; - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } while (size > width2) { - curwin->w_skipcol += width2; + wp->w_skipcol += width2; size -= width2; } done++; } else { - done += plines_win_nofill(curwin, curwin->w_topline, true); + done += plines_win_nofill(wp, wp->w_topline, true); } } } } - curwin->w_botline--; // approximate w_botline - invalidate_botline(curwin); + wp->w_botline--; // approximate w_botline + invalidate_botline(wp); } - curwin->w_wrow += done; // keep w_wrow updated - curwin->w_cline_row += done; // keep w_cline_row updated + wp->w_wrow += done; // keep w_wrow updated + wp->w_cline_row += done; // keep w_cline_row updated - if (curwin->w_cursor.lnum == curwin->w_topline) { - curwin->w_cline_row = 0; + if (wp->w_cursor.lnum == wp->w_topline) { + wp->w_cline_row = 0; } - check_topfill(curwin, true); + check_topfill(wp, true); // Compute the row number of the last row of the cursor line // and move the cursor onto the displayed part of the window. - int wrow = curwin->w_wrow; - if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_virtcol(); - validate_cheight(); - wrow += curwin->w_cline_height - 1 - - curwin->w_virtcol / curwin->w_width_inner; + int wrow = wp->w_wrow; + if (wp->w_p_wrap && wp->w_width_inner != 0) { + validate_virtcol(wp); + validate_cheight(wp); + wrow += wp->w_cline_height - 1 - + wp->w_virtcol / wp->w_width_inner; } bool moved = false; - while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) { + while (wrow >= wp->w_height_inner && wp->w_cursor.lnum > 1) { linenr_T first; - if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) { + if (hasFolding(wp, wp->w_cursor.lnum, &first, NULL)) { wrow--; if (first == 1) { - curwin->w_cursor.lnum = 1; + wp->w_cursor.lnum = 1; } else { - curwin->w_cursor.lnum = first - 1; + wp->w_cursor.lnum = first - 1; } } else { - wrow -= plines_win(curwin, curwin->w_cursor.lnum--, true); + wrow -= plines_win(wp, wp->w_cursor.lnum--, true); } - curwin->w_valid &= + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); moved = true; } if (moved) { // Move cursor to first line of closed fold. - foldAdjustCursor(); - coladvance(curwin->w_curswant); + foldAdjustCursor(wp); + coladvance(wp, wp->w_curswant); } - if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { - int so = get_scrolloff_value(curwin); + if (wp->w_cursor.lnum == wp->w_topline && do_sms) { + int so = get_scrolloff_value(wp); colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; // make sure the cursor is in the visible text - validate_virtcol(); - colnr_T col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; + validate_virtcol(wp); + colnr_T col = wp->w_virtcol - wp->w_skipcol + scrolloff_cols; int row = 0; if (col >= width1) { col -= width1; @@ -1337,32 +1314,32 @@ bool scrolldown(linenr_T line_count, int byfold) if (col > width2 && width2 > 0) { row += (int)col / width2; } - if (row >= curwin->w_height_inner) { - curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2; - coladvance(curwin->w_curswant); + if (row >= wp->w_height_inner) { + wp->w_curswant = wp->w_virtcol - (row - wp->w_height_inner + 1) * width2; + coladvance(wp, wp->w_curswant); } } return moved; } -/// Scroll the current window up by "line_count" logical lines. "CTRL-E" +/// Scroll a window up by "line_count" logical lines. "CTRL-E" /// /// @param line_count number of lines to scroll /// @param byfold if true, count a closed fold as one line -bool scrollup(linenr_T line_count, bool byfold) +bool scrollup(win_T *wp, linenr_T line_count, bool byfold) { - linenr_T topline = curwin->w_topline; - linenr_T botline = curwin->w_botline; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + linenr_T topline = wp->w_topline; + linenr_T botline = wp->w_botline; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; - if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { - int width1 = curwin->w_width_inner - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + if (do_sms || (byfold && hasAnyFolding(wp)) || win_may_fill(wp)) { + int width1 = wp->w_width_inner - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); int size = 0; - const colnr_T prev_skipcol = curwin->w_skipcol; + const colnr_T prev_skipcol = wp->w_skipcol; if (do_sms) { - size = linetabsize(curwin, curwin->w_topline); + size = linetabsize(wp, wp->w_topline); } // diff mode: first consume "topfill" @@ -1370,93 +1347,93 @@ bool scrollup(linenr_T line_count, bool byfold) // the line, then advance to the next line. // folding: count each sequence of folded lines as one logical line. for (int todo = line_count; todo > 0; todo--) { - if (curwin->w_topfill > 0) { - curwin->w_topfill--; + if (wp->w_topfill > 0) { + wp->w_topfill--; } else { - linenr_T lnum = curwin->w_topline; + linenr_T lnum = wp->w_topline; if (byfold) { // for a closed fold: go to the last line in the fold - hasFolding(lnum, NULL, &lnum); + hasFolding(wp, lnum, NULL, &lnum); } - if (lnum == curwin->w_topline && do_sms) { + if (lnum == wp->w_topline && do_sms) { // 'smoothscroll': increase "w_skipcol" until it goes over // the end of the line, then advance to the next line. - int add = curwin->w_skipcol > 0 ? width2 : width1; - curwin->w_skipcol += add; - if (curwin->w_skipcol >= size) { - if (lnum == curbuf->b_ml.ml_line_count) { + int add = wp->w_skipcol > 0 ? width2 : width1; + wp->w_skipcol += add; + if (wp->w_skipcol >= size) { + if (lnum == wp->w_buffer->b_ml.ml_line_count) { // at the last screen line, can't scroll further - curwin->w_skipcol -= add; + wp->w_skipcol -= add; break; } lnum++; } } else { - if (lnum >= curbuf->b_ml.ml_line_count) { + if (lnum >= wp->w_buffer->b_ml.ml_line_count) { break; } lnum++; } - if (lnum > curwin->w_topline) { + if (lnum > wp->w_topline) { // approximate w_botline - curwin->w_botline += lnum - curwin->w_topline; - curwin->w_topline = lnum; - curwin->w_topfill = win_get_fill(curwin, lnum); - curwin->w_skipcol = 0; + wp->w_botline += lnum - wp->w_topline; + wp->w_topline = lnum; + wp->w_topfill = win_get_fill(wp, lnum); + wp->w_skipcol = 0; if (todo > 1 && do_sms) { - size = linetabsize(curwin, curwin->w_topline); + size = linetabsize(wp, wp->w_topline); } } } } - if (prev_skipcol > 0 || curwin->w_skipcol > 0) { + if (prev_skipcol > 0 || wp->w_skipcol > 0) { // need to redraw more, because wl_size of the (new) topline may // now be invalid - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } } else { - curwin->w_topline += line_count; - curwin->w_botline += line_count; // approximate w_botline + wp->w_topline += line_count; + wp->w_botline += line_count; // approximate w_botline } - if (curwin->w_topline > curbuf->b_ml.ml_line_count) { - curwin->w_topline = curbuf->b_ml.ml_line_count; + if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count) { + wp->w_topline = wp->w_buffer->b_ml.ml_line_count; } - if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1) { - curwin->w_botline = curbuf->b_ml.ml_line_count + 1; + if (wp->w_botline > wp->w_buffer->b_ml.ml_line_count + 1) { + wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1; } - check_topfill(curwin, false); + check_topfill(wp, false); - if (hasAnyFolding(curwin)) { + if (hasAnyFolding(wp)) { // Make sure w_topline is at the first of a sequence of folded lines. - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); } - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - if (curwin->w_cursor.lnum < curwin->w_topline) { - curwin->w_cursor.lnum = curwin->w_topline; - curwin->w_valid &= + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); + if (wp->w_cursor.lnum < wp->w_topline) { + wp->w_cursor.lnum = wp->w_topline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); - coladvance(curwin->w_curswant); + coladvance(wp, wp->w_curswant); } - if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { - int col_off = curwin_col_off(); - int col_off2 = curwin_col_off2(); + if (wp->w_cursor.lnum == wp->w_topline && do_sms && wp->w_skipcol > 0) { + int col_off = win_col_off(wp); + int col_off2 = win_col_off2(wp); - int width1 = curwin->w_width_inner - col_off; + int width1 = wp->w_width_inner - col_off; int width2 = width1 + col_off2; int extra2 = col_off - col_off2; - int so = get_scrolloff_value(curwin); + int so = get_scrolloff_value(wp); colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; - int space_cols = (curwin->w_height_inner - 1) * width2; + int space_cols = (wp->w_height_inner - 1) * width2; // If we have non-zero scrolloff, just ignore the marker as we are // going past it anyway. - int overlap = scrolloff_cols != 0 ? 0 : sms_marker_overlap(curwin, extra2); + int overlap = scrolloff_cols != 0 ? 0 : sms_marker_overlap(wp, extra2); // Make sure the cursor is in a visible part of the line, taking // 'scrolloff' into account, but using screen lines. @@ -1464,26 +1441,26 @@ bool scrollup(linenr_T line_count, bool byfold) if (scrolloff_cols > space_cols / 2) { scrolloff_cols = space_cols / 2; } - validate_virtcol(); - if (curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { - colnr_T col = curwin->w_virtcol; + validate_virtcol(wp); + if (wp->w_virtcol < wp->w_skipcol + overlap + scrolloff_cols) { + colnr_T col = wp->w_virtcol; if (col < width1) { col += width1; } - while (col < curwin->w_skipcol + overlap + scrolloff_cols) { + while (col < wp->w_skipcol + overlap + scrolloff_cols) { col += width2; } - curwin->w_curswant = col; - coladvance(curwin->w_curswant); + wp->w_curswant = col; + coladvance(wp, wp->w_curswant); // validate_virtcol() marked various things as valid, but after // moving the cursor they need to be recomputed - curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); } } - bool moved = topline != curwin->w_topline || botline != curwin->w_botline; + bool moved = topline != wp->w_topline || botline != wp->w_botline; return moved; } @@ -1496,16 +1473,16 @@ void adjust_skipcol(void) return; } - int width1 = curwin->w_width_inner - curwin_col_off(); + int width1 = curwin->w_width_inner - win_col_off(curwin); if (width1 <= 0) { return; // no text will be displayed } - int width2 = width1 + curwin_col_off2(); + int width2 = width1 + win_col_off2(curwin); int so = get_scrolloff_value(curwin); colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; bool scrolled = false; - validate_cheight(); + validate_cheight(curwin); if (curwin->w_cline_height == curwin->w_height_inner // w_cline_height may be capped at w_height_inner, check there aren't // actually more lines. @@ -1515,8 +1492,8 @@ void adjust_skipcol(void) return; } - validate_virtcol(); - int overlap = sms_marker_overlap(curwin, curwin_col_off() - curwin_col_off2()); + validate_virtcol(curwin); + int overlap = sms_marker_overlap(curwin, win_col_off(curwin) - win_col_off2(curwin)); while (curwin->w_skipcol > 0 && curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { // scroll a screen line down @@ -1528,7 +1505,7 @@ void adjust_skipcol(void) scrolled = true; } if (scrolled) { - validate_virtcol(); + validate_virtcol(curwin); redraw_later(curwin, UPD_NOT_VALID); return; // don't scroll in the other direction now } @@ -1572,7 +1549,7 @@ void check_topfill(win_T *wp, bool down) } } } - win_check_anchored_floats(curwin); + win_check_anchored_floats(wp); } // Use as many filler lines as possible for w_topline. Make sure w_topline @@ -1601,7 +1578,7 @@ void scrolldown_clamp(void) return; } - validate_cursor(); // w_wrow needs to be valid + validate_cursor(curwin); // w_wrow needs to be valid // Compute the row number of the last row of the cursor line // and make sure it doesn't go off the screen. Make sure the cursor @@ -1613,8 +1590,8 @@ void scrolldown_clamp(void) end_row += plines_win_nofill(curwin, curwin->w_topline - 1, true); } if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_cheight(); - validate_virtcol(); + validate_cheight(curwin); + validate_virtcol(curwin); end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / curwin->w_width_inner; } @@ -1626,7 +1603,7 @@ void scrolldown_clamp(void) curwin->w_topline--; curwin->w_topfill = 0; } - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); curwin->w_botline--; // approximate w_botline curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); } @@ -1641,7 +1618,7 @@ void scrollup_clamp(void) return; } - validate_cursor(); // w_wrow needs to be valid + validate_cursor(curwin); // w_wrow needs to be valid // Compute the row number of the first row of the cursor line // and make sure it doesn't go off the screen. Make sure the cursor @@ -1650,14 +1627,14 @@ void scrollup_clamp(void) - plines_win_nofill(curwin, curwin->w_topline, true) - curwin->w_topfill); if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_virtcol(); + validate_virtcol(curwin); start_row -= curwin->w_virtcol / curwin->w_width_inner; } if (start_row >= get_scrolloff_value(curwin)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { - hasFolding(curwin->w_topline, NULL, &curwin->w_topline); + hasFolding(curwin, curwin->w_topline, NULL, &curwin->w_topline); curwin->w_topline++; } curwin->w_botline++; // approximate w_botline @@ -1681,7 +1658,7 @@ static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight) lp->fill = 0; if (lp->lnum < 1) { lp->height = MAXCOL; - } else if (hasFolding(lp->lnum, &lp->lnum, NULL)) { + } else if (hasFolding(wp, lp->lnum, &lp->lnum, NULL)) { // Add a closed fold lp->height = 1; } else { @@ -1711,7 +1688,7 @@ static void botline_forw(win_T *wp, lineoff_T *lp) assert(wp->w_buffer != 0); if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) { lp->height = MAXCOL; - } else if (hasFoldingWin(wp, lp->lnum, NULL, &lp->lnum, true, NULL)) { + } else if (hasFolding(wp, lp->lnum, NULL, &lp->lnum)) { // Add a closed fold lp->height = 1; } else { @@ -1745,12 +1722,12 @@ static void topline_botline(lineoff_T *lp) // Recompute topline to put the cursor at the top of the window. // Scroll at least "min_scroll" lines. // If "always" is true, always set topline (for "zt"). -void scroll_cursor_top(int min_scroll, int always) +void scroll_cursor_top(win_T *wp, int min_scroll, int always) { - linenr_T old_topline = curwin->w_topline; - int old_skipcol = curwin->w_skipcol; - linenr_T old_topfill = curwin->w_topfill; - int off = get_scrolloff_value(curwin); + linenr_T old_topline = wp->w_topline; + int old_skipcol = wp->w_skipcol; + linenr_T old_topfill = wp->w_topfill; + int off = get_scrolloff_value(wp); if (mouse_dragging > 0) { off = mouse_dragging - 1; @@ -1761,54 +1738,54 @@ void scroll_cursor_top(int min_scroll, int always) // - (part of) the cursor line is moved off the screen or // - moved at least 'scrolljump' lines and // - at least 'scrolloff' lines above and below the cursor - validate_cheight(); + validate_cheight(wp); int scrolled = 0; - int used = curwin->w_cline_height; // includes filler lines above - if (curwin->w_cursor.lnum < curwin->w_topline) { + int used = wp->w_cline_height; // includes filler lines above + if (wp->w_cursor.lnum < wp->w_topline) { scrolled = used; } linenr_T top; // just above displayed lines linenr_T bot; // just below displayed lines - if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { + if (hasFolding(wp, wp->w_cursor.lnum, &top, &bot)) { top--; bot++; } else { - top = curwin->w_cursor.lnum - 1; - bot = curwin->w_cursor.lnum + 1; + top = wp->w_cursor.lnum - 1; + bot = wp->w_cursor.lnum + 1; } linenr_T new_topline = top + 1; // "used" already contains the number of filler lines above, don't add it // again. // Hide filler lines above cursor line by adding them to "extra". - int extra = win_get_fill(curwin, curwin->w_cursor.lnum); + int extra = win_get_fill(wp, wp->w_cursor.lnum); // Check if the lines from "top" to "bot" fit in the window. If they do, // set new_topline and advance "top" and "bot" to include more lines. while (top > 0) { - int i = hasFolding(top, &top, NULL) + int i = hasFolding(wp, top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines - : plines_win_nofill(curwin, top, true); - if (top < curwin->w_topline) { + : plines_win_nofill(wp, top, true); + if (top < wp->w_topline) { scrolled += i; } // If scrolling is needed, scroll at least 'sj' lines. - if ((new_topline >= curwin->w_topline || scrolled > min_scroll) && extra >= off) { + if ((new_topline >= wp->w_topline || scrolled > min_scroll) && extra >= off) { break; } used += i; - if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { - if (hasFolding(bot, NULL, &bot)) { + if (extra + i <= off && bot < wp->w_buffer->b_ml.ml_line_count) { + if (hasFolding(wp, bot, NULL, &bot)) { // count one logical line for a sequence of folded lines used++; } else { - used += plines_win(curwin, bot, true); + used += plines_win(wp, bot, true); } } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } @@ -1821,43 +1798,43 @@ void scroll_cursor_top(int min_scroll, int always) // If we don't have enough space, put cursor in the middle. // This makes sure we get the same position when using "k" and "j" // in a small window. - if (used > curwin->w_height_inner) { - scroll_cursor_halfway(false, false); + if (used > wp->w_height_inner) { + scroll_cursor_halfway(wp, false, false); } else { // If "always" is false, only adjust topline to a lower value, higher // value may happen with wrapping lines. - if (new_topline < curwin->w_topline || always) { - curwin->w_topline = new_topline; + if (new_topline < wp->w_topline || always) { + wp->w_topline = new_topline; } - if (curwin->w_topline > curwin->w_cursor.lnum) { - curwin->w_topline = curwin->w_cursor.lnum; + if (wp->w_topline > wp->w_cursor.lnum) { + wp->w_topline = wp->w_cursor.lnum; } - curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); - if (curwin->w_topfill > 0 && extra > off) { - curwin->w_topfill -= extra - off; - if (curwin->w_topfill < 0) { - curwin->w_topfill = 0; + wp->w_topfill = win_get_fill(wp, wp->w_topline); + if (wp->w_topfill > 0 && extra > off) { + wp->w_topfill -= extra - off; + if (wp->w_topfill < 0) { + wp->w_topfill = 0; } } - check_topfill(curwin, false); - if (curwin->w_topline != old_topline) { - reset_skipcol(curwin); - } else if (curwin->w_topline == curwin->w_cursor.lnum) { - validate_virtcol(); - if (curwin->w_skipcol >= curwin->w_virtcol) { + check_topfill(wp, false); + if (wp->w_topline != old_topline) { + reset_skipcol(wp); + } else if (wp->w_topline == wp->w_cursor.lnum) { + validate_virtcol(wp); + if (wp->w_skipcol >= wp->w_virtcol) { // TODO(vim): if the line doesn't fit may optimize w_skipcol instead // of making it zero - reset_skipcol(curwin); + reset_skipcol(wp); } } - if (curwin->w_topline != old_topline - || curwin->w_skipcol != old_skipcol - || curwin->w_topfill != old_topfill) { - curwin->w_valid &= + if (wp->w_topline != old_topline + || wp->w_skipcol != old_skipcol + || wp->w_topfill != old_topfill) { + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; } } @@ -1886,79 +1863,79 @@ void set_empty_rows(win_T *wp, int used) /// When scrolling scroll at least "min_scroll" lines. /// If "set_topbot" is true, set topline and botline first (for "zb"). /// This is messy stuff!!! -void scroll_cursor_bot(int min_scroll, bool set_topbot) +void scroll_cursor_bot(win_T *wp, int min_scroll, bool set_topbot) { lineoff_T loff; - linenr_T old_topline = curwin->w_topline; - int old_skipcol = curwin->w_skipcol; - int old_topfill = curwin->w_topfill; - linenr_T old_botline = curwin->w_botline; - int old_valid = curwin->w_valid; - int old_empty_rows = curwin->w_empty_rows; - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + linenr_T old_topline = wp->w_topline; + int old_skipcol = wp->w_skipcol; + int old_topfill = wp->w_topfill; + linenr_T old_botline = wp->w_botline; + int old_valid = wp->w_valid; + int old_empty_rows = wp->w_empty_rows; + linenr_T cln = wp->w_cursor.lnum; // Cursor Line Number + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (set_topbot) { bool set_skipcol = false; int used = 0; - curwin->w_botline = cln + 1; + wp->w_botline = cln + 1; loff.fill = 0; - for (curwin->w_topline = curwin->w_botline; - curwin->w_topline > 1; - curwin->w_topline = loff.lnum) { - loff.lnum = curwin->w_topline; - topline_back_winheight(curwin, &loff, false); + for (wp->w_topline = wp->w_botline; + wp->w_topline > 1; + wp->w_topline = loff.lnum) { + loff.lnum = wp->w_topline; + topline_back_winheight(wp, &loff, false); if (loff.height == MAXCOL) { break; } - if (used + loff.height > curwin->w_height_inner) { + if (used + loff.height > wp->w_height_inner) { if (do_sms) { // 'smoothscroll' and 'wrap' are set. The above line is // too long to show in its entirety, so we show just a part // of it. - if (used < curwin->w_height_inner) { - int plines_offset = used + loff.height - curwin->w_height_inner; - used = curwin->w_height_inner; - curwin->w_topfill = loff.fill; - curwin->w_topline = loff.lnum; - curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset); + if (used < wp->w_height_inner) { + int plines_offset = used + loff.height - wp->w_height_inner; + used = wp->w_height_inner; + wp->w_topfill = loff.fill; + wp->w_topline = loff.lnum; + wp->w_skipcol = skipcol_from_plines(wp, plines_offset); set_skipcol = true; } } break; } used += loff.height; - curwin->w_topfill = loff.fill; + wp->w_topfill = loff.fill; } - set_empty_rows(curwin, used); - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill + set_empty_rows(wp, used); + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + if (wp->w_topline != old_topline + || wp->w_topfill != old_topfill || set_skipcol - || curwin->w_skipcol != 0) { - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); + || wp->w_skipcol != 0) { + wp->w_valid &= ~(VALID_WROW|VALID_CROW); if (set_skipcol) { - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } else { - reset_skipcol(curwin); + reset_skipcol(wp); } } } else { - validate_botline(curwin); + validate_botline(wp); } // The lines of the cursor line itself are always used. - int used = plines_win_nofill(curwin, cln, true); + int used = plines_win_nofill(wp, cln, true); int scrolled = 0; // If the cursor is on or below botline, we will at least scroll by the // height of the cursor line, which is "used". Correct for empty lines, // which are really part of botline. - if (cln >= curwin->w_botline) { + if (cln >= wp->w_botline) { scrolled = used; - if (cln == curwin->w_botline) { - scrolled -= curwin->w_empty_rows; + if (cln == wp->w_botline) { + scrolled -= wp->w_empty_rows; } if (do_sms) { // 'smoothscroll' and 'wrap' are set. @@ -1966,21 +1943,21 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) // occupies. If it is occupying more than the entire window, we // need to scroll the additional clipped lines to scroll past the // top line before we can move on to the other lines. - int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); + int top_plines = plines_win_nofill(wp, wp->w_topline, false); int skip_lines = 0; - int width1 = curwin->w_width_inner - curwin_col_off(); + int width1 = wp->w_width_inner - win_col_off(wp); if (width1 > 0) { - int width2 = width1 + curwin_col_off2(); + int width2 = width1 + win_col_off2(wp); // similar formula is used in curs_columns() - if (curwin->w_skipcol > width1) { - skip_lines += (curwin->w_skipcol - width1) / width2 + 1; - } else if (curwin->w_skipcol > 0) { + if (wp->w_skipcol > width1) { + skip_lines += (wp->w_skipcol - width1) / width2 + 1; + } else if (wp->w_skipcol > 0) { skip_lines = 1; } top_plines -= skip_lines; - if (top_plines > curwin->w_height_inner) { - scrolled += (top_plines - curwin->w_height_inner); + if (top_plines > wp->w_height_inner) { + scrolled += (top_plines - wp->w_height_inner); } } } @@ -1992,67 +1969,67 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) // - scrolled nothing or at least 'sj' lines // - at least 'so' lines below the cursor // - lines between botline and cursor have been counted - if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum)) { + if (!hasFolding(wp, wp->w_cursor.lnum, &loff.lnum, &boff.lnum)) { loff.lnum = cln; boff.lnum = cln; } loff.fill = 0; boff.fill = 0; - int fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; + int fill_below_window = win_get_fill(wp, wp->w_botline) - wp->w_filler_rows; int extra = 0; - int so = get_scrolloff_value(curwin); + int so = get_scrolloff_value(wp); while (loff.lnum > 1) { // Stop when scrolled nothing or at least "min_scroll", found "extra" // context for 'scrolloff' and counted all lines below the window. if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= (mouse_dragging > 0 ? mouse_dragging - 1 : so)) - || boff.lnum + 1 > curbuf->b_ml.ml_line_count) - && loff.lnum <= curwin->w_botline - && (loff.lnum < curwin->w_botline + || boff.lnum + 1 > wp->w_buffer->b_ml.ml_line_count) + && loff.lnum <= wp->w_botline + && (loff.lnum < wp->w_botline || loff.fill >= fill_below_window)) { break; } // Add one line above - topline_back(curwin, &loff); + topline_back(wp, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { used += loff.height; } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } - if (loff.lnum >= curwin->w_botline - && (loff.lnum > curwin->w_botline + if (loff.lnum >= wp->w_botline + && (loff.lnum > wp->w_botline || loff.fill <= fill_below_window)) { // Count screen lines that are below the window. scrolled += loff.height; - if (loff.lnum == curwin->w_botline + if (loff.lnum == wp->w_botline && loff.fill == 0) { - scrolled -= curwin->w_empty_rows; + scrolled -= wp->w_empty_rows; } } - if (boff.lnum < curbuf->b_ml.ml_line_count) { + if (boff.lnum < wp->w_buffer->b_ml.ml_line_count) { // Add one line below - botline_forw(curwin, &boff); + botline_forw(wp, &boff); used += boff.height; - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } if (extra < (mouse_dragging > 0 ? mouse_dragging - 1 : so) || scrolled < min_scroll) { extra += boff.height; - if (boff.lnum >= curwin->w_botline - || (boff.lnum + 1 == curwin->w_botline - && boff.fill > curwin->w_filler_rows)) { + if (boff.lnum >= wp->w_botline + || (boff.lnum + 1 == wp->w_botline + && boff.fill > wp->w_filler_rows)) { // Count screen lines that are below the window. scrolled += boff.height; - if (boff.lnum == curwin->w_botline + if (boff.lnum == wp->w_botline && boff.fill == 0) { - scrolled -= curwin->w_empty_rows; + scrolled -= wp->w_empty_rows; } } } @@ -2060,77 +2037,77 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) } linenr_T line_count; - // curwin->w_empty_rows is larger, no need to scroll + // wp->w_empty_rows is larger, no need to scroll if (scrolled <= 0) { line_count = 0; // more than a screenfull, don't scroll but redraw - } else if (used > curwin->w_height_inner) { + } else if (used > wp->w_height_inner) { line_count = used; // scroll minimal number of lines } else { line_count = 0; - boff.fill = curwin->w_topfill; - boff.lnum = curwin->w_topline - 1; + boff.fill = wp->w_topfill; + boff.lnum = wp->w_topline - 1; int i; - for (i = 0; i < scrolled && boff.lnum < curwin->w_botline;) { - botline_forw(curwin, &boff); + for (i = 0; i < scrolled && boff.lnum < wp->w_botline;) { + botline_forw(wp, &boff); i += boff.height; line_count++; } - if (i < scrolled) { // below curwin->w_botline, don't scroll + if (i < scrolled) { // below wp->w_botline, don't scroll line_count = 9999; } } // Scroll up if the cursor is off the bottom of the screen a bit. // Otherwise put it at 1/2 of the screen. - if (line_count >= curwin->w_height_inner && line_count > min_scroll) { - scroll_cursor_halfway(false, true); + if (line_count >= wp->w_height_inner && line_count > min_scroll) { + scroll_cursor_halfway(wp, false, true); } else if (line_count > 0) { if (do_sms) { - scrollup(scrolled, true); // TODO(vim): + scrollup(wp, scrolled, true); // TODO(vim): } else { - scrollup(line_count, true); + scrollup(wp, line_count, true); } } // If topline didn't change we need to restore w_botline and w_empty_rows // (we changed them). // If topline did change, update_screen() will set botline. - if (curwin->w_topline == old_topline && curwin->w_skipcol == old_skipcol && set_topbot) { - curwin->w_botline = old_botline; - curwin->w_empty_rows = old_empty_rows; - curwin->w_valid = old_valid; + if (wp->w_topline == old_topline && wp->w_skipcol == old_skipcol && set_topbot) { + wp->w_botline = old_botline; + wp->w_empty_rows = old_empty_rows; + wp->w_valid = old_valid; } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; } /// Recompute topline to put the cursor halfway across the window /// /// @param atend if true, also put the cursor halfway to the end of the file. /// -void scroll_cursor_halfway(bool atend, bool prefer_above) +void scroll_cursor_halfway(win_T *wp, bool atend, bool prefer_above) { - linenr_T old_topline = curwin->w_topline; - lineoff_T loff = { .lnum = curwin->w_cursor.lnum }; - lineoff_T boff = { .lnum = curwin->w_cursor.lnum }; - hasFolding(loff.lnum, &loff.lnum, &boff.lnum); - int used = plines_win_nofill(curwin, loff.lnum, true); + linenr_T old_topline = wp->w_topline; + lineoff_T loff = { .lnum = wp->w_cursor.lnum }; + lineoff_T boff = { .lnum = wp->w_cursor.lnum }; + hasFolding(wp, loff.lnum, &loff.lnum, &boff.lnum); + int used = plines_win_nofill(wp, loff.lnum, true); loff.fill = 0; boff.fill = 0; linenr_T topline = loff.lnum; colnr_T skipcol = 0; int want_height; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (do_sms) { // 'smoothscroll' and 'wrap' are set if (atend) { - want_height = (curwin->w_height_inner - used) / 2; + want_height = (wp->w_height_inner - used) / 2; used = 0; } else { - want_height = curwin->w_height_inner; + want_height = wp->w_height_inner; } } @@ -2139,20 +2116,20 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) // If using smoothscroll, we can precisely scroll to the // exact point where the cursor is halfway down the screen. if (do_sms) { - topline_back_winheight(curwin, &loff, false); + topline_back_winheight(wp, &loff, false); if (loff.height == MAXCOL) { break; } used += loff.height; - if (!atend && boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(curwin, &boff); + if (!atend && boff.lnum < wp->w_buffer->b_ml.ml_line_count) { + botline_forw(wp, &boff); used += boff.height; } if (used > want_height) { if (used - loff.height < want_height) { topline = loff.lnum; topfill = loff.fill; - skipcol = skipcol_from_plines(curwin, used - want_height); + skipcol = skipcol_from_plines(wp, used - want_height); } break; } @@ -2176,10 +2153,10 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) ? (round == 2 && below < above) : (round == 1 && below <= above)) { // add a line below the cursor - if (boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(curwin, &boff); + if (boff.lnum < wp->w_buffer->b_ml.ml_line_count) { + botline_forw(wp, &boff); used += boff.height; - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { done = true; break; } @@ -2196,13 +2173,13 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) ? (round == 1 && below >= above) : (round == 1 && below > above)) { // add a line above the cursor - topline_back(curwin, &loff); + topline_back(wp, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { used += loff.height; } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { done = true; break; } @@ -2216,51 +2193,51 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) } } - if (!hasFolding(topline, &curwin->w_topline, NULL) - && (curwin->w_topline != topline || skipcol != 0 || curwin->w_skipcol != 0)) { - curwin->w_topline = topline; + if (!hasFolding(wp, topline, &wp->w_topline, NULL) + && (wp->w_topline != topline || skipcol != 0 || wp->w_skipcol != 0)) { + wp->w_topline = topline; if (skipcol != 0) { - curwin->w_skipcol = skipcol; - redraw_later(curwin, UPD_NOT_VALID); + wp->w_skipcol = skipcol; + redraw_later(wp, UPD_NOT_VALID); } else if (do_sms) { - reset_skipcol(curwin); + reset_skipcol(wp); } } - curwin->w_topfill = topfill; - if (old_topline > curwin->w_topline + curwin->w_height_inner) { - curwin->w_botfill = false; + wp->w_topfill = topfill; + if (old_topline > wp->w_topline + wp->w_height_inner) { + wp->w_botfill = false; } - check_topfill(curwin, false); - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - curwin->w_valid |= VALID_TOPLINE; + check_topfill(wp, false); + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); + wp->w_valid |= VALID_TOPLINE; } // Correct the cursor position so that it is in a part of the screen at least // 'so' lines from the top and bottom, if possible. // If not possible, put it at the same position as scroll_cursor_halfway(). // When called topline must be valid! -void cursor_correct(void) +void cursor_correct(win_T *wp) { // How many lines we would like to have above/below the cursor depends on // whether the first/last line of the file is on screen. - int above_wanted = get_scrolloff_value(curwin); - int below_wanted = get_scrolloff_value(curwin); + int above_wanted = get_scrolloff_value(wp); + int below_wanted = get_scrolloff_value(wp); if (mouse_dragging > 0) { above_wanted = mouse_dragging - 1; below_wanted = mouse_dragging - 1; } - if (curwin->w_topline == 1) { + if (wp->w_topline == 1) { above_wanted = 0; - int max_off = curwin->w_height_inner / 2; + int max_off = wp->w_height_inner / 2; if (below_wanted > max_off) { below_wanted = max_off; } } - validate_botline(curwin); - if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 + validate_botline(wp); + if (wp->w_botline == wp->w_buffer->b_ml.ml_line_count + 1 && mouse_dragging == 0) { below_wanted = 0; - int max_off = (curwin->w_height_inner - 1) / 2; + int max_off = (wp->w_height_inner - 1) / 2; if (above_wanted > max_off) { above_wanted = max_off; } @@ -2268,18 +2245,18 @@ void cursor_correct(void) // If there are sufficient file-lines above and below the cursor, we can // return now. - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - if (cln >= curwin->w_topline + above_wanted - && cln < curwin->w_botline - below_wanted - && !hasAnyFolding(curwin)) { + linenr_T cln = wp->w_cursor.lnum; // Cursor Line Number + if (cln >= wp->w_topline + above_wanted + && cln < wp->w_botline - below_wanted + && !hasAnyFolding(wp)) { return; } - if (curwin->w_p_sms && !curwin->w_p_wrap) { + if (wp->w_p_sms && !wp->w_p_wrap) { // 'smoothscroll' is active - if (curwin->w_cline_height == curwin->w_height_inner) { + if (wp->w_cline_height == wp->w_height_inner) { // The cursor line just fits in the window, don't scroll. - reset_skipcol(curwin); + reset_skipcol(wp); return; } // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol. @@ -2289,52 +2266,52 @@ void cursor_correct(void) // the top and the bottom until: // - the desired context lines are found // - the lines from the top is past the lines from the bottom - linenr_T topline = curwin->w_topline; - linenr_T botline = curwin->w_botline - 1; + linenr_T topline = wp->w_topline; + linenr_T botline = wp->w_botline - 1; // count filler lines as context - int above = curwin->w_topfill; // screen lines above topline - int below = curwin->w_filler_rows; // screen lines below botline + int above = wp->w_topfill; // screen lines above topline + int below = wp->w_filler_rows; // screen lines below botline while ((above < above_wanted || below < below_wanted) && topline < botline) { if (below < below_wanted && (below <= above || above >= above_wanted)) { - if (hasFolding(botline, &botline, NULL)) { + if (hasFolding(wp, botline, &botline, NULL)) { below++; } else { - below += plines_win(curwin, botline, true); + below += plines_win(wp, botline, true); } botline--; } if (above < above_wanted && (above < below || below >= below_wanted)) { - if (hasFolding(topline, NULL, &topline)) { + if (hasFolding(wp, topline, NULL, &topline)) { above++; } else { - above += plines_win_nofill(curwin, topline, true); + above += plines_win_nofill(wp, topline, true); } // Count filler lines below this line as context. if (topline < botline) { - above += win_get_fill(curwin, topline + 1); + above += win_get_fill(wp, topline + 1); } topline++; } } if (topline == botline || botline == 0) { - curwin->w_cursor.lnum = topline; + wp->w_cursor.lnum = topline; } else if (topline > botline) { - curwin->w_cursor.lnum = botline; + wp->w_cursor.lnum = botline; } else { - if (cln < topline && curwin->w_topline > 1) { - curwin->w_cursor.lnum = topline; - curwin->w_valid &= + if (cln < topline && wp->w_topline > 1) { + wp->w_cursor.lnum = topline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); } - if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = botline; - curwin->w_valid &= + if (cln > botline && wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { + wp->w_cursor.lnum = botline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); } } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; } /// Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD) @@ -2461,7 +2438,7 @@ int onepage(Direction dir, int count) botline_forw(curwin, &loff); botline_topline(&loff); // We're at the wrong end of a fold now. - hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); + hasFolding(curwin, loff.lnum, &loff.lnum, NULL); // Always scroll at least one line. Avoid getting stuck on // very long lines. @@ -2491,9 +2468,9 @@ int onepage(Direction dir, int count) } } } - foldAdjustCursor(); - cursor_correct(); - check_cursor_col(); + foldAdjustCursor(curwin); + cursor_correct(curwin); + check_cursor_col(curwin); if (retval == OK) { beginline(BL_SOL | BL_FIX); } @@ -2504,14 +2481,14 @@ int onepage(Direction dir, int count) // But make sure we scroll at least one line (happens with mix of long // wrapping lines and non-wrapping line). if (check_top_offset()) { - scroll_cursor_top(1, false); + scroll_cursor_top(curwin, 1, false); if (curwin->w_topline <= old_topline && old_topline < curbuf->b_ml.ml_line_count) { curwin->w_topline = old_topline + 1; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); } } else if (curwin->w_botline > curbuf->b_ml.ml_line_count) { - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); } } @@ -2610,7 +2587,7 @@ void halfpage(bool flag, linenr_T Prenum) if (n < 0 && scrolled > 0) { break; } - hasFolding(curwin->w_topline, NULL, &curwin->w_topline); + hasFolding(curwin, curwin->w_topline, NULL, &curwin->w_topline); curwin->w_topline++; curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); @@ -2634,7 +2611,7 @@ void halfpage(bool flag, linenr_T Prenum) if (i > room) { break; } - hasFolding(curwin->w_botline, NULL, &curwin->w_botline); + hasFolding(curwin, curwin->w_botline, NULL, &curwin->w_botline); curwin->w_botline++; room -= i; } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); @@ -2646,7 +2623,7 @@ void halfpage(bool flag, linenr_T Prenum) if (hasAnyFolding(curwin)) { while (--n >= 0 && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - hasFolding(curwin->w_cursor.lnum, NULL, + hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum); curwin->w_cursor.lnum++; } @@ -2669,7 +2646,7 @@ void halfpage(bool flag, linenr_T Prenum) break; } curwin->w_topline--; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); curwin->w_topfill = 0; } curwin->w_valid &= ~(VALID_CROW|VALID_WROW| @@ -2688,7 +2665,7 @@ void halfpage(bool flag, linenr_T Prenum) } else if (hasAnyFolding(curwin)) { while (--n >= 0 && curwin->w_cursor.lnum > 1) { curwin->w_cursor.lnum--; - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); } } else { @@ -2697,9 +2674,9 @@ void halfpage(bool flag, linenr_T Prenum) } } // Move cursor to first line of closed fold. - foldAdjustCursor(); + foldAdjustCursor(curwin); check_topfill(curwin, !flag); - cursor_correct(); + cursor_correct(curwin); beginline(BL_SOL | BL_FIX); redraw_later(curwin, UPD_VALID); } @@ -2748,12 +2725,12 @@ void do_check_cursorbind(void) { int restart_edit_save = restart_edit; restart_edit = true; - check_cursor(); + check_cursor(curwin); // Avoid a scroll here for the cursor position, 'scrollbind' is // more important. if (!curwin->w_p_scb) { - validate_cursor(); + validate_cursor(curwin); } restart_edit = restart_edit_save; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index ee7359c476..a8fde5a652 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -1,7 +1,6 @@ #include <assert.h> #include <inttypes.h> #include <msgpack/object.h> -#include <msgpack/pack.h> #include <msgpack/sbuffer.h> #include <msgpack/unpack.h> #include <stdbool.h> @@ -29,7 +28,7 @@ #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/os/input.h" #include "nvim/rbuffer.h" @@ -44,73 +43,31 @@ # define NOT "[notify] " # define ERR "[error] " -// Cannot define array with negative offsets, so this one is needed to be added -// to MSGPACK_UNPACK_\* values. -# define MUR_OFF 2 +# define SEND "->" +# define RECV "<-" -static const char *const msgpack_error_messages[] = { - [MSGPACK_UNPACK_EXTRA_BYTES + MUR_OFF] = "extra bytes found", - [MSGPACK_UNPACK_CONTINUE + MUR_OFF] = "incomplete string", - [MSGPACK_UNPACK_PARSE_ERROR + MUR_OFF] = "parse error", - [MSGPACK_UNPACK_NOMEM_ERROR + MUR_OFF] = "not enough memory", -}; - -static void log_close(FILE *f) +static void log_request(char *dir, uint64_t channel_id, uint32_t req_id, const char *name) { - fputc('\n', f); - fflush(f); - fclose(f); - log_unlock(); + DLOGN("RPC %s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id, REQ, req_id, name); } -static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) +static void log_response(char *dir, uint64_t channel_id, char *kind, uint32_t req_id) { - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - DLOGN("RPC ->ch %" PRIu64 ": ", channel_id); - const msgpack_unpack_return result = - msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); - switch (result) { - case MSGPACK_UNPACK_SUCCESS: { - uint64_t type = unpacked.data.via.array.ptr[0].via.u64; - log_lock(); - FILE *f = open_log_file(); - fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); - msgpack_object_print(f, unpacked.data); - log_close(f); - msgpack_unpacked_destroy(&unpacked); - break; - } - case MSGPACK_UNPACK_EXTRA_BYTES: - case MSGPACK_UNPACK_CONTINUE: - case MSGPACK_UNPACK_PARSE_ERROR: - case MSGPACK_UNPACK_NOMEM_ERROR: { - log_lock(); - FILE *f = open_log_file(); - fprintf(f, ERR); - fprintf(f, "%s", msgpack_error_messages[result + MUR_OFF]); - log_close(f); - break; - } - } + DLOGN("RPC %s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind, req_id); } -static void log_client_msg(uint64_t channel_id, bool is_request, const char *name) +static void log_notify(char *dir, uint64_t channel_id, const char *name) { - DLOGN("RPC <-ch %" PRIu64 ": ", channel_id); - log_lock(); - FILE *f = open_log_file(); - fprintf(f, "%s: %s", is_request ? REQ : RES, name); - log_close(f); + DLOGN("RPC %s %" PRIu64 ": %s %s\n", dir, channel_id, NOT, name); } #else -# define log_client_msg(...) -# define log_server_msg(...) +# define log_request(...) +# define log_response(...) +# define log_notify(...) #endif static Set(cstr_t) event_strings = SET_INIT; -static msgpack_sbuffer out_buffer; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/channel.c.generated.h" @@ -119,7 +76,6 @@ static msgpack_sbuffer out_buffer; void rpc_init(void) { ch_before_blocking_events = multiqueue_new_child(main_loop.events); - msgpack_sbuffer_init(&out_buffer); } void rpc_start(Channel *channel) @@ -169,8 +125,9 @@ bool rpc_send_event(uint64_t id, const char *name, Array args) return false; } + log_notify(SEND, channel ? channel->id : 0, name); if (channel) { - send_event(channel, name, args); + serialize_request(&channel, 1, 0, name, args); } else { broadcast_event(name, args); } @@ -199,7 +156,9 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem RpcState *rpc = &channel->rpc; uint32_t request_id = rpc->next_request_id++; // Send the msgpack-rpc request - send_request(channel, request_id, method_name, args); + serialize_request(&channel, 1, request_id, method_name, args); + + log_request(SEND, channel->id, request_id, method_name); // Push the frame ChannelCallFrame frame = { request_id, false, false, NIL, NULL }; @@ -361,8 +320,13 @@ static void parse_msgpack(Channel *channel) frame->result = p->result; } frame->result_mem = arena_finish(&p->arena); + log_response(RECV, channel->id, frame->errored ? ERR : RES, p->request_id); } else { - log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name); + if (p->type == kMessageTypeNotification) { + log_notify(RECV, channel->id, p->handler.name); + } else { + log_request(RECV, channel->id, p->request_id, p->handler.name); + } Object res = p->result; if (p->result.type != kObjectTypeArray) { @@ -442,15 +406,7 @@ static void request_event(void **argv) Object result = handler.fn(channel->id, e->args, &e->used_mem, &error); if (e->type == kMessageTypeRequest || ERROR_SET(&error)) { // Send the response. - msgpack_packer response; - msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write); - channel_write(channel, serialize_response(channel->id, - e->handler, - e->type, - e->request_id, - &error, - &result, - &out_buffer)); + serialize_response(channel, e->handler, e->type, e->request_id, &error, &result); } if (handler.ret_alloc) { api_free_object(result); @@ -533,41 +489,14 @@ static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageT { Error e = ERROR_INIT; api_set_error(&e, kErrorTypeException, "%s", err); - channel_write(chan, serialize_response(chan->id, - handler, - type, - id, - &e, - &NIL, - &out_buffer)); + serialize_response(chan, handler, type, id, &e, &NIL); api_clear_error(&e); } -static void send_request(Channel *channel, uint32_t id, const char *name, Array args) -{ - const String method = cstr_as_string(name); - channel_write(channel, serialize_request(channel->id, - id, - method, - args, - &out_buffer, - 1)); -} - -static void send_event(Channel *channel, const char *name, Array args) -{ - const String method = cstr_as_string(name); - channel_write(channel, serialize_request(channel->id, - 0, - method, - args, - &out_buffer, - 1)); -} - static void broadcast_event(const char *name, Array args) { - kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; + kvec_withinit_t(Channel *, 4) subscribed = KV_INITIAL_VALUE; + kvi_init(subscribed); Channel *channel; map_foreach_value(&channels, channel, { @@ -577,25 +506,11 @@ static void broadcast_event(const char *name, Array args) } }); - if (!kv_size(subscribed)) { - goto end; - } - - const String method = cstr_as_string(name); - WBuffer *buffer = serialize_request(0, - 0, - method, - args, - &out_buffer, - kv_size(subscribed)); - - for (size_t i = 0; i < kv_size(subscribed); i++) { - Channel *c = kv_A(subscribed, i); - channel_write(c, buffer); + if (kv_size(subscribed)) { + serialize_request(subscribed.items, kv_size(subscribed), 0, name, args); } -end: - kv_destroy(subscribed); + kvi_destroy(subscribed); } static void unsubscribe(Channel *channel, char *event) @@ -653,27 +568,28 @@ static void chan_close_with_error(Channel *channel, char *msg, int loglevel) channel_close(channel->id, kChannelPartRpc, NULL); } -static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, const String method, - Array args, msgpack_sbuffer *sbuffer, size_t refcount) +static void serialize_request(Channel **chans, size_t nchans, uint32_t request_id, + const char *method, Array args) { - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_rpc_serialize_request(request_id, method, args, &pac); - log_server_msg(channel_id, sbuffer); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - refcount, - xfree); - msgpack_sbuffer_clear(sbuffer); - return rv; + PackerBuffer packer; + packer_buffer_init_channels(chans, nchans, &packer); + + mpack_array(&packer.ptr, request_id ? 4 : 3); + mpack_w(&packer.ptr, request_id ? 0 : 2); + + if (request_id) { + mpack_uint(&packer.ptr, request_id); + } + + mpack_str(cstr_as_string(method), &packer); + mpack_object_array(args, &packer); + + packer_buffer_finish_channels(&packer); } -static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler, - MessageType type, uint32_t response_id, Error *err, Object *arg, - msgpack_sbuffer *sbuffer) +void serialize_response(Channel *channel, MsgpackRpcRequestHandler handler, MessageType type, + uint32_t response_id, Error *err, Object *arg) { - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); if (ERROR_SET(err) && type == kMessageTypeNotification) { if (handler.fn == handle_nvim_paste) { // TODO(bfredl): this is pretty much ad-hoc. maybe TUI and UI:s should be @@ -685,19 +601,65 @@ static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, INTEGER_OBJ(err->type)); ADD_C(args, CSTR_AS_OBJ(err->msg)); - msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), - args, &pac); + serialize_request(&channel, 1, 0, "nvim_error_event", args); } + return; + } + + PackerBuffer packer; + packer_buffer_init_channels(&channel, 1, &packer); + + mpack_array(&packer.ptr, 4); + mpack_w(&packer.ptr, 1); + mpack_uint(&packer.ptr, response_id); + + if (ERROR_SET(err)) { + // error represented by a [type, message] array + mpack_array(&packer.ptr, 2); + mpack_integer(&packer.ptr, err->type); + mpack_str(cstr_as_string(err->msg), &packer); + // Nil result + mpack_nil(&packer.ptr); } else { - msgpack_rpc_serialize_response(response_id, err, arg, &pac); + // Nil error + mpack_nil(&packer.ptr); + // Return value + mpack_object(arg, &packer); } - log_server_msg(channel_id, sbuffer); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - 1, // responses only go though 1 channel - xfree); - msgpack_sbuffer_clear(sbuffer); - return rv; + + packer_buffer_finish_channels(&packer); + + log_response(SEND, channel->id, ERROR_SET(err) ? ERR : RES, response_id); +} + +static void packer_buffer_init_channels(Channel **chans, size_t nchans, PackerBuffer *packer) +{ + packer->startptr = alloc_block(); + packer->ptr = packer->startptr; + packer->endptr = packer->startptr + ARENA_BLOCK_SIZE; + packer->packer_flush = channel_flush_callback; + packer->anydata = chans; + packer->anylen = nchans; +} + +static void packer_buffer_finish_channels(PackerBuffer *packer) +{ + size_t len = (size_t)(packer->ptr - packer->startptr); + if (len > 0) { + WBuffer *buf = wstream_new_buffer(packer->startptr, len, packer->anylen, free_block); + Channel **chans = packer->anydata; + for (size_t i = 0; i < packer->anylen; i++) { + channel_write(chans[i], buf); + } + } else { + free_block(packer->startptr); + } +} + +static void channel_flush_callback(PackerBuffer *packer) +{ + packer_buffer_finish_channels(packer); + packer_buffer_init_channels(packer->anydata, packer->anylen, packer); } void rpc_set_client_info(uint64_t id, Dictionary info) @@ -762,7 +724,6 @@ void rpc_free_all_mem(void) }); set_destroy(cstr_t, &event_strings); - msgpack_sbuffer_destroy(&out_buffer); multiqueue_free(ch_before_blocking_events); } #endif diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c deleted file mode 100644 index bc1eefb270..0000000000 --- a/src/nvim/msgpack_rpc/helpers.c +++ /dev/null @@ -1,245 +0,0 @@ -#include <msgpack/object.h> -#include <msgpack/sbuffer.h> -#include <msgpack/unpack.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "klib/kvec.h" -#include "msgpack/pack.h" -#include "nvim/api/private/helpers.h" -#include "nvim/assert_defs.h" -#include "nvim/lua/executor.h" -#include "nvim/memory.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/types_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/helpers.c.generated.h" -#endif - -static msgpack_sbuffer sbuffer; - -void msgpack_rpc_helpers_init(void) -{ - msgpack_sbuffer_init(&sbuffer); -} - -#ifdef EXITFREE -void msgpack_rpc_helpers_free_all_mem(void) -{ - msgpack_sbuffer_destroy(&sbuffer); -} -#endif - -// uncrustify:off - -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - if (result) { - msgpack_pack_true(res); - } else { - msgpack_pack_false(res); - } -} - -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_int64(res, result); -} - -void msgpack_rpc_from_float(Float result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_double(res, result); -} - -void msgpack_rpc_from_string(const String result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_str(res, result.size); - if (result.size > 0) { - msgpack_pack_str_body(res, result.data, result.size); - } -} - -static void msgpack_rpc_from_handle(ObjectType type, Integer o, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(3) -{ - msgpack_packer pac; - msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); - msgpack_pack_int64(&pac, (handle_T)o); - msgpack_pack_ext(res, sbuffer.size, (int8_t)(type - EXT_OBJECT_TYPE_SHIFT)); - msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); - msgpack_sbuffer_clear(&sbuffer); -} - -typedef struct { - Object *aobj; - bool container; - size_t idx; -} APIToMPObjectStackItem; - -/// Convert type used by Nvim API to msgpack type. -/// -/// consumes (frees) any luaref inside `result`, even though they are not used -/// (just represented as NIL) -/// -/// @param[in] result Object to convert. -/// @param[out] res Structure that defines where conversion results are saved. -/// -/// @return true in case of success, false otherwise. -void msgpack_rpc_from_object(Object *result, msgpack_packer *const res) - FUNC_ATTR_NONNULL_ARG(2) -{ - kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE; - kvi_init(stack); - kvi_push(stack, ((APIToMPObjectStackItem) { result, false, 0 })); - while (kv_size(stack)) { - APIToMPObjectStackItem cur = kv_last(stack); - STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 - && kObjectTypeTabpage == kObjectTypeWindow + 1, - "Buffer, window and tabpage enum items are in order"); - switch (cur.aobj->type) { - case kObjectTypeLuaRef: - // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef - // should only appear when the caller has opted in to handle references, - // see nlua_pop_Object. - api_free_luaref(cur.aobj->data.luaref); - cur.aobj->data.luaref = LUA_NOREF; - FALLTHROUGH; - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(cur.aobj->data.boolean, res); - break; - case kObjectTypeInteger: - msgpack_rpc_from_integer(cur.aobj->data.integer, res); - break; - case kObjectTypeFloat: - msgpack_rpc_from_float(cur.aobj->data.floating, res); - break; - case kObjectTypeString: - msgpack_rpc_from_string(cur.aobj->data.string, res); - break; - case kObjectTypeBuffer: - case kObjectTypeWindow: - case kObjectTypeTabpage: - msgpack_rpc_from_handle(cur.aobj->type, cur.aobj->data.integer, res); - break; - case kObjectTypeArray: { - const size_t size = cur.aobj->data.array.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.array.items[idx], - .container = false, - })); - } - } else { - msgpack_pack_array(res, size); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - case kObjectTypeDictionary: { - const size_t size = cur.aobj->data.dictionary.size; - if (cur.container) { - if (cur.idx >= size) { - (void)kv_pop(stack); - } else { - const size_t idx = cur.idx; - cur.idx++; - kv_last(stack) = cur; - msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res); - kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.dictionary.items[idx].value, - .container = false, - })); - } - } else { - msgpack_pack_map(res, size); - cur.container = true; - kv_last(stack) = cur; - } - break; - } - } - if (!cur.container) { - (void)kv_pop(stack); - } - } - kvi_destroy(stack); -} - -// uncrustify:on - -void msgpack_rpc_from_array(Array result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(&result.items[i], res); - } -} - -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) -{ - msgpack_pack_map(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(&result.items[i].value, res); - } -} - -/// Serializes a msgpack-rpc request or notification(id == 0) -void msgpack_rpc_serialize_request(uint32_t request_id, const String method, Array args, - msgpack_packer *pac) - FUNC_ATTR_NONNULL_ARG(4) -{ - msgpack_pack_array(pac, request_id ? 4 : 3); - msgpack_pack_int(pac, request_id ? 0 : 2); - - if (request_id) { - msgpack_pack_uint32(pac, request_id); - } - - msgpack_rpc_from_string(method, pac); - msgpack_rpc_from_array(args, pac); -} - -/// Serializes a msgpack-rpc response -void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object *arg, - msgpack_packer *pac) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_pack_array(pac, 4); - msgpack_pack_int(pac, 1); - msgpack_pack_uint32(pac, response_id); - - if (ERROR_SET(err)) { - // error represented by a [type, message] array - msgpack_pack_array(pac, 2); - msgpack_rpc_from_integer(err->type, pac); - msgpack_rpc_from_string(cstr_as_string(err->msg), pac); - // Nil result - msgpack_pack_nil(pac); - } else { - // Nil error - msgpack_pack_nil(pac); - // Return value - msgpack_rpc_from_object(arg, pac); - } -} diff --git a/src/nvim/msgpack_rpc/helpers.h b/src/nvim/msgpack_rpc/helpers.h deleted file mode 100644 index 6344d8c567..0000000000 --- a/src/nvim/msgpack_rpc/helpers.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include <msgpack.h> // IWYU pragma: keep - -#include "nvim/api/private/defs.h" - -/// Value by which objects represented as EXT type are shifted -/// -/// Subtracted when packing, added when unpacking. Used to allow moving -/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be -/// split or reordered. -#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer -#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/helpers.h.generated.h" -#endif diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c new file mode 100644 index 0000000000..cac68f76f0 --- /dev/null +++ b/src/nvim/msgpack_rpc/packer.c @@ -0,0 +1,235 @@ +#include <assert.h> + +#include "nvim/api/private/defs.h" +#include "nvim/lua/executor.h" +#include "nvim/msgpack_rpc/packer.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/packer.c.generated.h" +#endif + +static void check_buffer(PackerBuffer *packer) +{ + ptrdiff_t remaining = packer->endptr - packer->ptr; + if (remaining < MPACK_ITEM_SIZE) { + packer->packer_flush(packer); + } +} + +static void mpack_w8(char **b, const char *data) +{ +#ifdef ORDER_BIG_ENDIAN + memcpy(*b, data, 8); + *b += 8; +#else + for (int i = 7; i >= 0; i--) { + *(*b)++ = data[i]; + } +#endif +} + +void mpack_integer(char **ptr, Integer i) +{ + if (i >= 0) { + if (i > 0xfffffff) { + mpack_w(ptr, 0xcf); + mpack_w8(ptr, (char *)&i); + } else { + mpack_uint(ptr, (uint32_t)i); + } + } else { + if (i < -0x80000000LL) { + mpack_w(ptr, 0xd3); + mpack_w8(ptr, (char *)&i); + } else if (i < -0x8000) { + mpack_w(ptr, 0xd2); + mpack_w4(ptr, (uint32_t)i); + } else if (i < -0x80) { + mpack_w(ptr, 0xd1); + mpack_w2(ptr, (uint32_t)i); + } else if (i < -0x20) { + mpack_w(ptr, 0xd0); + mpack_w(ptr, (char)i); + } else { + mpack_w(ptr, (char)i); + } + } +} + +void mpack_float8(char **ptr, double i) +{ + mpack_w(ptr, 0xcb); + mpack_w8(ptr, (char *)&i); +} + +void mpack_str(String str, PackerBuffer *packer) +{ + const size_t len = str.size; + if (len < 20) { + mpack_w(&packer->ptr, 0xa0 | len); + } else if (len < 0xff) { + mpack_w(&packer->ptr, 0xd9); + mpack_w(&packer->ptr, len); + } else if (len < 0xffff) { + mpack_w(&packer->ptr, 0xda); + mpack_w2(&packer->ptr, (uint32_t)len); + } else if (len < 0xffffffff) { + mpack_w(&packer->ptr, 0xdb); + mpack_w4(&packer->ptr, (uint32_t)len); + } else { + abort(); + } + + size_t pos = 0; + while (pos < len) { + ptrdiff_t remaining = packer->endptr - packer->ptr; + size_t to_copy = MIN(len - pos, (size_t)remaining); + memcpy(packer->ptr, str.data + pos, to_copy); + packer->ptr += to_copy; + pos += to_copy; + + if (pos < len) { + packer->packer_flush(packer); + } + } +} + +void mpack_handle(ObjectType type, handle_T handle, PackerBuffer *packer) +{ + char exttype = (char)(type - EXT_OBJECT_TYPE_SHIFT); + if (-0x1f <= handle && handle <= 0x7f) { + mpack_w(&packer->ptr, 0xd4); + mpack_w(&packer->ptr, exttype); + mpack_w(&packer->ptr, (char)handle); + } else { + // we want to encode some small negative sentinel like -1. This is handled above + assert(handle >= 0); + // FAIL: we cannot use fixext 4/8 due to a design error + // (in theory fixext 2 for handle<=0xff but we don't gain much from it) + char buf[MPACK_ITEM_SIZE]; + char *pos = buf; + mpack_uint(&pos, (uint32_t)handle); + ptrdiff_t packsize = pos - buf; + 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; + } +} + +void mpack_object(Object *obj, PackerBuffer *packer) +{ + mpack_object_inner(obj, NULL, 0, packer); +} + +void mpack_object_array(Array arr, PackerBuffer *packer) +{ + mpack_array(&packer->ptr, (uint32_t)arr.size); + if (arr.size > 0) { + Object container = ARRAY_OBJ(arr); + mpack_object_inner(&arr.items[0], arr.size > 1 ? &container : NULL, 1, packer); + } +} + +typedef struct { + Object *container; + size_t idx; +} ContainerStackItem; + +void mpack_object_inner(Object *current, Object *container, size_t container_idx, + PackerBuffer *packer) + FUNC_ATTR_NONNULL_ARG(1, 4) +{ + // The inner loop of this function packs "current" and then fetches the next + // value from "container". "stack" is only used for nested containers. + kvec_withinit_t(ContainerStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + + while (true) { + check_buffer(packer); + switch (current->type) { + case kObjectTypeLuaRef: + // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef + // should only appear when the caller has opted in to handle references, + // see nlua_pop_Object. + api_free_luaref(current->data.luaref); + current->data.luaref = LUA_NOREF; + FALLTHROUGH; + case kObjectTypeNil: + mpack_nil(&packer->ptr); + break; + case kObjectTypeBoolean: + mpack_bool(&packer->ptr, current->data.boolean); + break; + case kObjectTypeInteger: + mpack_integer(&packer->ptr, current->data.integer); + break; + case kObjectTypeFloat: + mpack_float8(&packer->ptr, current->data.floating); + break; + case kObjectTypeString: + mpack_str(current->data.string, packer); + break; + case kObjectTypeBuffer: + case kObjectTypeWindow: + case kObjectTypeTabpage: + mpack_handle(current->type, (handle_T)current->data.integer, packer); + break; + case kObjectTypeDictionary: + case kObjectTypeArray: {} + size_t current_size; + if (current->type == kObjectTypeArray) { + current_size = current->data.array.size; + mpack_array(&packer->ptr, (uint32_t)current_size); + } else { + current_size = current->data.dictionary.size; + mpack_map(&packer->ptr, (uint32_t)current_size); + } + if (current_size > 0) { + if (current->type == kObjectTypeArray && current_size == 1) { + current = ¤t->data.array.items[0]; + continue; + } + if (container) { + kvi_push(stack, ((ContainerStackItem) { + .container = container, + .idx = container_idx, + })); + } + container = current; + container_idx = 0; + } + break; + } + + if (!container) { + if (kv_size(stack)) { + ContainerStackItem it = kv_pop(stack); + container = it.container; + container_idx = it.idx; + } else { + break; + } + } + + if (container->type == kObjectTypeArray) { + Array arr = container->data.array; + current = &arr.items[container_idx++]; + if (container_idx >= arr.size) { + container = NULL; + } + } else { + Dictionary dict = container->data.dictionary; + KeyValuePair *it = &dict.items[container_idx++]; + check_buffer(packer); + mpack_str(it->key, packer); + current = &it->value; + if (container_idx >= dict.size) { + container = NULL; + } + } + } + kvi_destroy(stack); +} diff --git a/src/nvim/msgpack_rpc/packer.h b/src/nvim/msgpack_rpc/packer.h new file mode 100644 index 0000000000..8117bd09bd --- /dev/null +++ b/src/nvim/msgpack_rpc/packer.h @@ -0,0 +1,76 @@ +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "nvim/api/private/defs.h" +#include "nvim/msgpack_rpc/packer_defs.h" + +#define mpack_w(b, byte) *(*(b))++ = (char)(byte); +static inline void mpack_w2(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static inline void mpack_w4(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static inline void mpack_uint(char **buf, uint32_t val) +{ + if (val > 0xffff) { + mpack_w(buf, 0xce); + mpack_w4(buf, val); + } else if (val > 0xff) { + mpack_w(buf, 0xcd); + mpack_w2(buf, val); + } else if (val > 0x7f) { + mpack_w(buf, 0xcc); + mpack_w(buf, val); + } else { + mpack_w(buf, val); + } +} + +#define mpack_nil(buf) mpack_w(buf, 0xc0) +static inline void mpack_bool(char **buf, bool val) +{ + mpack_w(buf, 0xc2 | (val ? 1 : 0)); +} + +static inline void mpack_array(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x90 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xdc); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdd); + mpack_w4(buf, len); + } +} + +static inline void mpack_map(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x80 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xde); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdf); + mpack_w4(buf, len); + } +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/packer.h.generated.h" +#endif diff --git a/src/nvim/msgpack_rpc/packer_defs.h b/src/nvim/msgpack_rpc/packer_defs.h new file mode 100644 index 0000000000..420f3dc424 --- /dev/null +++ b/src/nvim/msgpack_rpc/packer_defs.h @@ -0,0 +1,24 @@ +#pragma once + +#include <stddef.h> +#include <stdint.h> + +// Max possible length: bytecode + 8 int/float bytes +// Ext objects are maximum 8=3+5 (nested uint32 payload) +#define MPACK_ITEM_SIZE 9 + +typedef struct packer_buffer_t PackerBuffer; + +// Must ensure at least MPACK_ITEM_SIZE of space. +typedef void (*PackerBufferFlush)(PackerBuffer *self); + +struct packer_buffer_t { + char *startptr; + char *ptr; + char *endptr; + + // these are free to be used by packer_flush for any purpose, if want + void *anydata; + size_t anylen; + PackerBufferFlush packer_flush; +}; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 6bc138f65a..dbb30b0c9a 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -10,7 +10,6 @@ #include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" #include "nvim/ui_client.h" diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 8ff47097fa..3603a054b6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1009,12 +1009,12 @@ normal_end: mb_check_adjust_col(curwin); // #6203 if (curwin->w_p_scb && s->toplevel) { - validate_cursor(); // may need to update w_leftcol + validate_cursor(curwin); // may need to update w_leftcol do_check_scrollbind(true); } if (curwin->w_p_crb && s->toplevel) { - validate_cursor(); // may need to update w_leftcol + validate_cursor(curwin); // may need to update w_leftcol do_check_cursorbind(); } @@ -1343,7 +1343,7 @@ static void normal_redraw(NormalState *s) // Before redrawing, make sure w_topline is correct, and w_leftcol // if lines don't wrap, and w_skipcol if lines wrap. update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); show_cursor_info_later(false); @@ -1420,7 +1420,7 @@ static int normal_check(VimState *state) // Ensure curwin->w_topline and curwin->w_leftcol are up to date // before triggering a WinScrolled autocommand. update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); normal_check_cursor_moved(s); normal_check_text_changed(s); @@ -1515,7 +1515,7 @@ void end_visual_mode(void) curbuf->b_visual.vi_end = curwin->w_cursor; curbuf->b_visual.vi_curswant = curwin->w_curswant; curbuf->b_visual_mode_eval = VIsual_mode; - if (!virtual_active()) { + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } @@ -1863,8 +1863,8 @@ void clear_showcmd(void) bot = VIsual.lnum; } // Include closed folds as a whole. - hasFolding(top, &top, NULL); - hasFolding(bot, NULL, &bot); + hasFolding(curwin, top, &top, NULL); + hasFolding(curwin, bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -2174,14 +2174,14 @@ void check_scrollbind(linenr_T topline_diff, int leftcol_diff) y = topline - curwin->w_topline; if (y > 0) { - scrollup(y, false); + scrollup(curwin, y, false); } else { - scrolldown(-y, false); + scrolldown(curwin, -y, false); } } redraw_later(curwin, UPD_VALID); - cursor_correct(); + cursor_correct(curwin); curwin->w_redr_status = true; } @@ -2466,8 +2466,8 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); - col_off1 = curwin_col_off(); - col_off2 = col_off1 - curwin_col_off2(); + col_off1 = win_col_off(curwin); + col_off2 = col_off1 - win_col_off2(curwin); width1 = curwin->w_width_inner - col_off1; width2 = curwin->w_width_inner - col_off2; @@ -2481,7 +2481,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) // try to stick in the last column of the screen. if (curwin->w_curswant == MAXCOL) { atend = true; - validate_virtcol(); + validate_virtcol(curwin); if (width1 <= 0) { curwin->w_curswant = 0; } else { @@ -2506,7 +2506,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) while (dist--) { if (dir == BACKWARD) { if (curwin->w_curswant >= width1 - && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { // Move back within the line. This can give a negative value // for w_curswant if width1 < width2 (with cpoptions+=n), // which will get clipped to column 0. @@ -2533,7 +2533,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) n = width1; } if (curwin->w_curswant + width2 < (colnr_T)n - && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { // move forward within line curwin->w_curswant += width2; } else { @@ -2558,17 +2558,17 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) } } - if (virtual_active() && atend) { - coladvance(MAXCOL); + if (virtual_active(curwin) && atend) { + coladvance(curwin, MAXCOL); } else { - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) { // Check for landing on a character that got split at the end of the // last line. We want to advance a screenline, not end up in the same // screenline or move two screenlines. - validate_virtcol(); + validate_virtcol(curwin); colnr_T virtcol = curwin->w_virtcol; if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) { virtcol -= vim_strsize(get_showbreak_value(curwin)); @@ -2616,13 +2616,13 @@ void scroll_redraw(bool up, linenr_T count) linenr_T prev_lnum = curwin->w_cursor.lnum; bool moved = up - ? scrollup(count, true) - : scrolldown(count, true); + ? scrollup(curwin, count, true) + : scrolldown(curwin, count, true); if (get_scrolloff_value(curwin) > 0) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. - cursor_correct(); + cursor_correct(curwin); check_cursor_moved(curwin); curwin->w_valid |= VALID_TOPLINE; @@ -2651,7 +2651,7 @@ void scroll_redraw(bool up, linenr_T count) } } if (curwin->w_cursor.lnum != prev_lnum) { - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } if (moved) { curwin->w_viewport_invalid = true; @@ -2803,7 +2803,7 @@ static void nv_zet(cmdarg_T *cap) } else { curwin->w_cursor.lnum = cap->count0; } - check_cursor_col(); + check_cursor_col(curwin); } switch (nchar) { @@ -2826,7 +2826,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 't': - scroll_cursor_top(0, true); + scroll_cursor_top(curwin, 0, true); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2837,7 +2837,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 'z': - scroll_cursor_halfway(true, false); + scroll_cursor_halfway(curwin, true, false); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2847,7 +2847,7 @@ static void nv_zet(cmdarg_T *cap) // when <count> is at bottom of window, and puts that one at // bottom of window. if (cap->count0 != 0) { - scroll_cursor_bot(0, true); + scroll_cursor_bot(curwin, 0, true); curwin->w_cursor.lnum = curwin->w_topline; } else if (curwin->w_topline == 1) { curwin->w_cursor.lnum = 1; @@ -2860,7 +2860,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 'b': - scroll_cursor_bot(0, true); + scroll_cursor_bot(curwin, 0, true); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2895,7 +2895,7 @@ static void nv_zet(cmdarg_T *cap) // "zs" - scroll screen, cursor at the start case 's': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { col = 0; // like the cursor is in col 0 } else { getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL); @@ -2915,12 +2915,12 @@ static void nv_zet(cmdarg_T *cap) // "ze" - scroll screen, cursor at the end case 'e': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { col = 0; // like the cursor is in col 0 } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); } - int n = curwin->w_width_inner - curwin_col_off(); + int n = curwin->w_width_inner - win_col_off(curwin); if (col + siso < n) { col = 0; } else { @@ -2980,7 +2980,7 @@ static void nv_zet(cmdarg_T *cap) case 'E': if (foldmethodIsManual(curwin)) { clearFolding(curwin); - changed_window_setting(); + changed_window_setting(curwin); } else if (foldmethodIsMarker(curwin)) { deleteFold(curwin, 1, curbuf->b_ml.ml_line_count, true, false); } else { @@ -3005,7 +3005,7 @@ static void nv_zet(cmdarg_T *cap) // "za": open closed fold or close open fold at cursor case 'a': - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { openFold(curwin->w_cursor, cap->count1); } else { closeFold(curwin->w_cursor, cap->count1); @@ -3015,7 +3015,7 @@ static void nv_zet(cmdarg_T *cap) // "zA": open fold at cursor recursively case 'A': - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { openFoldRecurse(curwin->w_cursor); } else { closeFoldRecurse(curwin->w_cursor); @@ -3151,11 +3151,11 @@ static void nv_zet(cmdarg_T *cap) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { wp->w_p_fen = curwin->w_p_fen; - changed_window_setting_win(wp); + changed_window_setting(wp); } } } - changed_window_setting(); + changed_window_setting(curwin); } // Redraw when 'foldlevel' changed. @@ -3223,8 +3223,7 @@ static void nv_colon(cmdarg_T *cap) clearop(cap->oap); } else if (cap->oap->op_type != OP_NOP && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count - || cap->oap->start.col > - (colnr_T)strlen(ml_get(cap->oap->start.lnum)) + || cap->oap->start.col > ml_get_len(cap->oap->start.lnum) || did_emsg)) { // The start of the operator has become invalid by the Ex command. clearopbeep(cap->oap); @@ -3592,7 +3591,7 @@ bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp) } if (VIsual_mode == 'V') { *pp = get_cursor_line_ptr(); - *lenp = strlen(*pp); + *lenp = (size_t)get_cursor_line_len(); } else { if (lt(curwin->w_cursor, VIsual)) { *pp = ml_get_pos(&curwin->w_cursor); @@ -3640,7 +3639,7 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; n--) { - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); if (curwin->w_cursor.lnum > curwin->w_topline) { curwin->w_cursor.lnum--; @@ -3669,7 +3668,7 @@ static void nv_scroll(cmdarg_T *cap) if (used >= half) { break; } - if (hasFolding(curwin->w_topline + n, NULL, &lnum)) { + if (hasFolding(curwin, curwin->w_topline + n, NULL, &lnum)) { n = lnum - curwin->w_topline; } } @@ -3682,7 +3681,7 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. lnum = curwin->w_topline; while (n-- > 0 && lnum < curwin->w_botline - 1) { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); lnum++; } n = lnum - curwin->w_topline; @@ -3696,7 +3695,7 @@ static void nv_scroll(cmdarg_T *cap) // Correct for 'so', except when an operator is pending. if (cap->oap->op_type == OP_NOP) { - cursor_correct(); + cursor_correct(curwin); } beginline(BL_SOL | BL_FIX); } @@ -3721,7 +3720,7 @@ static void nv_right(cmdarg_T *cap) // In virtual edit mode, there's no such thing as "past_line", as lines // are (theoretically) infinitely long. - if (virtual_active()) { + if (virtual_active(curwin)) { past_line = false; } @@ -3764,7 +3763,7 @@ static void nv_right(cmdarg_T *cap) break; } else if (past_line) { curwin->w_set_curswant = true; - if (virtual_active()) { + if (virtual_active(curwin)) { oneright(); } else { curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); @@ -3806,7 +3805,7 @@ static void nv_left(cmdarg_T *cap) || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL)) && curwin->w_cursor.lnum > 1) { curwin->w_cursor.lnum--; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_set_curswant = true; // When the NL before the first char has to be deleted we @@ -3897,6 +3896,10 @@ static void nv_gotofile(cmdarg_T *cap) return; } + if (!check_can_set_curbuf_disabled()) { + return; + } + char *ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { @@ -3937,7 +3940,7 @@ static void nv_dollar(cmdarg_T *cap) // In virtual mode when off the edge of a line and an operator // is pending (whew!) keep the cursor where it is. // Otherwise, send it to the end of the line. - if (!virtual_active() || gchar_cursor() != NUL + if (!virtual_active(curwin) || gchar_cursor() != NUL || cap->oap->op_type == OP_NOP) { curwin->w_curswant = MAXCOL; // so we stay at the end } @@ -4031,7 +4034,7 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrappe // "/$" will put the cursor after the end of the line, may need to // correct that here - check_cursor(); + check_cursor(curwin); return i; } @@ -4057,7 +4060,7 @@ static void nv_csearch(cmdarg_T *cap) curwin->w_set_curswant = true; // Include a Tab for "tx" and for "dfx". - if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD + if (gchar_cursor() == TAB && virtual_active(curwin) && cap->arg == FORWARD && (t_cmd || cap->oap->op_type != OP_NOP)) { colnr_T scol, ecol; @@ -4233,7 +4236,8 @@ static void nv_brackets(cmdarg_T *cap) (cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : 1), - MAXLNUM); + MAXLNUM, + false); xfree(ptr); curwin->w_set_curswant = true; } @@ -4512,7 +4516,7 @@ static void nv_replace(cmdarg_T *cap) } // Break tabs, etc. - if (virtual_active()) { + if (virtual_active(curwin)) { if (u_save_cursor() == false) { return; } @@ -4527,9 +4531,8 @@ static void nv_replace(cmdarg_T *cap) } // Abort if not enough characters to replace. - char *ptr = get_cursor_pos_ptr(); - if (strlen(ptr) < (unsigned)cap->count1 - || (mb_charlen(ptr) < cap->count1)) { + if ((size_t)get_cursor_pos_len() < (unsigned)cap->count1 + || (mb_charlen(get_cursor_pos_ptr()) < cap->count1)) { clearopbeep(cap->oap); return; } @@ -4625,7 +4628,7 @@ static void v_swap_corners(int cmdchar) pos_T old_cursor = curwin->w_cursor; getvcols(curwin, &old_cursor, &VIsual, &left, &right); curwin->w_cursor.lnum = VIsual.lnum; - coladvance(left); + coladvance(curwin, left); VIsual = curwin->w_cursor; curwin->w_cursor.lnum = old_cursor.lnum; @@ -4635,20 +4638,20 @@ static void v_swap_corners(int cmdchar) if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') { curwin->w_curswant++; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (curwin->w_cursor.col == old_cursor.col - && (!virtual_active() + && (!virtual_active(curwin) || curwin->w_cursor.coladd == old_cursor.coladd)) { curwin->w_cursor.lnum = VIsual.lnum; if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') { right++; } - coladvance(right); + coladvance(curwin, right); VIsual = curwin->w_cursor; curwin->w_cursor.lnum = old_cursor.lnum; - coladvance(left); + coladvance(curwin, left); curwin->w_curswant = left; } } else { @@ -4678,8 +4681,8 @@ static void nv_Replace(cmdarg_T *cap) if (!MODIFIABLE(curbuf)) { emsg(_(e_modifiable)); } else { - if (virtual_active()) { - coladvance(getviscol()); + if (virtual_active(curwin)) { + coladvance(curwin, getviscol()); } invoke_edit(cap, false, cap->arg ? 'V' : 'R', false); } @@ -4713,8 +4716,8 @@ static void nv_vreplace(cmdarg_T *cap) } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); - if (virtual_active()) { - coladvance(getviscol()); + if (virtual_active(curwin)) { + coladvance(curwin, getviscol()); } invoke_edit(cap, true, 'v', false); } @@ -4761,7 +4764,7 @@ static void n_swapchar(cmdarg_T *cap) } } - check_cursor(); + check_cursor(curwin); curwin->w_set_curswant = true; if (did_change) { changed_lines(curbuf, startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, @@ -4893,7 +4896,7 @@ static void nv_gomark(cmdarg_T *cap) move_res = nv_mark_move_to(cap, flags, fm); // May need to clear the coladd that a mark includes. - if (!virtual_active()) { + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } @@ -5022,7 +5025,7 @@ static void nv_visual(cmdarg_T *cap) // was only one -- webb if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) { curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1; - check_cursor(); + check_cursor(curwin); } VIsual_mode = resel_VIsual_mode; if (VIsual_mode == 'v') { @@ -5036,11 +5039,11 @@ static void nv_visual(cmdarg_T *cap) } else { curwin->w_curswant = resel_VIsual_vcol; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } if (resel_VIsual_vcol == MAXCOL) { curwin->w_curswant = MAXCOL; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else if (VIsual_mode == Ctrl_V) { // Update curswant on the original line, that is where "col" is valid. linenr_T lnum = curwin->w_cursor.lnum; @@ -5049,7 +5052,7 @@ static void nv_visual(cmdarg_T *cap) assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1; curwin->w_cursor.lnum = lnum; - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } else { curwin->w_set_curswant = true; } @@ -5101,9 +5104,9 @@ static void n_start_visual_mode(int c) // Corner case: the 0 position in a tab may change when going into // virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting. // - if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB) { - validate_virtcol(); - coladvance(curwin->w_virtcol); + if (c == Ctrl_V && (get_ve_flags(curwin) & VE_BLOCK) && gchar_cursor() == TAB) { + validate_virtcol(curwin); + coladvance(curwin, curwin->w_virtcol); } VIsual = curwin->w_cursor; @@ -5191,10 +5194,10 @@ static void nv_gv_cmd(cmdarg_T *cap) // Set Visual to the start and w_cursor to the end of the Visual // area. Make sure they are on an existing character. - check_cursor(); + check_cursor(curwin); VIsual = curwin->w_cursor; curwin->w_cursor = tpos; - check_cursor(); + check_cursor(curwin); update_topline(curwin); // When called from normal "g" command: start Select mode when @@ -5221,10 +5224,10 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - int width1 = curwin->w_width_inner - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + int width1 = curwin->w_width_inner - win_col_off(curwin); + int width2 = width1 + win_col_off2(curwin); - validate_virtcol(); + validate_virtcol(curwin); i = 0; if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) { i = (curwin->w_virtcol - width1) / width2 * width2 + width1; @@ -5236,10 +5239,10 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) // 'relativenumber' is on and lines are wrapping the middle can be more // to the left. if (cap->nchar == 'm') { - i += (curwin->w_width_inner - curwin_col_off() - + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2; + i += (curwin->w_width_inner - win_col_off(curwin) + + ((curwin->w_p_wrap && i > 0) ? win_col_off2(curwin) : 0)) / 2; } - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); if (flag) { do { i = gchar_cursor(); @@ -5281,7 +5284,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) { oparg_T *oap = cap->oap; int i; - int col_off = curwin_col_off(); + int col_off = win_col_off(curwin); const bool flag = cap->nchar == K_END || cap->nchar == K_KEND; oap->motion_type = kMTCharWise; @@ -5290,14 +5293,14 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) curwin->w_curswant = MAXCOL; // so we stay at the end if (cap->count1 == 1) { int width1 = curwin->w_width_inner - col_off; - int width2 = width1 + curwin_col_off2(); + int width2 = width1 + win_col_off2(curwin); - validate_virtcol(); + validate_virtcol(curwin); i = width1 - 1; if (curwin->w_virtcol >= (colnr_T)width1) { i += ((curwin->w_virtcol - width1) / width2 + 1) * width2; } - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); // Make sure we stick in this column. update_curswant_force(); @@ -5318,7 +5321,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) cursor_down(cap->count1 - 1, false); } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); // if the character doesn't fit move one back if (curwin->w_cursor.col > 0 && utf_ptr2cells(get_cursor_pos_ptr()) > 1) { @@ -5347,9 +5350,9 @@ static void nv_gi_cmd(cmdarg_T *cap) if (curbuf->b_last_insert.mark.lnum != 0) { curwin->w_cursor = curbuf->b_last_insert.mark; check_cursor_lnum(curwin); - int i = (int)strlen(get_cursor_line_ptr()); + int i = (int)get_cursor_line_len(); if (curwin->w_cursor.col > (colnr_T)i) { - if (virtual_active()) { + if (virtual_active(curwin)) { curwin->w_cursor.coladd += curwin->w_cursor.col - i; } curwin->w_cursor.col = i; @@ -5477,9 +5480,9 @@ static void nv_g_cmd(cmdarg_T *cap) oap->inclusive = false; i = linetabsize(curwin, curwin->w_cursor.lnum); if (cap->count0 > 0 && cap->count0 <= 100) { - coladvance((colnr_T)(i * cap->count0 / 100)); + coladvance(curwin, (colnr_T)(i * cap->count0 / 100)); } else { - coladvance((colnr_T)(i / 2)); + coladvance(curwin, (colnr_T)(i / 2)); } curwin->w_set_curswant = true; break; @@ -5702,11 +5705,11 @@ static void n_opencmd(cmdarg_T *cap) if (cap->cmdchar == 'O') { // Open above the first line of a folded sequence of lines - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); } else { // Open below the last line of a folded sequence of lines - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum); } // trigger TextChangedI for the 'o/O' command @@ -5887,7 +5890,7 @@ static void nv_pipe(cmdarg_T *cap) cap->oap->inclusive = false; beginline(0); if (cap->count0 > 0) { - coladvance((colnr_T)(cap->count0 - 1)); + coladvance(curwin, (colnr_T)(cap->count0 - 1)); curwin->w_curswant = (colnr_T)(cap->count0 - 1); } else { curwin->w_curswant = 0; @@ -5983,8 +5986,8 @@ static void adjust_cursor(oparg_T *oap) // - 'virtualedit' is not "all" and not "onemore". if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL && (!VIsual_active || *p_sel == 'o') - && !virtual_active() - && (get_ve_flags() & VE_ONEMORE) == 0) { + && !virtual_active(curwin) + && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { curwin->w_cursor.col--; // prevent cursor from moving on the trail byte mb_adjust_cursor(); @@ -6036,7 +6039,7 @@ bool unadjust_for_sel(void) mark_mb_adjustpos(curbuf, pp); } else if (pp->lnum > 1) { pp->lnum--; - pp->col = (colnr_T)strlen(ml_get(pp->lnum)); + pp->col = ml_get_len(pp->lnum); return true; } } @@ -6147,7 +6150,7 @@ static void nv_esc(cmdarg_T *cap) if (VIsual_active) { end_visual_mode(); // stop Visual - check_cursor_col(); // make sure cursor is not beyond EOL + check_cursor_col(curwin); // make sure cursor is not beyond EOL curwin->w_set_curswant = true; redraw_curbuf_later(UPD_INVERTED); } else if (no_reason) { @@ -6160,12 +6163,12 @@ static void nv_esc(cmdarg_T *cap) void set_cursor_for_append_to_line(void) { curwin->w_set_curswant = true; - if (get_ve_flags() == VE_ALL) { + if (get_ve_flags(curwin) == VE_ALL) { const int save_State = State; // Pretend Insert mode here to allow the cursor on the // character past the end of the line State = MODE_INSERT; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); State = save_State; } else { curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr()); @@ -6203,7 +6206,7 @@ static void nv_edit(cmdarg_T *cap) case 'a': // "a"ppend is like "i"nsert on the next character. // increment coladd when in virtual space, increment the // column otherwise, also to append after an unprintable char - if (virtual_active() + if (virtual_active(curwin) && (curwin->w_cursor.coladd > 0 || *get_cursor_pos_ptr() == NUL || *get_cursor_pos_ptr() == TAB)) { @@ -6220,7 +6223,7 @@ static void nv_edit(cmdarg_T *cap) // Pretend Insert mode here to allow the cursor on the // character past the end of the line State = MODE_INSERT; - coladvance(getviscol()); + coladvance(curwin, getviscol()); State = save_State; } @@ -6575,7 +6578,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // line. if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } } auto_format(false, true); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5a0ef66e91..2decb11d25 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -266,7 +266,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount) // Set "'[" and "']" marks. curbuf->b_op_start = oap->start; curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + curbuf->b_op_end.col = ml_get_len(oap->end.lnum); if (curbuf->b_op_end.col > 0) { curbuf->b_op_end.col--; } @@ -408,6 +408,7 @@ static void shift_block(oparg_T *oap, int amount) memset(newp + bd.textcol + tabs, ' ', (size_t)spaces); // Note that STRMOVE() copies the trailing NUL. STRMOVE(newp + bd.textcol + tabs + spaces, bd.textstart); + assert(newlen - oldlen == (colnr_T)new_line_len - get_cursor_line_len()); } else { // left char *verbatim_copy_end; // end of the part of the line which is // copied verbatim @@ -494,6 +495,7 @@ static void shift_block(oparg_T *oap, int amount) memset(newp + verbatim_diff, ' ', fill); // Note that STRMOVE() copies the trailing NUL. STRMOVE(newp + verbatim_diff + fill, non_white); + assert(newlen - oldlen == (colnr_T)new_line_len - get_cursor_line_len()); } // replace the line ml_replace(curwin->w_cursor.lnum, newp, false); @@ -562,8 +564,8 @@ static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def assert(count >= 0); // Make sure the allocated size matches what is actually copied below. - newp = xmalloc(strlen(oldp) + (size_t)spaces + s_len - + (spaces > 0 && !bdp->is_short ? (size_t)ts_val - (size_t)spaces : 0) + newp = xmalloc((size_t)ml_get_len(lnum) + (size_t)spaces + s_len + + (spaces > 0 && !bdp->is_short ? (size_t)(ts_val - spaces) : 0) + (size_t)count + 1); // copy up to shifted part @@ -1570,7 +1572,7 @@ int op_delete(oparg_T *oap) // Thus the number of characters may increase! int n = bd.textlen - bd.startspaces - bd.endspaces; char *oldp = ml_get(lnum); - char *newp = xmalloc(strlen(oldp) - (size_t)n + 1); + char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)n + 1); // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); // insert spaces @@ -1587,7 +1589,7 @@ int op_delete(oparg_T *oap) kExtmarkUndo); } - check_cursor_col(); + check_cursor_col(curwin); changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, oap->end.lnum + 1, 0, true); oap->line_count = 0; // no lines deleted @@ -1613,15 +1615,8 @@ int op_delete(oparg_T *oap) } else { beginline(0); // cursor in column 0 } - - int old_len = (int)strlen(ml_get(curwin->w_cursor.lnum)); - truncate_line(false); // delete the rest of the line - - extmark_splice_cols(curbuf, - (int)curwin->w_cursor.lnum - 1, curwin->w_cursor.col, - old_len - curwin->w_cursor.col, 0, kExtmarkUndo); - - // leave cursor past last char in line + truncate_line(false); // delete the rest of the line, + // leaving cursor past last char in line if (oap->line_count > 1) { u_clearline(curbuf); // "U" command not possible after "2cc" } @@ -1644,7 +1639,7 @@ int op_delete(oparg_T *oap) coladvance_force(getviscol2(oap->start.col, oap->start.coladd)); oap->start = curwin->w_cursor; if (oap->line_count == 1) { - coladvance(endcol); + coladvance(curwin, endcol); oap->end.col = curwin->w_cursor.col; oap->end.coladd = curwin->w_cursor.coladd; curwin->w_cursor = oap->start; @@ -1686,8 +1681,7 @@ int op_delete(oparg_T *oap) if (virtual_op) { // fix up things for virtualedit-delete: // break the tabs which are going to get in our way - char *curline = get_cursor_line_ptr(); - int len = (int)strlen(curline); + int len = get_cursor_line_len(); if (oap->end.coladd != 0 && (int)oap->end.col >= len - 1 @@ -1847,7 +1841,7 @@ static int op_replace(oparg_T *oap, int c) pos_T vpos; vpos.lnum = curwin->w_cursor.lnum; - getvpos(&vpos, oap->start_vcol); + getvpos(curwin, &vpos, oap->start_vcol); bd.startspaces += vpos.coladd; n = bd.startspaces; } else { @@ -1880,7 +1874,7 @@ static int op_replace(oparg_T *oap, int c) numc *= utf_char2len(c); char *oldp = get_cursor_line_ptr(); - colnr_T oldlen = (int)strlen(oldp); + colnr_T oldlen = get_cursor_line_len(); size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { @@ -1944,7 +1938,7 @@ static int op_replace(oparg_T *oap, int c) if (oap->motion_type == kMTLineWise) { oap->start.col = 0; curwin->w_cursor.col = 0; - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; } @@ -1982,7 +1976,7 @@ static int op_replace(oparg_T *oap, int c) } coladvance_force(getviscol()); if (curwin->w_cursor.lnum == oap->end.lnum) { - getvpos(&oap->end, end_vcol); + getvpos(curwin, &oap->end, end_vcol); } } // with "coladd" set may move to just after a TAB @@ -2025,7 +2019,7 @@ static int op_replace(oparg_T *oap, int c) } curwin->w_cursor = oap->start; - check_cursor(); + check_cursor(curwin); changed_lines(curbuf, oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0, true); if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { @@ -2063,7 +2057,7 @@ void op_tilde(oparg_T *oap) if (oap->motion_type == kMTLineWise) { oap->start.col = 0; pos.col = 0; - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; } @@ -2078,7 +2072,7 @@ void op_tilde(oparg_T *oap) while (true) { did_change |= swapchars(oap->op_type, &pos, pos.lnum == oap->end.lnum ? oap->end.col + 1 - : (int)strlen(ml_get_pos(&pos))); + : ml_get_pos_len(&pos)); if (ltoreq(oap->end, pos) || inc(&pos) == -1) { break; } @@ -2237,12 +2231,10 @@ void op_insert(oparg_T *oap, int count1) // Get indent information ind_pre_col = (colnr_T)getwhitecols_curline(); ind_pre_vcol = get_indent(); - char *firstline = ml_get(oap->start.lnum) + bd.textcol; - + pre_textlen = ml_get_len(oap->start.lnum) - bd.textcol; if (oap->op_type == OP_APPEND) { - firstline += bd.textlen; + pre_textlen -= bd.textlen; } - pre_textlen = (int)strlen(firstline); } if (oap->op_type == OP_APPEND) { @@ -2267,7 +2259,7 @@ void op_insert(oparg_T *oap, int count1) } } else { curwin->w_cursor = oap->end; - check_cursor_col(); + check_cursor_col(curwin); // Works just like an 'i'nsert on the next character. if (!LINEEMPTY(curwin->w_cursor.lnum) @@ -2369,7 +2361,7 @@ void op_insert(oparg_T *oap, int count1) // Subsequent calls to ml_get() flush the firstline data - take a // copy of the required string. char *firstline = ml_get(oap->start.lnum); - const size_t len = strlen(firstline); + colnr_T len = ml_get_len(oap->start.lnum); colnr_T add = bd.textcol; colnr_T offset = 0; // offset when cursor was moved in insert mode if (oap->op_type == OP_APPEND) { @@ -2386,12 +2378,12 @@ void op_insert(oparg_T *oap, int count1) } } } - if ((size_t)add > len) { - firstline += len; // short line, point to the NUL - } else { - firstline += add; + if (add > len) { + add = len; // short line, point to the NUL } - int ins_len = (int)strlen(firstline) - pre_textlen - offset; + firstline += add; + len -= add; + int ins_len = len - pre_textlen - offset; if (pre_textlen >= 0 && ins_len > 0) { char *ins_text = xmemdupz(firstline, (size_t)ins_len); // block handled here @@ -2400,7 +2392,7 @@ void op_insert(oparg_T *oap, int count1) } curwin->w_cursor.col = oap->start.col; - check_cursor(); + check_cursor(curwin); xfree(ins_text); } } @@ -2446,7 +2438,7 @@ int op_change(oparg_T *oap) coladvance_force(getviscol()); } firstline = ml_get(oap->start.lnum); - pre_textlen = (int)strlen(firstline); + pre_textlen = ml_get_len(oap->start.lnum); pre_indent = (int)getwhitecols(firstline); bd.textcol = curwin->w_cursor.col; } @@ -2479,7 +2471,7 @@ int op_change(oparg_T *oap) bd.textcol += (colnr_T)(new_indent - pre_indent); } - ins_len = (int)strlen(firstline) - pre_textlen; + ins_len = ml_get_len(oap->start.lnum) - pre_textlen; if (ins_len > 0) { // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. @@ -2495,13 +2487,13 @@ int op_change(oparg_T *oap) // initial coladd offset as part of "startspaces" if (bd.is_short) { vpos.lnum = linenr; - getvpos(&vpos, oap->start_vcol); + getvpos(curwin, &vpos, oap->start_vcol); } else { vpos.coladd = 0; } char *oldp = ml_get(linenr); - char *newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd - + (size_t)ins_len + 1); + char *newp = xmalloc((size_t)ml_get_len(linenr) + + (size_t)vpos.coladd + (size_t)ins_len + 1); // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); int offset = bd.textcol; @@ -2516,7 +2508,7 @@ int op_change(oparg_T *oap) 0, vpos.coladd + ins_len, kExtmarkUndo); } } - check_cursor(); + check_cursor(curwin); changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true); xfree(ins_text); } @@ -2850,7 +2842,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) bool allocated = false; const pos_T orig_start = curbuf->b_op_start; const pos_T orig_end = curbuf->b_op_end; - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(curwin); if (flags & PUT_FIXINDENT) { orig_indent = get_indent(); @@ -3071,9 +3063,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) // Correct line number for closed fold. Don't move the cursor yet, // u_save() uses it. if (dir == BACKWARD) { - hasFolding(lnum, &lnum, NULL); + hasFolding(curwin, lnum, &lnum, NULL); } else { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); } if (dir == FORWARD) { lnum++; @@ -3178,6 +3170,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } // get the old line and advance to the position to insert at char *oldp = get_cursor_line_ptr(); + colnr_T oldlen = get_cursor_line_len(); CharsizeArg csarg; CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, oldp); @@ -3188,7 +3181,6 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) vcol += incr; ci = utfc_next(ci); } - size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr); char *ptr = ci.ptr; bd.textcol = (colnr_T)(ptr - oldp); @@ -3238,7 +3230,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) totlen = (size_t)count * (size_t)(yanklen + spaces) + (size_t)bd.startspaces + (size_t)bd.endspaces; - char *newp = xmalloc(totlen + oldlen + 1); + char *newp = xmalloc(totlen + (size_t)oldlen + 1); // copy part up to cursor to new line ptr = newp; @@ -3268,7 +3260,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) ptr += bd.endspaces; // move the text after the cursor to the end of the line. - int columns = (int)oldlen - bd.textcol - delcount + 1; + int columns = oldlen - bd.textcol - delcount + 1; assert(columns >= 0); memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns); ml_replace(curwin->w_cursor.lnum, newp, false); @@ -3300,7 +3292,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor.col++; // in Insert mode we might be after the NUL, correct for that - colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -3364,22 +3356,22 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) totlen = (size_t)count * (size_t)yanklen; do { char *oldp = ml_get(lnum); - size_t oldlen = strlen(oldp); + colnr_T oldlen = ml_get_len(lnum); if (lnum > start_lnum) { pos_T pos = { .lnum = lnum, }; - if (getvpos(&pos, vcol) == OK) { + if (getvpos(curwin, &pos, vcol) == OK) { col = pos.col; } else { col = MAXCOL; } } - if (VIsual_active && col > (colnr_T)oldlen) { + if (VIsual_active && col > oldlen) { lnum++; continue; } - char *newp = xmalloc(totlen + oldlen + 1); + char *newp = xmalloc(totlen + (size_t)oldlen + 1); memmove(newp, oldp, (size_t)col); char *ptr = newp + col; for (size_t i = 0; i < (size_t)count; i++) { @@ -3436,7 +3428,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) lnum = new_cursor.lnum; char *ptr = ml_get(lnum) + col; totlen = strlen(y_array[y_size - 1]); - char *newp = xmalloc((size_t)(strlen(ptr) + totlen + 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); // insert second line @@ -3470,7 +3462,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor.lnum = lnum; char *ptr = ml_get(lnum); if (cnt == count && i == y_size - 1) { - lendiff = (int)strlen(ptr); + lendiff = ml_get_len(lnum); } if (*ptr == '#' && preprocs_left()) { indent = 0; // Leave # lines at start @@ -3487,7 +3479,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor = old_pos; // remember how many chars were removed if (cnt == count && i == y_size - 1) { - lendiff -= (int)strlen(ml_get(lnum)); + lendiff -= ml_get_len(lnum); } } } @@ -3593,7 +3585,7 @@ error: curwin->w_set_curswant = true; // Make sure the cursor is not after the NUL. - int len = (int)strlen(get_cursor_line_ptr()); + int len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { if (cur_ve_flags == VE_ALL) { curwin->w_cursor.coladd = curwin->w_cursor.col - len; @@ -3623,7 +3615,7 @@ end: /// there move it left. void adjust_cursor_eol(void) { - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(curwin); const bool adj_cursor = (curwin->w_cursor.col > 0 && gchar_cursor() == NUL @@ -4085,7 +4077,7 @@ int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatopti // vim: use the column of the last join curwin->w_cursor.col = (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col); - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; @@ -4306,7 +4298,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T } } if (endcol == MAXCOL) { - endcol = (colnr_T)strlen(p); + endcol = ml_get_len(lnum); } if (startcol > endcol || is_oneChar) { bdp->textlen = 0; @@ -4363,20 +4355,20 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) } else if (oap->motion_type == kMTLineWise) { curwin->w_cursor.col = 0; pos.col = 0; - length = (colnr_T)strlen(ml_get(pos.lnum)); + length = ml_get_len(pos.lnum); } else { // oap->motion_type == kMTCharWise if (pos.lnum == oap->start.lnum && !oap->inclusive) { dec(&(oap->end)); } - length = (colnr_T)strlen(ml_get(pos.lnum)); + length = ml_get_len(pos.lnum); pos.col = 0; if (pos.lnum == oap->start.lnum) { pos.col += oap->start.col; length -= oap->start.col; } if (pos.lnum == oap->end.lnum) { - length = (int)strlen(ml_get(oap->end.lnum)); + length = ml_get_len(oap->end.lnum); if (oap->end.col >= length) { oap->end.col = length - 1; } @@ -4452,16 +4444,17 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // "Unsigned" const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL; - if (virtual_active()) { + if (virtual_active(curwin)) { save_coladd = pos->coladd; pos->coladd = 0; } curwin->w_cursor = *pos; char *ptr = ml_get(pos->lnum); + int linelen = ml_get_len(pos->lnum); int col = pos->col; - if (*ptr == NUL || col + !!save_coladd >= (int)strlen(ptr)) { + if (col + !!save_coladd >= linelen) { goto theend; } @@ -4600,9 +4593,7 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // get the number value (unsigned) if (visual && VIsual_mode != 'V') { - maxlen = (curbuf->b_visual.vi_curswant == MAXCOL - ? (int)strlen(ptr) - col - : length); + maxlen = curbuf->b_visual.vi_curswant == MAXCOL ? linelen - col : length; } bool overflow = false; @@ -4774,7 +4765,7 @@ theend: curwin->w_cursor = save_cursor; } else if (did_change) { curwin->w_set_curswant = true; - } else if (virtual_active()) { + } else if (virtual_active(curwin)) { curwin->w_cursor.coladd = save_coladd; } @@ -5356,7 +5347,7 @@ void cursor_pos_info(dict_T *dict) switch (l_VIsual_mode) { case Ctrl_V: - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); block_prep(&oparg, &bd, lnum, false); virtual_op = kNone; s = bd.textstart; @@ -5445,10 +5436,10 @@ void cursor_pos_info(dict_T *dict) } } else { char *p = get_cursor_line_ptr(); - validate_virtcol(); + validate_virtcol(curwin); col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize_str(p)); + col_print(buf2, sizeof(buf2), get_cursor_line_len(), linetabsize_str(p)); if (char_count_cursor == byte_count_cursor && char_count == byte_count) { @@ -5530,7 +5521,7 @@ static void op_colon(oparg_T *oap) // When using !! on a closed fold the range ".!" works best to operate // on, it will be made the whole closed fold later. linenr_T endOfStartFold = oap->start.lnum; - hasFolding(oap->start.lnum, NULL, &endOfStartFold); + hasFolding(curwin, oap->start.lnum, NULL, &endOfStartFold); if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) { // Make it a range with the end line. stuffcharReadbuff(','); @@ -5541,7 +5532,7 @@ static void op_colon(oparg_T *oap) } else if (oap->start.lnum == curwin->w_cursor.lnum // do not use ".+number" for a closed fold, it would count // folded lines twice - && !hasFolding(oap->end.lnum, NULL, NULL)) { + && !hasFolding(curwin, oap->end.lnum, NULL, NULL)) { stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); } else { @@ -5703,11 +5694,11 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) // (Actually, this does convert column positions into character // positions) curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); + coladvance(curwin, oap->end_vcol); oap->end = curwin->w_cursor; curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); + coladvance(curwin, oap->start_vcol); oap->start = curwin->w_cursor; } @@ -5832,7 +5823,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') { if (VIsual_mode == 'v') { if (redo_VIsual.rv_line_count <= 1) { - validate_virtcol(); + validate_virtcol(curwin); curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1; } else { curwin->w_curswant = redo_VIsual.rv_vcol; @@ -5840,7 +5831,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { curwin->w_curswant = MAXCOL; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } cap->count0 = redo_VIsual.rv_count; cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); @@ -5862,11 +5853,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && cap->oap->op_type != OP_DELETE) { if (lt(VIsual, curwin->w_cursor)) { VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)strlen(ml_get(curwin->w_cursor.lnum)); + curwin->w_cursor.col = ml_get_len(curwin->w_cursor.lnum); } else { curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)strlen(ml_get(VIsual.lnum)); + VIsual.col = ml_get_len(VIsual.lnum); } VIsual_mode = 'v'; } else if (VIsual_mode == 'v') { @@ -5887,15 +5877,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (lt(oap->start, curwin->w_cursor)) { // Include folded lines completely. if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + if (hasFolding(curwin, oap->start.lnum, &oap->start.lnum, NULL)) { oap->start.col = 0; } if ((curwin->w_cursor.col > 0 || oap->inclusive || oap->motion_type == kMTLineWise) - && hasFolding(curwin->w_cursor.lnum, NULL, + && hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - curwin->w_cursor.col = (colnr_T)strlen(get_cursor_line_ptr()); + curwin->w_cursor.col = get_cursor_line_len(); } } oap->end = curwin->w_cursor; @@ -5908,12 +5898,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { // Include folded lines completely. if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + if (hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.col = 0; } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)strlen(ml_get(oap->start.lnum)); + if (hasFolding(curwin, oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = ml_get_len(oap->start.lnum); } } oap->end = oap->start; @@ -5925,7 +5915,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->line_count = oap->end.lnum - oap->start.lnum + 1; // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); if (VIsual_active || redo_VIsual_busy) { get_op_vcol(oap, redo_VIsual.rv_vcol, true); @@ -6097,7 +6087,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (inindent(0)) { oap->motion_type = kMTLineWise; } else { - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; oap->inclusive = true; @@ -6156,7 +6146,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->excl_tr_ws = cap->cmdchar == 'z'; op_yank(oap, !gui_yank); } - check_cursor_col(); + check_cursor_col(curwin); break; case OP_CHANGE: @@ -6230,7 +6220,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { op_tilde(oap); } - check_cursor_col(); + check_cursor_col(curwin); break; case OP_FORMAT: @@ -6348,7 +6338,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg); VIsual_active = false; } - check_cursor_col(); + check_cursor_col(curwin); break; default: clearopbeep(oap); @@ -6360,7 +6350,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { reset_lbr(); - coladvance(curwin->w_curswant = old_col); + coladvance(curwin, curwin->w_curswant = old_col); } } else { curwin->w_cursor = old_cursor; @@ -6871,14 +6861,13 @@ bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum if (start_lnum == end_lnum) { return end_col - start_col; } - const char *first = ml_get_buf(buf, start_lnum); - bcount_t deleted_bytes = (bcount_t)strlen(first) - start_col + 1; + bcount_t deleted_bytes = ml_get_buf_len(buf, start_lnum) - start_col + 1; for (linenr_T i = 1; i <= end_lnum - start_lnum - 1; i++) { if (start_lnum + i > max_lnum) { return deleted_bytes; } - deleted_bytes += (bcount_t)strlen(ml_get_buf(buf, start_lnum + i)) + 1; + deleted_bytes += ml_get_buf_len(buf, start_lnum + i) + 1; } if (end_lnum > max_lnum) { return deleted_bytes; diff --git a/src/nvim/option.c b/src/nvim/option.c index 0ac65ed95d..3d7fdefdeb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1937,7 +1937,7 @@ static const char *did_set_arabic(optset_T *args) // set rightleft mode if (!win->w_p_rl) { win->w_p_rl = true; - changed_window_setting(); + changed_window_setting(curwin); } // Enable Arabic shaping (major part of what Arabic requires) @@ -1968,7 +1968,7 @@ static const char *did_set_arabic(optset_T *args) // reset rightleft mode if (win->w_p_rl) { win->w_p_rl = false; - changed_window_setting(); + changed_window_setting(curwin); } // 'arabicshape' isn't reset, it is a global option and @@ -2024,9 +2024,6 @@ static const char *did_set_cmdheight(optset_T *args) { OptInt old_value = args->os_oldval.number; - if (ui_has(kUIMessages)) { - p_ch = 0; - } if (p_ch > Rows - min_rows() + 1) { p_ch = Rows - min_rows() + 1; } @@ -3032,7 +3029,7 @@ void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags) if (flags & P_HLONLY) { redraw_later(win, UPD_NOT_VALID); } else { - changed_window_setting_win(win); + changed_window_setting(win); } } if (flags & P_RBUF) { @@ -4629,6 +4626,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(win->w_p_rnu); case PV_NUW: return &(win->w_p_nuw); + case PV_WFB: + return &(win->w_p_wfb); case PV_WFH: return &(win->w_p_wfh); case PV_WFW: @@ -6105,9 +6104,9 @@ char *get_flp_value(buf_T *buf) } /// Get the local or global value of the 'virtualedit' flags. -unsigned get_ve_flags(void) +unsigned get_ve_flags(win_T *wp) { - return (curwin->w_ve_flags ? curwin->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); + return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); } /// Get the local or global value of 'showbreak'. diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 4452df413a..ca8e1eaec5 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -6573,9 +6573,6 @@ return { top are deleted if new lines exceed this limit. Minimum is 1, maximum is 100000. Only in |terminal| buffers. - - Note: Lines that are not visible and kept in scrollback are not - reflown when the terminal buffer is resized horizontally. ]=], full_name = 'scrollback', redraw = { 'current_buffer' }, @@ -7331,7 +7328,8 @@ return { items, for instance "scanning tags" q do not show "recording @a" when recording a macro *shm-q* F don't give the file info when editing a file, like *shm-F* - `:silent` was used for the command + `: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]" @@ -8405,6 +8403,8 @@ return { "split" when both are present. uselast If included, jump to the previously used window when jumping to errors with |quickfix| commands. + If a window has 'winfixbuf' enabled, 'switchbuf' is currently not + applied to the split window. ]=], expand_cb = 'expand_set_switchbuf', full_name = 'switchbuf', @@ -9816,6 +9816,23 @@ return { varname = 'p_window', }, { + abbreviation = 'wfb', + defaults = { if_true = false }, + desc = [=[ + If enabled, the window and the buffer it is displaying are paired. + For example, attempting to change the buffer with |:edit| will fail. + Other commands which change a window's buffer such as |:cnext| will + also skip any window with 'winfixbuf' enabled. However if an Ex + command has a "!" modifier, it can force switching buffers. + ]=], + full_name = 'winfixbuf', + pv_name = 'p_wfb', + redraw = { 'current_window' }, + scope = { 'window' }, + short_desc = N_('pin a window to a specific buffer'), + type = 'boolean', + }, + { abbreviation = 'wfh', defaults = { if_true = false }, desc = [=[ diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 518208a037..f80926726a 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -2100,7 +2100,13 @@ const char *did_set_showbreak(optset_T *args) /// The 'showcmdloc' option is changed. const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) { - return did_set_opt_strings(p_sloc, p_sloc_values, true); + const char *errmsg = did_set_opt_strings(p_sloc, p_sloc_values, false); + + if (errmsg == NULL) { + comp_col(); + } + + return errmsg; } int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches) @@ -2473,9 +2479,8 @@ const char *did_set_virtualedit(optset_T *args) } else if (strcmp(ve, args->os_oldval.string.data) != 0) { // Recompute cursor position in case the new 've' setting // changes something. - validate_virtcol_win(win); - // XXX: this only works when win == curwin - coladvance(win->w_virtcol); + validate_virtcol(win); + coladvance(win, win->w_virtcol); } } return NULL; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 0d4452662d..8a81f6e928 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -726,7 +726,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es // with it, skip a character if (after_pathsep(dst, dst + c) #if defined(BACKSLASH_IN_FILENAME) - && dst[-1] != ':' + && dst[c - 1] != ':' #endif && vim_ispathsep(*tail)) { tail++; diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index ade745df2c..85caf4aa43 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -789,7 +789,7 @@ void os_copy_xattr(const char *from_file, const char *to_file) // get the length of the extended attributes ssize_t size = listxattr((char *)from_file, NULL, 0); // not supported or no attributes to copy - if (errno == ENOTSUP || size <= 0) { + if (size <= 0) { return; } char *xattr_buf = xmalloc((size_t)size); diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index fdc06f9804..12831ff05f 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -32,11 +32,10 @@ static void start_wait_eof_timer(void **argv) FUNC_ATTR_NONNULL_ALL { PtyProcess *ptyproc = (PtyProcess *)argv[0]; - Process *proc = (Process *)ptyproc; - uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); - ptyproc->wait_eof_timer.data = (void *)ptyproc; - uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); + if (ptyproc->finish_wait != NULL) { + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); + } } /// @returns zero on success, or negative error code. @@ -117,6 +116,8 @@ int pty_process_spawn(PtyProcess *ptyproc) } proc->pid = (int)GetProcessId(process_handle); + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, pty_process_finish1, @@ -176,6 +177,16 @@ void pty_process_close(PtyProcess *ptyproc) pty_process_close_master(ptyproc); + if (ptyproc->finish_wait != NULL) { + UnregisterWaitEx(ptyproc->finish_wait, NULL); + ptyproc->finish_wait = NULL; + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + } + if (ptyproc->process_handle != NULL) { + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + } + if (proc->internal_close_cb) { proc->internal_close_cb(proc); } @@ -204,6 +215,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; + assert(ptyproc->finish_wait != NULL); if (proc->out.closed || proc->out.did_eof || !uv_is_readable(proc->out.uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); @@ -215,16 +227,10 @@ static void pty_process_finish2(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - UnregisterWaitEx(ptyproc->finish_wait, NULL); - uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); - DWORD exit_code = 0; GetExitCodeProcess(ptyproc->process_handle, &exit_code); proc->status = proc->exit_signal ? 128 + proc->exit_signal : (int)exit_code; - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; - proc->internal_exit_cb(proc); } diff --git a/src/nvim/plines.c b/src/nvim/plines.c index eca07f0144..44b06a0403 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -540,7 +540,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en if (ci.chr.value == TAB && (State & MODE_NORMAL) && !wp->w_p_list - && !virtual_active() + && !virtual_active(wp) && !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) { // cursor at end *cursor = vcol + incr - 1; @@ -583,7 +583,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e { colnr_T col; - if (virtual_active()) { + if (virtual_active(wp)) { // For virtual mode, only want one value getvcol(wp, pos, &col, NULL, NULL); @@ -593,7 +593,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e // Cannot put the cursor on part of a wide character. char *ptr = ml_get_buf(wp->w_buffer, pos->lnum); - if (pos->col < (colnr_T)strlen(ptr)) { + if (pos->col < ml_get_buf_len(wp->w_buffer, pos->lnum)) { int c = utf_ptr2char(ptr + pos->col); if ((c != TAB) && vim_isprintc(c)) { endadd = (colnr_T)(char2cells(c) - 1); @@ -902,7 +902,7 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ if (start_vcol >= 0) { linenr_T lnum_next = lnum; - const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); height_sum_nofill += height_cur_nofill; const int64_t row_off = (start_vcol < width1 || width2 <= 0) @@ -914,7 +914,7 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ while (lnum <= end_lnum) { linenr_T lnum_next = lnum; - const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next); height_sum_fill += win_get_fill(wp, lnum); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); height_sum_nofill += height_cur_nofill; diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index e34d6fd97f..5e8fb7f5de 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -140,7 +140,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i // to avoid that must_redraw is set when 'cursorcolumn' is on. pum_is_visible = true; pum_is_drawn = true; - validate_cursor_col(); + validate_cursor_col(curwin); int above_row = 0; int below_row = cmdline_row; @@ -273,7 +273,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i context_lines = 0; } else { // Leave two lines of context if possible - validate_cheight(); + validate_cheight(curwin); if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { context_lines = 3; } else { @@ -995,7 +995,7 @@ static bool pum_set_selected(int n, int repeat) } // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_SOME_VALID); // When the preview window was resized we need to diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 651ebc9f93..0f639bf86a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -773,9 +773,9 @@ static int qf_get_next_buf_line(qfstate_T *state) return QF_END_OF_INPUT; } char *p_buf = ml_get_buf(state->buf, state->buflnum); + size_t len = (size_t)ml_get_buf_len(state->buf, state->buflnum); state->buflnum += 1; - size_t len = strlen(p_buf); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -2699,7 +2699,8 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Didn't find it, go to the window before the quickfix // window, unless 'switchbuf' contains 'uselast': in this case we // try to jump to the previously used window first. - if ((swb_flags & SWB_USELAST) && win_valid(prevwin)) { + if ((swb_flags & SWB_USELAST) && win_valid(prevwin) + && !prevwin->w_p_wfb) { win = prevwin; } else if (altwin != NULL) { win = altwin; @@ -2714,6 +2715,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Remember a usable window. if (altwin == NULL && !win->w_p_pvw + && !win->w_p_wfb && bt_normal(win->w_buffer)) { altwin = win; } @@ -2802,8 +2804,43 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int ECMD_HIDE + ECMD_SET_HELP, prev_winid == curwin->handle ? curwin : NULL); } else { - retval = buflist_getfile(qf_ptr->qf_fnum, 1, - GETF_SETMARK | GETF_SWITCH, forceit); + int fnum = qf_ptr->qf_fnum; + + if (!forceit && curwin->w_p_wfb && curbuf->b_fnum != fnum) { + if (qi->qfl_type == QFLT_LOCATION) { + // Location lists cannot split or reassign their window + // so 'winfixbuf' windows must fail + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return FAIL; + } + + if (win_valid(prevwin) && !prevwin->w_p_wfb + && !bt_quickfix(prevwin->w_buffer)) { + // 'winfixbuf' is set; attempt to change to a window without it + // that isn't a quickfix/location list window. + win_goto(prevwin); + } + if (curwin->w_p_wfb) { + // Split the window, which will be 'nowinfixbuf', and set curwin + // to that + if (win_split(0, 0) == OK) { + *opened_window = true; + } + if (curwin->w_p_wfb) { + // Autocommands set 'winfixbuf' or sent us to another window + // with it set, or we failed to split the window. Give up, + // but don't return immediately, as they may have messed + // with the list. + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + retval = FAIL; + } + } + } + + if (retval == OK) { + retval = buflist_getfile(fnum, 1, + GETF_SETMARK | GETF_SWITCH, forceit); + } } // If a location list, check whether the associated window is still // present. @@ -2852,12 +2889,12 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char if (qf_col > 0) { curwin->w_cursor.coladd = 0; if (qf_viscol == true) { - coladvance(qf_col - 1); + coladvance(curwin, qf_col - 1); } else { curwin->w_cursor.col = qf_col - 1; } curwin->w_set_curswant = true; - check_cursor(); + check_cursor(curwin); } else { beginline(BL_WHITE | BL_FIX); } @@ -3794,7 +3831,7 @@ void ex_copen(exarg_T *eap) curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; - check_cursor(); + check_cursor(curwin); update_topline(curwin); // scroll to show the line } @@ -4297,6 +4334,11 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit) if (qf_restore_list(qi, save_qfid) == FAIL) { return; } + + if (!check_can_set_curbuf_forceit(forceit)) { + return; + } + // Autocommands might have cleared the list, check for that if (!qf_list_empty(qf_get_curlist(qi))) { qf_jump(qi, 0, 0, forceit); @@ -5125,7 +5167,7 @@ void ex_cfile(exarg_T *eap) // This function is used by the :cfile, :cgetfile and :caddfile // commands. - // :cfile always creates a new quickfix list and jumps to the + // :cfile always creates a new quickfix list and may jump to the // first error. // :cgetfile creates a new quickfix list but doesn't jump to the // first error. @@ -5314,12 +5356,13 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp break; } col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); - if (col > (colnr_T)strlen(ml_get_buf(buf, lnum))) { + if (col > ml_get_buf_len(buf, lnum)) { break; } } } else { char *const str = ml_get_buf(buf, lnum); + const colnr_T linelen = ml_get_buf_len(buf, lnum); int score; uint32_t matches[MAX_FUZZY_MATCHES]; const size_t sz = sizeof(matches) / sizeof(matches[0]); @@ -5358,7 +5401,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp break; } col = (colnr_T)matches[pat_len - 1] + col + 1; - if (col > (colnr_T)strlen(str)) { + if (col > linelen) { break; } } @@ -5587,6 +5630,10 @@ theend: /// ":lvimgrepadd {pattern} file(s)" void ex_vimgrep(exarg_T *eap) { + if (!check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + char *au_name = vgr_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 48e41c290d..1720274e61 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -604,7 +604,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, && pos->col < MAXCOL - 2) { // Watch out for the "col" being MAXCOL - 2, used in a closed fold. ptr = ml_get_buf(buf, pos->lnum); - if ((int)strlen(ptr) <= pos->col) { + if (ml_get_buf_len(buf, pos->lnum) <= pos->col) { start_char_len = 1; } else { start_char_len = utfc_ptr2len(ptr + pos->col); @@ -851,7 +851,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, if (endpos.col == 0) { if (pos->lnum > 1) { // just in case pos->lnum--; - pos->col = (colnr_T)strlen(ml_get_buf(buf, pos->lnum)); + pos->col = ml_get_buf_len(buf, pos->lnum); } } else { pos->col--; @@ -969,7 +969,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, // A pattern like "\n\zs" may go past the last line. if (pos->lnum > buf->b_ml.ml_line_count) { pos->lnum = buf->b_ml.ml_line_count; - pos->col = (int)strlen(ml_get_buf(buf, pos->lnum)); + pos->col = ml_get_buf_len(buf, pos->lnum); if (pos->col > 0) { pos->col--; } @@ -1076,11 +1076,11 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in // If the cursor is in a closed fold, don't find another match in the same // fold. if (dirc == '/') { - if (hasFolding(pos.lnum, NULL, &pos.lnum)) { + if (hasFolding(curwin, pos.lnum, NULL, &pos.lnum)) { pos.col = MAXCOL - 2; // avoid overflow when adding 1 } } else { - if (hasFolding(pos.lnum, &pos.lnum, NULL)) { + if (hasFolding(curwin, pos.lnum, &pos.lnum, NULL)) { pos.col = 0; } } @@ -1389,7 +1389,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in show_top_bot_msg, msgbuf, (count != 1 || has_offset || (!(fdo_flags & FDO_SEARCH) - && hasFolding(curwin->w_cursor.lnum, NULL, + && hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL))), SEARCH_STAT_DEF_MAX_COUNT, SEARCH_STAT_DEF_TIMEOUT); @@ -1554,7 +1554,7 @@ int searchc(cmdarg_T *cap, bool t_cmd) char *p = get_cursor_line_ptr(); int col = curwin->w_cursor.col; - int len = (int)strlen(p); + int len = get_cursor_line_len(); while (count--) { while (true) { @@ -1958,7 +1958,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } linep = ml_get(pos.lnum); - pos.col = (colnr_T)strlen(linep); // pos.col on trailing NUL + pos.col = ml_get_len(pos.lnum); // pos.col on trailing NUL do_quotes = -1; line_breakcheck(); @@ -2105,7 +2105,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } if (pos.lnum > 1) { ptr = ml_get(pos.lnum - 1); - if (*ptr && *(ptr + strlen(ptr) - 1) == '\\') { + if (*ptr && *(ptr + ml_get_len(pos.lnum - 1) - 1) == '\\') { do_quotes = 1; if (start_in_quotes == kNone) { inquote = at_start; @@ -2472,7 +2472,7 @@ int current_search(int count, bool forward) } else { // try again from end of buffer // searching backwards, so set pos to last line and col pos.lnum = curwin->w_buffer->b_ml.ml_line_count; - pos.col = (colnr_T)strlen(ml_get(curwin->w_buffer->b_ml.ml_line_count)); + pos.col = ml_get_len(curwin->w_buffer->b_ml.ml_line_count); } } } @@ -3564,8 +3564,10 @@ static char *get_line_and_copy(linenr_T lnum, char *buf) /// @param action What to do when we find it /// @param start_lnum first line to start searching /// @param end_lnum last line for searching +/// @param forceit If true, always switch to the found path void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments, - int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum) + int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum, + int forceit) { SearchedFile *files; // Stack of included files SearchedFile *bigger; // When we need more space @@ -4025,17 +4027,17 @@ search_line: break; } if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, - NULL, true, lnum, false))) { + NULL, true, lnum, forceit))) { break; // failed to jump to file } } else { setpcmark(); } curwin->w_cursor.lnum = lnum; - check_cursor(); + check_cursor(curwin); } else { if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true, - files[depth].lnum, false))) { + files[depth].lnum, forceit))) { break; // failed to jump to file } // autocommands may have changed the lnum, we don't @@ -4051,7 +4053,7 @@ search_line: if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 53100dc081..490ab2b050 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -41,7 +41,6 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c2091c6bae..04c4f47a60 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1342,7 +1342,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att while (!got_int) { char *line = ml_get_buf(wp->w_buffer, lnum); - len = strlen(line); + len = (size_t)ml_get_buf_len(wp->w_buffer, lnum); if (buflen < len + MAXWLEN + 2) { xfree(buf); buflen = len + MAXWLEN + 2; @@ -2682,7 +2682,7 @@ void ex_spellrepall(exarg_T *eap) char *line = get_cursor_line_ptr(); if (addlen <= 0 || strncmp(line + curwin->w_cursor.col, repl_to, repl_to_len) != 0) { - char *p = xmalloc(strlen(line) + (size_t)addlen + 1); + 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); diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 1c632d2700..45e1b37818 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5114,13 +5114,12 @@ static void sug_write(spellinfo_T *spin, char *fname) for (linenr_T lnum = 1; lnum <= wcount; lnum++) { // <sugline>: <sugnr> ... NUL char *line = ml_get_buf(spin->si_spellbuf, lnum); - size_t len = strlen(line) + 1; - if (fwrite(line, len, 1, fd) == 0) { + int len = ml_get_buf_len(spin->si_spellbuf, lnum) + 1; + if (fwrite(line, (size_t)len, 1, fd) == 0) { emsg(_(e_write)); goto theend; } - assert((size_t)spin->si_memtot + len <= INT_MAX); - spin->si_memtot += (int)len; + spin->si_memtot += len; } // Write another byte to check for errors. diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 5c0e295f88..0cbd3d9795 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -480,9 +480,8 @@ void spell_suggest(int count) badlen++; end_visual_mode(); // make sure we don't include the NUL at the end of the line - char *line = get_cursor_line_ptr(); - if (badlen > (int)strlen(line) - (int)curwin->w_cursor.col) { - badlen = (int)strlen(line) - (int)curwin->w_cursor.col; + if (badlen > get_cursor_line_len() - curwin->w_cursor.col) { + badlen = get_cursor_line_len() - curwin->w_cursor.col; } // Find the start of the badly spelled word. } else if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0 @@ -514,7 +513,7 @@ void spell_suggest(int count) int need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, curwin->w_cursor.col); // Make a copy of current line since autocommands may free the line. - char *line = xstrdup(get_cursor_line_ptr()); + char *line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); spell_suggest_timeout = 5000; // Get the list of suggestions. Limit to 'lines' - 2 or the number in diff --git a/src/nvim/state.c b/src/nvim/state.c index 0df060ecf4..baa1e75775 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -135,9 +135,9 @@ void state_handle_k_event(void) } /// Return true if in the current mode we need to use virtual. -bool virtual_active(void) +bool virtual_active(win_T *wp) { - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(wp); // While an operator is being executed we return "virtual_op", because // VIsual_active has already been reset, thus we can't check for "block" diff --git a/src/nvim/state.h b/src/nvim/state.h index 9002f018d2..8220d90a67 100644 --- a/src/nvim/state.h +++ b/src/nvim/state.h @@ -1,6 +1,7 @@ #pragma once #include "nvim/state_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "state.h.generated.h" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index b403f840ca..1d914dd105 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1002,11 +1002,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Get the byte value now, in case we need it below. This is more // efficient than making a copy of the line. int byteval; - const size_t len = strlen(line_ptr); - if (wp->w_cursor.col > (colnr_T)len) { + const colnr_T len = ml_get_buf_len(wp->w_buffer, lnum); + if (wp->w_cursor.col > len) { // Line may have changed since checking the cursor column, or the lnum // was adjusted above. - wp->w_cursor.col = (colnr_T)len; + wp->w_cursor.col = len; wp->w_cursor.coladd = 0; byteval = 0; } else { diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 06622a86b7..a04aaa8f60 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -299,8 +299,8 @@ char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newli char *vim_strsave_up(const char *string) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *p1 = xstrdup(string); - vim_strup(p1); + char *p1 = xmalloc(strlen(string) + 1); + vim_strcpy_up(p1, string); return p1; } @@ -309,8 +309,8 @@ char *vim_strsave_up(const char *string) char *vim_strnsave_up(const char *string, size_t len) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *p1 = xstrnsave(string, len); - vim_strup(p1); + char *p1 = xmalloc(len + 1); + vim_strncpy_up(p1, string, len); return p1; } @@ -324,6 +324,39 @@ void vim_strup(char *p) } } +// strcpy plus vim_strup. +void vim_strcpy_up(char *restrict dst, const char *restrict src) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while ((c = (uint8_t)(*src++)) != NUL) { + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } + *dst = '\0'; +} + +// strncpy (NUL-terminated) plus vim_strup. +void vim_strncpy_up(char *restrict dst, const char *restrict src, size_t n) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while (n-- && (c = (uint8_t)(*src++)) != NUL) { + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } + *dst = '\0'; +} + +// memcpy (does not NUL-terminate) plus vim_strup. +void vim_memcpy_up(char *restrict dst, const char *restrict src, size_t n) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while (n--) { + c = (uint8_t)(*src++); + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } +} + /// Make given string all upper-case or all lower-case /// /// Handles multi-byte characters as good as possible. @@ -953,6 +986,40 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const return OK; } +static void format_overflow_error(const char *pstart) +{ + const char *p = pstart; + + while (ascii_isdigit((int)(*p))) { + p++; + } + + size_t arglen = (size_t)(p - pstart); + char *argcopy = xstrnsave(pstart, arglen); + semsg(_(e_val_too_large), argcopy); + xfree(argcopy); +} + +enum { MAX_ALLOWED_STRING_WIDTH = 6400, }; + +static int get_unsigned_int(const char *pstart, const char **p, unsigned *uj) +{ + *uj = (unsigned)(**p - '0'); + (*p)++; + + while (ascii_isdigit((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH) { + *uj = 10 * *uj + (unsigned)(**p - '0'); + (*p)++; + } + + if (*uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(pstart); + return FAIL; + } + + return OK; +} + static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *fmt, typval_T *tvs) FUNC_ATTR_NONNULL_ARG(1, 2) { @@ -986,6 +1053,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * // variable for positional arg int pos_arg = -1; + const char *pstart = p + 1; p++; // skip '%' @@ -1005,11 +1073,12 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } // Positional argument - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(pstart, &p, &uj) == FAIL) { + goto error; } + pos_arg = (int)uj; any_pos = 1; @@ -1047,10 +1116,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * if (ascii_isdigit((int)(*p))) { // Positional argument field width - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) { + goto error; } if (*p != '$') { @@ -1072,10 +1141,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1093,10 +1163,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * if (ascii_isdigit((int)(*p))) { // Parse precision - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1119,10 +1189,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1414,11 +1485,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (*ptype == '$') { // Positional argument - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + pos_arg = (int)uj; p++; @@ -1449,15 +1522,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st // parse field width if (*p == '*') { + const char *digstart = p + 1; + p++; if (ascii_isdigit((int)(*p))) { // Positional argument field width - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + arg_idx = (int)uj; p++; @@ -1469,6 +1545,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st &arg_cur, fmt), va_arg(ap, int))); + if (j > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; + } + if (j >= 0) { min_field_width = (size_t)j; } else { @@ -1478,11 +1559,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; + + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; + } - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; } + min_field_width = uj; } @@ -1494,22 +1582,32 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; + } + + if (uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; } + precision = uj; } else if (*p == '*') { + const char *digstart = p; + p++; if (ascii_isdigit((int)(*p))) { // positional argument - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + arg_idx = (int)uj; p++; @@ -1521,6 +1619,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st &arg_cur, fmt), va_arg(ap, int))); + if (j > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; + } + if (j >= 0) { precision = (size_t)j; } else { @@ -2127,6 +2230,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st emsg(_("E767: Too many arguments to printf()")); } +error: xfree(ap_types); va_end(ap); @@ -2173,7 +2277,7 @@ String arena_printf(Arena *arena, const char *fmt, ...) char *buf = NULL; if (arena) { if (!arena->cur_blk) { - alloc_block(arena); + arena_alloc_block(arena); } // happy case, we can fit the printed string in the rest of the current diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index ab8ab62b90..76824879d7 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -549,8 +549,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) // Skip lines that end in a backslash. for (; start_lnum > 1; start_lnum--) { - char *line = ml_get(start_lnum - 1); - if (*line == NUL || *(line + strlen(line) - 1) != '\\') { + char *l = ml_get(start_lnum - 1); + if (*l == NUL || *(l + ml_get_len(start_lnum - 1) - 1) != '\\') { break; } } @@ -2352,7 +2352,6 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ regmmatch_T regmatch; regmmatch_T best_regmatch; // startpos/endpos of best match lpos_T pos; - char *line; bool had_match = false; char buf_chartab[32]; // chartab array for syn option iskeyword @@ -2457,8 +2456,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ break; } - line = ml_get_buf(syn_buf, startpos->lnum); - int line_len = (int)strlen(line); + int line_len = ml_get_buf_len(syn_buf, startpos->lnum); // take care of an empty match or negative offset if (pos.col <= matchcol) { @@ -2635,7 +2633,7 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s if (result->lnum > syn_buf->b_ml.ml_line_count) { // a "\n" at the end of the pattern may take us below the last line result->lnum = syn_buf->b_ml.ml_line_count; - col = (int)strlen(ml_get_buf(syn_buf, result->lnum)); + col = ml_get_buf_len(syn_buf, result->lnum); } if (off != 0) { base = ml_get_buf(syn_buf, result->lnum); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ab5bfc6773..0265d2d822 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -290,6 +290,10 @@ void set_buflocal_tfu_callback(buf_T *buf) /// @param verbose print "tag not found" message void do_tag(char *tag, int type, int count, int forceit, bool verbose) { + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { + return; + } + taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; int tagstacklen = curwin->w_tagstacklen; @@ -445,7 +449,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose) } curwin->w_cursor.col = saved_fmark.mark.col; curwin->w_set_curswant = true; - check_cursor(); + check_cursor(curwin); if ((fdo_flags & FDO_TAG) && old_KeyTyped) { foldOpenCursor(); } @@ -2784,6 +2788,10 @@ static char *tag_full_fname(tagptrs_T *tagp) /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) { + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { + return FAIL; + } + char *pbuf_end; char *tofree_fname = NULL; tagptrs_T tagp; @@ -2994,7 +3002,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) // A search command may have positioned the cursor beyond the end // of the line. May need to correct that here. - check_cursor(); + check_cursor(curwin); } else { const int save_secure = secure; @@ -3039,7 +3047,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index b5a3cffe2f..edde7ff57a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -307,7 +307,8 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // Set up screen term->vts = vterm_obtain_screen(term->vt); vterm_screen_enable_altscreen(term->vts, true); - vterm_screen_enable_reflow(term->vts, true); + // TODO(clason): reenable when https://github.com/neovim/neovim/issues/23762 is fixed + // vterm_screen_enable_reflow(term->vts, true); // delete empty lines at the end of the buffer vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term); vterm_screen_set_unrecognised_fallbacks(term->vts, &vterm_fallbacks, term); @@ -612,7 +613,7 @@ static void terminal_check_cursor(void) row_to_linenr(term, term->cursor.row)); // Nudge cursor when returning to normal-mode. int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); - coladvance(MAX(0, term->cursor.col + off)); + coladvance(curwin, MAX(0, term->cursor.col + off)); } // Function executed before each iteration of terminal mode. @@ -626,7 +627,7 @@ static int terminal_check(VimState *state) } terminal_check_cursor(); - validate_cursor(); + validate_cursor(curwin); if (must_redraw) { update_screen(); diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index bfe3ed5972..c427206764 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -86,8 +86,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on // When 'ai' is off we don't want a space under the cursor to be // deleted. Replace it with an 'x' temporarily. - if (!curbuf->b_p_ai - && !(State & VREPLACE_FLAG)) { + if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG)) { cc = gchar_cursor(); if (ascii_iswhite(cc)) { save_char = (char)cc; @@ -158,7 +157,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on } // find column of textwidth border - coladvance((colnr_T)textwidth); + coladvance(curwin, (colnr_T)textwidth); wantcol = curwin->w_cursor.col; // If startcol is large (a long line), formatting takes too much @@ -446,7 +445,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on // Check if cursor is not past the NUL off the line, cindent // may have added or removed indent. curwin->w_cursor.col += startcol; - colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -507,12 +506,11 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char **leader_flags, bo static bool ends_in_white(linenr_T lnum) { char *s = ml_get(lnum); - size_t l; if (*s == NUL) { return false; } - l = strlen(s) - 1; + colnr_T l = ml_get_len(lnum) - 1; return ascii_iswhite((uint8_t)s[l]); } @@ -545,7 +543,7 @@ static bool same_leader(linenr_T lnum, int leader1_len, char *leader1_flags, int return false; } if (*p == COM_START) { - int line_len = (int)strlen(ml_get(lnum)); + int line_len = ml_get_len(lnum); if (line_len <= leader1_len) { return false; } @@ -648,7 +646,7 @@ void auto_format(bool trailblank, bool prev_line) // in 'formatoptions' and there is a single character before the cursor. // Otherwise the line would be broken and when typing another non-white // next they are not joined back together. - int wasatend = (pos.col == (colnr_T)strlen(old)); + bool wasatend = (pos.col == get_cursor_line_len()); if (*old != NUL && !trailblank && wasatend) { dec_cursor(); int cc = gchar_cursor(); @@ -691,9 +689,9 @@ void auto_format(bool trailblank, bool prev_line) if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { // "cannot happen" curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { - check_cursor_col(); + check_cursor_col(curwin); } // Insert mode: If the cursor is now after the end of the line while it @@ -702,7 +700,7 @@ void auto_format(bool trailblank, bool prev_line) // formatted. if (!wasatend && has_format_option(FO_WHITE_PAR)) { char *linep = get_cursor_line_ptr(); - colnr_T len = (colnr_T)strlen(linep); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col == len) { char *plinep = xstrnsave(linep, (size_t)len + 2); plinep[len] = ' '; @@ -716,7 +714,7 @@ void auto_format(bool trailblank, bool prev_line) } } - check_cursor(); + check_cursor(curwin); } /// When an extra space was added to continue a paragraph for auto-formatting, @@ -840,7 +838,7 @@ void op_format(oparg_T *oap, bool keep_cursor) saved_cursor.lnum = 0; // formatting may have made the cursor position invalid - check_cursor(); + check_cursor(curwin); } if (oap->is_VIsual) { @@ -1064,7 +1062,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) // put cursor on last non-space State = MODE_NORMAL; // don't go past end-of-line - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) { dec_cursor(); } @@ -1120,7 +1118,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) } first_par_line = false; // If the line is getting long, format it next time - if (strlen(get_cursor_line_ptr()) > (size_t)max_len) { + if (get_cursor_line_len() > max_len) { force_format = true; } else { force_format = false; diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index d9c2b3b111..3c81a840ea 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -187,7 +187,7 @@ bool findpar(bool *pincl, int dir, int count, int what, bool both) // skip folded lines fold_skipped = false; - if (first && hasFolding(curr, &fold_first, &fold_last)) { + if (first && hasFolding(curwin, curr, &fold_first, &fold_last)) { curr = ((dir > 0) ? fold_last : fold_first) + dir; fold_skipped = true; } @@ -218,7 +218,7 @@ bool findpar(bool *pincl, int dir, int count, int what, bool both) // Put the cursor on the last character in the last line and make the // motion inclusive. - if ((curwin->w_cursor.col = (colnr_T)strlen(line)) != 0) { + if ((curwin->w_cursor.col = ml_get_len(curr)) != 0) { curwin->w_cursor.col--; curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col); *pincl = true; @@ -318,8 +318,8 @@ int fwd_word(int count, bool bigword, bool eol) while (--count >= 0) { // When inside a range of folded lines, move to the last char of the // last line. - if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - coladvance(MAXCOL); + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { + coladvance(curwin, MAXCOL); } int sclass = cls(); // starting class @@ -374,7 +374,7 @@ int bck_word(int count, bool bigword, bool stop) while (--count >= 0) { // When inside a range of folded lines, move to the first char of the // first line. - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.col = 0; } sclass = cls(); @@ -431,8 +431,8 @@ int end_word(int count, bool bigword, bool stop, bool empty) while (--count >= 0) { // When inside a range of folded lines, move to the last char of the // last line. - if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - coladvance(MAXCOL); + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { + coladvance(curwin, MAXCOL); } sclass = cls(); if (inc_cursor() == -1) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 7fae34d33f..c332c17e43 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1643,7 +1643,7 @@ static void invalidate(TUIData *tui, int top, int bot, int left, int right) static void ensure_space_buf_size(TUIData *tui, size_t len) { if (len > tui->space_buf_len) { - tui->space_buf = xrealloc(tui->space_buf, len * sizeof *tui->space_buf); + tui->space_buf = xrealloc(tui->space_buf, len); memset(tui->space_buf + tui->space_buf_len, ' ', len - tui->space_buf_len); tui->space_buf_len = len; } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 3708a87ddf..bddcf98b53 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -53,7 +53,7 @@ typedef struct { #define MAX_UI_COUNT 16 -static UI *uis[MAX_UI_COUNT]; +static RemoteUI *uis[MAX_UI_COUNT]; static bool ui_ext[kUIExtCount] = { 0 }; static size_t ui_count = 0; static int ui_mode_idx = SHAPE_IDX_N; @@ -108,7 +108,7 @@ static void ui_log(const char *funname) do { \ bool any_call = false; \ for (size_t i = 0; i < ui_count; i++) { \ - UI *ui = uis[i]; \ + RemoteUI *ui = uis[i]; \ if ((cond)) { \ remote_ui_##funname(__VA_ARGS__); \ any_call = true; \ @@ -212,7 +212,7 @@ void ui_refresh(void) bool inclusive = ui_override(); for (size_t i = 0; i < ui_count; i++) { - UI *ui = uis[i]; + RemoteUI *ui = uis[i]; width = MIN(ui->width, width); height = MIN(ui->height, height); for (UIExtension j = 0; (int)j < kUIExtCount; j++) { @@ -367,12 +367,11 @@ void vim_beep(unsigned val) void do_autocmd_uienter_all(void) { for (size_t i = 0; i < ui_count; i++) { - UIData *data = uis[i]->data; - do_autocmd_uienter(data->channel_id, true); + do_autocmd_uienter(uis[i]->channel_id, true); } } -void ui_attach_impl(UI *ui, uint64_t chanid) +void ui_attach_impl(RemoteUI *ui, uint64_t chanid) { if (ui_count == MAX_UI_COUNT) { abort(); @@ -408,7 +407,7 @@ void ui_attach_impl(UI *ui, uint64_t chanid) do_autocmd_uienter(chanid, true); } -void ui_detach_impl(UI *ui, uint64_t chanid) +void ui_detach_impl(RemoteUI *ui, uint64_t chanid) { size_t shift_index = MAX_UI_COUNT; @@ -444,7 +443,7 @@ void ui_detach_impl(UI *ui, uint64_t chanid) do_autocmd_uienter(chanid, false); } -void ui_set_ext_option(UI *ui, UIExtension ext, bool active) +void ui_set_ext_option(RemoteUI *ui, UIExtension ext, bool active) { if (ext < kUIGlobalCount) { ui_refresh(); @@ -649,7 +648,7 @@ Array ui_array(Arena *arena) { Array all_uis = arena_array(arena, ui_count); for (size_t i = 0; i < ui_count; i++) { - UI *ui = uis[i]; + RemoteUI *ui = uis[i]; Dictionary info = arena_dict(arena, 10 + kUIExtCount); PUT_C(info, "width", INTEGER_OBJ(ui->width)); PUT_C(info, "height", INTEGER_OBJ(ui->height)); @@ -671,7 +670,7 @@ Array ui_array(Arena *arena) PUT_C(info, (char *)ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); } } - PUT_C(info, "chan", INTEGER_OBJ((Integer)ui->data->channel_id)); + PUT_C(info, "chan", INTEGER_OBJ((Integer)ui->channel_id)); ADD_C(all_uis, DICTIONARY_OBJ(info)); } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 2bb5ee16b3..4f36cae4b2 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -62,6 +62,9 @@ uint64_t ui_client_start_server(int argc, char **argv) CALLBACK_READER_INIT, on_err, CALLBACK_NONE, false, true, true, false, kChannelStdinPipe, NULL, 0, 0, NULL, &exit_status); + if (!channel) { + return 0; + } // If stdin is not a pty, it is forwarded to the client. // Replace stdin in the TUI process with the tty fd. diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 85e7d63e11..6e89894e0b 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -79,13 +79,13 @@ void ui_comp_syn_init(void) dbghl_recompose = syn_check_group(S_LEN("RedrawDebugRecompose")); } -void ui_comp_attach(UI *ui) +void ui_comp_attach(RemoteUI *ui) { composed_uis++; ui->composed = true; } -void ui_comp_detach(UI *ui) +void ui_comp_detach(RemoteUI *ui) { composed_uis--; if (composed_uis == 0) { diff --git a/src/nvim/ui_defs.h b/src/nvim/ui_defs.h index a2071782b6..4d73cc2321 100644 --- a/src/nvim/ui_defs.h +++ b/src/nvim/ui_defs.h @@ -5,6 +5,7 @@ #include <stdint.h> #include "nvim/api/private/defs.h" +#include "nvim/msgpack_rpc/packer_defs.h" /// Keep in sync with ui_ext_names[] in ui.h typedef enum { @@ -29,25 +30,41 @@ enum { typedef int LineFlags; -typedef struct ui_t UI; - typedef struct { + bool rgb; + bool override; ///< Force highest-requested UI capabilities. + bool composed; + bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities. + int width; + int height; + int pum_nlines; /// actual nr. lines shown in PUM + bool pum_pos; /// UI reports back pum position? + double pum_row; + double pum_col; + double pum_height; + double pum_width; + + // TUI fields. + char *term_name; + char *term_background; ///< Deprecated. No longer needed since background color detection happens + ///< in Lua. To be removed in a future release. + int term_colors; + bool stdin_tty; + bool stdout_tty; + uint64_t channel_id; -#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. +#define UI_BUF_SIZE ARENA_BLOCK_SIZE ///< total buffer size for pending msgpack data. /// guaranteed size available for each new event (so packing of simple events /// and the header of grid_line will never fail) #define EVENT_BUF_SIZE 256 - char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data - char *buf_wptr; ///< write head of buffer + +// Fields related to packing + PackerBuffer packer; + const char *cur_event; ///< name of current event (might get multiple arglists) Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) - // state for write_cb, while packing a single arglist to msgpack. This - // might fail due to buffer overflow. - size_t pack_totlen; - char *temp_buf; - // We start packing the two outermost msgpack arrays before knowing the total // number of elements. Thus track the location where array size will need // to be written in the msgpack buffer, once the specific array is finished. @@ -65,33 +82,7 @@ typedef struct { // Position of legacy cursor, used both for drawing and visible user cursor. Integer client_row, client_col; bool wildmenu_active; -} UIData; - -struct ui_t { - bool rgb; - bool override; ///< Force highest-requested UI capabilities. - bool composed; - bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities. - int width; - int height; - int pum_nlines; /// actual nr. lines shown in PUM - bool pum_pos; /// UI reports back pum position? - double pum_row; - double pum_col; - double pum_height; - double pum_width; - - // TUI fields. - char *term_name; - char *term_background; ///< Deprecated. No longer needed since background color detection happens - ///< in Lua. To be removed in a future release. - int term_colors; - bool stdin_tty; - bool stdout_tty; - - // TODO(bfredl): integrate into struct! - UIData data[1]; -}; +} RemoteUI; typedef struct { const char *name; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index e9170ba858..ba720c9f6a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -477,7 +477,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, bool r uhp->uh_entry = NULL; uhp->uh_getbot_entry = NULL; uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { uhp->uh_cursor_vcol = getviscol(); } else { uhp->uh_cursor_vcol = -1; @@ -2488,8 +2488,8 @@ static void u_undoredo(bool undo, bool do_buf_event) if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) { if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) { curwin->w_cursor.col = curhead->uh_cursor.col; - if (virtual_active() && curhead->uh_cursor_vcol >= 0) { - coladvance(curhead->uh_cursor_vcol); + if (virtual_active(curwin) && curhead->uh_cursor_vcol >= 0) { + coladvance(curwin, curhead->uh_cursor_vcol); } else { curwin->w_cursor.coladd = 0; } @@ -2506,7 +2506,7 @@ static void u_undoredo(bool undo, bool do_buf_event) } // Make sure the cursor is on an existing line and column. - check_cursor(); + check_cursor(curwin); // Remember where we are for "g-" and ":earlier 10s". curbuf->b_u_seq_cur = curhead->uh_seq; @@ -3073,7 +3073,7 @@ void u_undoline(void) } curwin->w_cursor.col = t; curwin->w_cursor.lnum = curbuf->b_u_line_lnum; - check_cursor_col(); + check_cursor_col(curwin); } /// Allocate memory and copy curbuf line into it. diff --git a/src/nvim/version.c b/src/nvim/version.c index 038c9701bf..ff6f7835b2 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2710,15 +2710,13 @@ void list_version(void) : "\nRun \":verbose version\" for more info")); } -/// Show the intro message when not editing a file. -void maybe_intro_message(void) +/// Whether it still is not too late to show an intro message +bool may_show_intro(void) { - if (buf_is_empty(curbuf) - && (curbuf->b_fname == NULL) - && (firstwin->w_next == NULL) - && (vim_strchr(p_shm, SHM_INTRO) == NULL)) { - intro_message(false); - } + return (buf_is_empty(curbuf) + && (curbuf->b_fname == NULL) + && (firstwin->w_next == NULL) + && (vim_strchr(p_shm, SHM_INTRO) == NULL)); } /// Give an introductory message about Vim. diff --git a/src/nvim/window.c b/src/nvim/window.c index b631cbe7c8..c9c2124730 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -133,6 +133,35 @@ static void log_frame_layout(frame_T *frame) } #endif +/// Check if the current window is allowed to move to a different buffer. +/// +/// @return If the window has 'winfixbuf', or this function will return false. +bool check_can_set_curbuf_disabled(void) +{ + if (curwin->w_p_wfb) { + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return false; + } + + return true; +} + +/// Check if the current window is allowed to move to a different buffer. +/// +/// @param forceit If true, do not error. If false and 'winfixbuf' is enabled, error. +/// +/// @return If the window has 'winfixbuf', then forceit must be true +/// or this function will return false. +bool check_can_set_curbuf_forceit(int forceit) +{ + if (!forceit && curwin->w_p_wfb) { + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return false; + } + + return true; +} + /// @return the current window, unless in the cmdline window and "prevwin" is /// set, then return "prevwin". win_T *prevwin_curwin(void) @@ -455,9 +484,14 @@ newwindow: case 'H': case 'L': CHECK_CMDWIN; - win_totop(Prenum, - ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) - | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); + if (one_window(curwin)) { + beep_flush(); + } else { + const int dir = ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) + | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT); + + win_splitmove(curwin, Prenum, dir); + } break; // make all windows the same width and/or height @@ -592,7 +626,7 @@ wingotofile: ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, Prenum == 0, - type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); + type, Prenum1, ACTION_SPLIT, 1, MAXLNUM, false); xfree(ptr); curwin->w_set_curswant = true; break; @@ -718,6 +752,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) kErrorTypeException, "Failed to switch to window %d", win->handle); + goto cleanup; } try_start(); @@ -729,10 +764,11 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) buf->handle); } - // If window is not current, state logic will not validate its cursor. - // So do it now. - validate_cursor(); + // If window is not current, state logic will not validate its cursor. So do it now. + // Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set). + validate_cursor(curwin); +cleanup: restore_win_noblock(&switchwin, true); if (noautocmd) { unblock_autocmds(); @@ -817,9 +853,10 @@ void ui_ext_win_position(win_T *wp, bool validate) int comp_row = (int)row - (south ? wp->w_height_outer : 0); int comp_col = (int)col - (east ? wp->w_width_outer : 0); + int above_ch = wp->w_config.zindex < kZIndexMessages ? (int)p_ch : 0; comp_row += grid->comp_row; comp_col += grid->comp_col; - comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0); + comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - above_ch), 0); if (!c.fixed || east) { comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); } @@ -902,19 +939,35 @@ void ui_ext_win_viewport(win_T *wp) } } -/// If "split_disallowed" is set give an error and return FAIL. +/// If "split_disallowed" is set, or "wp"'s buffer is closing, give an error and return FAIL. /// Otherwise return OK. -static int check_split_disallowed(void) +int check_split_disallowed(const win_T *wp) + FUNC_ATTR_NONNULL_ALL +{ + Error err = ERROR_INIT; + const bool ok = check_split_disallowed_err(wp, &err); + if (ERROR_SET(&err)) { + emsg(_(err.msg)); + api_clear_error(&err); + } + return ok ? OK : FAIL; +} + +/// Like `check_split_disallowed`, but set `err` to the (untranslated) error message on failure and +/// return false. Otherwise return true. +/// @see check_split_disallowed +bool check_split_disallowed_err(const win_T *wp, Error *err) + FUNC_ATTR_NONNULL_ALL { if (split_disallowed > 0) { - emsg(_("E242: Can't split a window while closing another")); - return FAIL; + api_set_error(err, kErrorTypeException, "E242: Can't split a window while closing another"); + return false; } - if (curwin->w_buffer->b_locked_split) { - emsg(_(e_cannot_split_window_when_closing_buffer)); - return FAIL; + if (wp->w_buffer->b_locked_split) { + api_set_error(err, kErrorTypeException, "%s", e_cannot_split_window_when_closing_buffer); + return false; } - return OK; + return true; } // split the current window, implements CTRL-W s and :split @@ -933,7 +986,7 @@ static int check_split_disallowed(void) // return FAIL for failure, OK otherwise int win_split(int size, int flags) { - if (check_split_disallowed() == FAIL) { + if (check_split_disallowed(curwin) == FAIL) { return FAIL; } @@ -957,14 +1010,19 @@ int win_split(int size, int flags) clear_snapshot(curtab, SNAP_HELP_IDX); } - return win_split_ins(size, flags, NULL, 0) == NULL ? FAIL : OK; + return win_split_ins(size, flags, NULL, 0, NULL) == NULL ? FAIL : OK; } /// When "new_wp" is NULL: split the current window in two. /// When "new_wp" is not NULL: insert this window at the far /// top/left/right/bottom. +/// When "to_flatten" is not NULL: flatten this frame before reorganising frames; +/// remains unflattened on failure. +/// +/// On failure, if "new_wp" was not NULL, no changes will have been made to the +/// window layout or sizes. /// @return NULL for failure, or pointer to new window -win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) +win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_flatten) { win_T *wp = new_wp; @@ -985,13 +1043,12 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) int need_status = 0; int new_size = size; - bool new_in_layout = (new_wp == NULL || new_wp->w_floating); bool vertical = flags & WSP_VERT; bool toplevel = flags & (WSP_TOP | WSP_BOT); // add a status line when p_ls == 1 and splitting the first window - if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) { - if (oldwin->w_height <= p_wmh && new_in_layout) { + if (one_window(firstwin) && p_ls == 1 && oldwin->w_status_height == 0) { + if (oldwin->w_height <= p_wmh) { emsg(_(e_noroom)); return NULL; } @@ -1040,7 +1097,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_width; needed += minwidth; } - if (available < needed && new_in_layout) { + if (available < needed) { emsg(_(e_noroom)); return NULL; } @@ -1120,7 +1177,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_height; needed += minheight; } - if (available < needed && new_in_layout) { + if (available < needed) { emsg(_(e_noroom)); return NULL; } @@ -1190,13 +1247,13 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) if (new_wp == NULL) { wp = win_alloc(oldwin, false); } else { - win_append(oldwin, wp); + win_append(oldwin, wp, NULL); } } else { if (new_wp == NULL) { wp = win_alloc(oldwin->w_prev, false); } else { - win_append(oldwin->w_prev, wp); + win_append(oldwin->w_prev, wp, NULL); } } @@ -1210,13 +1267,37 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) // make the contents of the new window the same as the current one win_init(wp, curwin, flags); } else if (wp->w_floating) { - new_frame(wp); + ui_comp_remove_grid(&wp->w_grid_alloc); + if (ui_has(kUIMultigrid)) { + wp->w_pos_changed = true; + } else { + // No longer a float, a non-multigrid UI shouldn't draw it as such + ui_call_win_hide(wp->w_grid_alloc.handle); + win_free_grid(wp, true); + } + + // External windows are independent of tabpages, and may have been the curwin of others. + if (wp->w_config.external) { + FOR_ALL_TABS(tp) { + if (tp != curtab && tp->tp_curwin == wp) { + tp->tp_curwin = tp->tp_firstwin; + } + } + } + wp->w_floating = false; + new_frame(wp); + // non-floating window doesn't store float config or have a border. wp->w_config = WIN_CONFIG_INIT; CLEAR_FIELD(wp->w_border_adj); } + // Going to reorganize frames now, make sure they're flat. + if (to_flatten != NULL) { + frame_flatten(to_flatten); + } + bool before; frame_T *curfrp; @@ -1452,7 +1533,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) if (!(flags & WSP_NOENTER)) { // make the new window the current window - win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS + win_enter_ext(wp, (new_wp == NULL ? WEE_TRIGGER_NEW_AUTOCMDS : 0) | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS); } if (vertical) { @@ -1689,7 +1770,7 @@ static void win_exchange(int Prenum) return; } - if (firstwin == curwin && lastwin_nofloating() == curwin) { + if (one_window(curwin)) { // just one window beep_flush(); return; @@ -1731,13 +1812,13 @@ static void win_exchange(int Prenum) if (wp->w_prev != curwin) { win_remove(curwin, NULL); frame_remove(curwin->w_frame); - win_append(wp->w_prev, curwin); + win_append(wp->w_prev, curwin, NULL); frame_insert(frp, curwin->w_frame); } if (wp != wp2) { win_remove(wp, NULL); frame_remove(wp->w_frame); - win_append(wp2, wp); + win_append(wp2, wp, NULL); if (frp2 == NULL) { frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame); } else { @@ -1781,7 +1862,7 @@ static void win_rotate(bool upwards, int count) return; } - if (count <= 0 || (firstwin == curwin && lastwin_nofloating() == curwin)) { + if (count <= 0 || one_window(curwin)) { // nothing to do beep_flush(); return; @@ -1811,7 +1892,7 @@ static void win_rotate(bool upwards, int count) // find last frame and append removed window/frame after it for (; frp->fr_next != NULL; frp = frp->fr_next) {} - win_append(frp->fr_win, wp1); + win_append(frp->fr_win, wp1, NULL); frame_append(frp, wp1->w_frame); wp2 = frp->fr_win; // previously last window @@ -1826,7 +1907,7 @@ static void win_rotate(bool upwards, int count) assert(frp->fr_parent->fr_child); // append the removed window/frame before the first in the list - win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1); + win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1, NULL); frame_insert(frp->fr_parent->fr_child, frp); } @@ -1855,48 +1936,57 @@ static void win_rotate(bool upwards, int count) redraw_all_later(UPD_NOT_VALID); } -// Move the current window to the very top/bottom/left/right of the screen. -static void win_totop(int size, int flags) +/// Move "wp" into a new split in a given direction, possibly relative to the +/// current window. +/// "wp" must be valid in the current tabpage. +/// Returns FAIL for failure, OK otherwise. +int win_splitmove(win_T *wp, int size, int flags) { int dir = 0; - int height = curwin->w_height; + int height = wp->w_height; - if (firstwin == curwin && lastwin_nofloating() == curwin) { - beep_flush(); - return; + if (one_window(wp)) { + return OK; // nothing to do } - if (is_aucmd_win(curwin)) { - return; + if (is_aucmd_win(wp) || check_split_disallowed(wp) == FAIL) { + return FAIL; } - if (check_split_disallowed() == FAIL) { - return; + + frame_T *unflat_altfr = NULL; + if (wp->w_floating) { + win_remove(wp, NULL); + } else { + // Remove the window and frame from the tree of frames. Don't flatten any + // frames yet so we can restore things if win_split_ins fails. + winframe_remove(wp, &dir, NULL, &unflat_altfr); + win_remove(wp, NULL); + last_status(false); // may need to remove last status line + win_comp_pos(); // recompute window positions } - if (curwin->w_floating) { - ui_comp_remove_grid(&curwin->w_grid_alloc); - if (ui_has(kUIMultigrid)) { - curwin->w_pos_changed = true; - } else { - // No longer a float, a non-multigrid UI shouldn't draw it as such - ui_call_win_hide(curwin->w_grid_alloc.handle); - win_free_grid(curwin, true); + // Split a window on the desired side and put "wp" there. + if (win_split_ins(size, flags, wp, dir, unflat_altfr) == NULL) { + if (!wp->w_floating) { + // win_split_ins doesn't change sizes or layout if it fails to insert an + // existing window, so just undo winframe_remove. + winframe_restore(wp, dir, unflat_altfr); } - } else { - // Remove the window and frame from the tree of frames. - winframe_remove(curwin, &dir, NULL); + win_append(wp->w_prev, wp, NULL); + return FAIL; } - win_remove(curwin, NULL); - last_status(false); // may need to remove last status line - win_comp_pos(); // recompute window positions - // Split a window on the desired side and put the window there. - win_split_ins(size, flags, curwin, dir); - if (!(flags & WSP_VERT)) { - win_setheight(height); + // If splitting horizontally, try to preserve height. + // Note that win_split_ins autocommands may have immediately closed "wp", or made it floating! + if (size == 0 && !(flags & WSP_VERT) && win_valid(wp) && !wp->w_floating) { + win_setheight_win(height, wp); if (p_ea) { - win_equal(curwin, true, 'v'); + // Equalize windows. Note that win_split_ins autocommands may have + // made a window other than "wp" current. + win_equal(curwin, curwin == wp, 'v'); } } + + return OK; } // Move window "win1" to below/right of "win2" and make "win1" the current @@ -1954,7 +2044,7 @@ void win_move_after(win_T *win1, win_T *win2) } win_remove(win1, NULL); frame_remove(win1->w_frame); - win_append(win2, win1); + win_append(win2, win1, NULL); frame_append(win2->w_frame, win1->w_frame); win_comp_pos(); // recompute w_winrow for all windows @@ -2433,37 +2523,13 @@ bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT } /// Check if "win" is the only non-floating window in the current tabpage. -bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (win->w_floating) { - return false; - } - - bool seen_one = false; - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (!wp->w_floating) { - if (seen_one) { - return false; - } - seen_one = true; - } - } - return true; -} - -/// Like ONE_WINDOW but only considers non-floating windows -bool one_nonfloat(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - return firstwin->w_next == NULL || firstwin->w_next->w_floating; -} - -/// if wp is the last non-floating window /// -/// always false for a floating window -bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// This should be used in place of ONE_WINDOW when necessary, +/// with "firstwin" or the affected window as argument depending on the situation. +bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating); + assert(!firstwin->w_floating); + return firstwin == win && (win->w_next == NULL || win->w_next->w_floating); } /// Check if floating windows in the current tab can be closed. @@ -2508,7 +2574,7 @@ bool can_close_in_cmdwin(win_T *win, Error *err) /// @param prev_curtab previous tabpage that will be closed if "win" is the /// last window in the tabpage /// -/// @return true when the window was closed already. +/// @return false if there are other windows and nothing is done, true otherwise. static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab) FUNC_ATTR_NONNULL_ARG(1) { @@ -2532,10 +2598,6 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev // that below. goto_tabpage_tp(alt_tabpage(), false, true); - // save index for tabclosed event - char prev_idx[NUMBUFLEN]; - snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(prev_curtab)); - // Safety check: Autocommands may have closed the window when jumping // to the other tab page. if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win) { @@ -2629,6 +2691,9 @@ int win_close(win_T *win, bool free_buf, bool force) return FAIL; } } + if (!win_valid_any_tab(win)) { + return FAIL; // window already closed by autocommands + } } else { emsg(e_floatonly); return FAIL; @@ -2713,10 +2778,8 @@ int win_close(win_T *win, bool free_buf, bool force) win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, true); - if (only_one_window() && win_valid(win) && win->w_buffer == NULL - && (last_window(win) || curtab != prev_curtab - || close_last_window_tabpage(win, free_buf, prev_curtab)) - && !win->w_floating) { + if (win_valid(win) && win->w_buffer == NULL + && !win->w_floating && last_window(win)) { // Autocommands have closed all windows, quit now. Restore // curwin->w_buffer, otherwise writing ShaDa file may fail. if (curwin->w_buffer == NULL) { @@ -2728,10 +2791,7 @@ int win_close(win_T *win, bool free_buf, bool force) if (curtab != prev_curtab && win_valid_any_tab(win) && win->w_buffer == NULL) { // Need to close the window anyway, since the buffer is NULL. - // Don't trigger autocmds with a NULL buffer. - block_autocmds(); win_close_othertab(win, false, prev_curtab); - unblock_autocmds(); return FAIL; } @@ -2758,13 +2818,10 @@ int win_close(win_T *win, bool free_buf, bool force) ui_comp_remove_grid(&win->w_grid_alloc); assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer" if (win->w_config.external) { - for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - if (tp == curtab) { - continue; - } - if (tp->tp_curwin == win) { + FOR_ALL_TABS(tp) { + if (tp != curtab && tp->tp_curwin == win) { // NB: an autocmd can still abort the closing of this window, - // bur carring out this change anyway shouldn't be a catastrophe. + // but carrying out this change anyway shouldn't be a catastrophe. tp->tp_curwin = tp->tp_firstwin; } } @@ -2815,7 +2872,7 @@ int win_close(win_T *win, bool free_buf, bool force) // The cursor position may be invalid if the buffer changed after last // using the window. - check_cursor(); + check_cursor(curwin); } if (!was_floating) { @@ -2905,10 +2962,14 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) } // Fire WinClosed just before starting to free window-related resources. - do_autocmd_winclosed(win); - // autocmd may have freed the window already. - if (!win_valid_any_tab(win)) { - return; + // If the buffer is NULL, it isn't safe to trigger autocommands, + // and win_close() should have already triggered WinClosed. + if (win->w_buffer != NULL) { + do_autocmd_winclosed(win); + // autocmd may have freed the window already. + if (!win_valid_any_tab(win)) { + return; + } } if (win->w_buffer != NULL) { @@ -3005,24 +3066,11 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) if (!win->w_floating) { // Remove the window and its frame from the tree of frames. frame_T *frp = win->w_frame; - wp = winframe_remove(win, dirp, tp); + wp = winframe_remove(win, dirp, tp, NULL); xfree(frp); } else { *dirp = 'h'; // Dummy value. - if (tp == NULL) { - if (win_valid(prevwin) && prevwin != win) { - wp = prevwin; - } else { - wp = firstwin; - } - } else { - assert(tp != curtab); - if (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) { - wp = tp->tp_prevwin; - } else { - wp = tp->tp_firstwin; - } - } + wp = win_float_find_altwin(win, tp); } win_free(win, tp); @@ -3086,9 +3134,64 @@ void win_free_all(void) /// /// @param dirp set to 'v' or 'h' for direction if 'ea' /// @param tp tab page "win" is in, NULL for current +/// @param unflat_altfr if not NULL, set to pointer of frame that got +/// the space, and it is not flattened /// /// @return a pointer to the window that got the freed up space. -win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) +win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp, frame_T **unflat_altfr) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + frame_T *altfr; + win_T *wp = winframe_find_altwin(win, dirp, tp, &altfr); + if (wp == NULL) { + return NULL; + } + + frame_T *frp_close = win->w_frame; + + // Save the position of the containing frame (which will also contain the + // altframe) before we remove anything, to recompute window positions later. + const win_T *const topleft = frame2win(frp_close->fr_parent); + int row = topleft->w_winrow; + int col = topleft->w_wincol; + + // Remove this frame from the list of frames. + frame_remove(frp_close); + + if (*dirp == 'v') { + frame_new_height(altfr, altfr->fr_height + frp_close->fr_height, + altfr == frp_close->fr_next, false); + } else { + assert(*dirp == 'h'); + frame_new_width(altfr, altfr->fr_width + frp_close->fr_width, + altfr == frp_close->fr_next, false); + } + + // If the altframe wasn't adjacent and left/above, resizing it will have + // changed window positions within the parent frame. Recompute them. + if (altfr != frp_close->fr_prev) { + frame_comp_pos(frp_close->fr_parent, &row, &col); + } + + if (unflat_altfr == NULL) { + frame_flatten(altfr); + } else { + *unflat_altfr = altfr; + } + + return wp; +} + +/// Find the window that will get the freed space from a call to `winframe_remove`. +/// Makes no changes to the window layout. +/// +/// @param dirp set to 'v' or 'h' for the direction where "altfr" will be resized +/// to fill the space +/// @param tp tab page "win" is in, NULL for current +/// @param altfr if not NULL, set to pointer of frame that will get the space +/// +/// @return a pointer to the window that will get the freed up space. +win_T *winframe_find_altwin(win_T *win, int *dirp, tabpage_T *tp, frame_T **altfr) FUNC_ATTR_NONNULL_ARG(1, 2) { assert(tp == NULL || tp != curtab); @@ -3100,13 +3203,10 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) frame_T *frp_close = win->w_frame; - // Remove the window from its frame. + // Find the window and frame that gets the space. frame_T *frp2 = win_altframe(win, tp); win_T *wp = frame2win(frp2); - // Remove this frame from the list of frames. - frame_remove(frp_close); - if (frp_close->fr_parent->fr_layout == FR_COL) { // When 'winfixheight' is set, try to find another frame in the column // (as close to the closed frame as possible) to distribute the height @@ -3133,8 +3233,6 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) } } } - frame_new_height(frp2, frp2->fr_height + frp_close->fr_height, - frp2 == frp_close->fr_next, false); *dirp = 'v'; } else { // When 'winfixwidth' is set, try to find another frame in the column @@ -3162,70 +3260,123 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) } } } - frame_new_width(frp2, frp2->fr_width + frp_close->fr_width, - frp2 == frp_close->fr_next, false); *dirp = 'h'; } - // If rows/columns go to a window below/right its positions need to be - // updated. Can only be done after the sizes have been updated. - if (frp2 == frp_close->fr_next) { - int row = win->w_winrow; - int col = win->w_wincol; + assert(wp != win && frp2 != frp_close); + if (altfr != NULL) { + *altfr = frp2; + } + + return wp; +} - frame_comp_pos(frp2, &row, &col); +/// Flatten "frp" into its parent frame if it's the only child, also merging its +/// list with the grandparent if they share the same layout. +/// Frees "frp" if flattened; also "frp->fr_parent" if it has the same layout. +static void frame_flatten(frame_T *frp) + FUNC_ATTR_NONNULL_ALL +{ + if (frp->fr_next != NULL || frp->fr_prev != NULL) { + return; } - if (frp2->fr_next == NULL && frp2->fr_prev == NULL) { - // There is no other frame in this list, move its info to the parent - // and remove it. - frp2->fr_parent->fr_layout = frp2->fr_layout; - frp2->fr_parent->fr_child = frp2->fr_child; - frame_T *frp; - FOR_ALL_FRAMES(frp, frp2->fr_child) { - frp->fr_parent = frp2->fr_parent; - } - frp2->fr_parent->fr_win = frp2->fr_win; - if (frp2->fr_win != NULL) { - frp2->fr_win->w_frame = frp2->fr_parent; + // There is no other frame in this list, move its info to the parent + // and remove it. + frp->fr_parent->fr_layout = frp->fr_layout; + frp->fr_parent->fr_child = frp->fr_child; + frame_T *frp2; + FOR_ALL_FRAMES(frp2, frp->fr_child) { + frp2->fr_parent = frp->fr_parent; + } + frp->fr_parent->fr_win = frp->fr_win; + if (frp->fr_win != NULL) { + frp->fr_win->w_frame = frp->fr_parent; + } + frp2 = frp->fr_parent; + if (topframe->fr_child == frp) { + topframe->fr_child = frp2; + } + xfree(frp); + + frp = frp2->fr_parent; + if (frp != NULL && frp->fr_layout == frp2->fr_layout) { + // The frame above the parent has the same layout, have to merge + // the frames into this list. + if (frp->fr_child == frp2) { + frp->fr_child = frp2->fr_child; + } + assert(frp2->fr_child); + frp2->fr_child->fr_prev = frp2->fr_prev; + if (frp2->fr_prev != NULL) { + frp2->fr_prev->fr_next = frp2->fr_child; + } + for (frame_T *frp3 = frp2->fr_child;; frp3 = frp3->fr_next) { + frp3->fr_parent = frp; + if (frp3->fr_next == NULL) { + frp3->fr_next = frp2->fr_next; + if (frp2->fr_next != NULL) { + frp2->fr_next->fr_prev = frp3; + } + break; + } } - frp = frp2->fr_parent; if (topframe->fr_child == frp2) { topframe->fr_child = frp; } xfree(frp2); + } +} - frp2 = frp->fr_parent; - if (frp2 != NULL && frp2->fr_layout == frp->fr_layout) { - // The frame above the parent has the same layout, have to merge - // the frames into this list. - if (frp2->fr_child == frp) { - frp2->fr_child = frp->fr_child; - } - assert(frp->fr_child); - frp->fr_child->fr_prev = frp->fr_prev; - if (frp->fr_prev != NULL) { - frp->fr_prev->fr_next = frp->fr_child; - } - frame_T *frp3; - for (frp3 = frp->fr_child;; frp3 = frp3->fr_next) { - frp3->fr_parent = frp2; - if (frp3->fr_next == NULL) { - frp3->fr_next = frp->fr_next; - if (frp->fr_next != NULL) { - frp->fr_next->fr_prev = frp3; - } - break; - } - } - if (topframe->fr_child == frp) { - topframe->fr_child = frp2; - } - xfree(frp); +/// Undo changes from a prior call to winframe_remove, also restoring lost +/// vertical separators and statuslines, and changed window positions for +/// windows within "unflat_altfr". +/// Caller must ensure no other changes were made to the layout or window sizes! +void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr) + FUNC_ATTR_NONNULL_ALL +{ + frame_T *frp = wp->w_frame; + + // Put "wp"'s frame back where it was. + if (frp->fr_prev != NULL) { + frame_append(frp->fr_prev, frp); + } else { + frame_insert(frp->fr_next, frp); + } + + // Vertical separators to the left may have been lost. Restore them. + if (wp->w_vsep_width == 0 && frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) { + frame_add_vsep(frp->fr_prev); + } + + // Statuslines or horizontal separators above may have been lost. Restore them. + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { + if (global_stl_height() == 0 && wp->w_status_height == 0) { + frame_add_statusline(frp->fr_prev); + } else if (wp->w_hsep_height == 0) { + frame_add_hsep(frp->fr_prev); } } - return wp; + // Restore the lost room that was redistributed to the altframe. Also + // adjusts window sizes to fit restored statuslines/separators, if needed. + if (dir == 'v') { + frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height, + unflat_altfr == frp->fr_next, false); + } else if (dir == 'h') { + frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width, + unflat_altfr == frp->fr_next, false); + } + + // Recompute window positions within the parent frame to restore them. + // Positions were unchanged if the altframe was adjacent and left/above. + if (unflat_altfr != frp->fr_prev) { + const win_T *const topleft = frame2win(frp->fr_parent); + int row = topleft->w_winrow; + int col = topleft->w_wincol; + + frame_comp_pos(frp->fr_parent, &row, &col); + } } /// If 'splitbelow' or 'splitright' is set, the space goes above or to the left @@ -3791,7 +3942,7 @@ void close_others(int message, int forceit) return; } - if (one_nonfloat() && !lastwin->w_floating) { + if (one_window(firstwin) && !lastwin->w_floating) { if (message && !autocmd_busy) { msg(_(m_onlyone), 0); @@ -4330,7 +4481,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab) if (wp->w_floating) { if (wp->w_config.external) { win_remove(wp, old_curtab); - win_append(lastwin_nofloating(), wp); + win_append(lastwin_nofloating(), wp, NULL); } else { ui_comp_remove_grid(&wp->w_grid_alloc); } @@ -4766,19 +4917,15 @@ static void win_enter_ext(win_T *const wp, const int flags) if (wp->w_buffer != curbuf) { buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); } - if (!curwin_invalid) { prevwin = curwin; // remember for CTRL-W p curwin->w_redr_status = true; - } else if (wp == prevwin) { - prevwin = NULL; // don't want it to be the new curwin } - curwin = wp; curbuf = wp->w_buffer; - check_cursor(); - if (!virtual_active()) { + check_cursor(curwin); + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } if (*p_spk == 'c') { @@ -4970,7 +5117,7 @@ win_T *win_alloc(win_T *after, bool hidden) block_autocmds(); // link the window in the window list if (!hidden) { - win_append(after, new_wp); + win_append(after, new_wp, NULL); } new_wp->w_wincol = 0; @@ -5140,21 +5287,29 @@ void win_free_grid(win_T *wp, bool reinit) } } -// Append window "wp" in the window list after window "after". -void win_append(win_T *after, win_T *wp) +/// Append window "wp" in the window list after window "after". +/// +/// @param tp tab page "win" (and "after", if not NULL) is in, NULL for current +void win_append(win_T *after, win_T *wp, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(2) { + assert(tp == NULL || tp != curtab); + + win_T **first = tp == NULL ? &firstwin : &tp->tp_firstwin; + win_T **last = tp == NULL ? &lastwin : &tp->tp_lastwin; + // after NULL is in front of the first - win_T *before = after == NULL ? firstwin : after->w_next; + win_T *before = after == NULL ? *first : after->w_next; wp->w_next = before; wp->w_prev = after; if (after == NULL) { - firstwin = wp; + *first = wp; } else { after->w_next = wp; } if (before == NULL) { - lastwin = wp; + *last = wp; } else { before->w_prev = wp; } @@ -6486,7 +6641,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) } } else if (sline > 0) { while (sline > 0 && lnum > 1) { - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); if (lnum == 1) { // first line in buffer is folded line_size = 1; @@ -6506,7 +6661,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (sline < 0) { // Line we want at top would go off top of screen. Use next // line instead. - hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFolding(wp, lnum, NULL, &lnum); lnum++; wp->w_wrow -= line_size + sline; } else if (sline > 0) { @@ -6547,7 +6702,7 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) if (wp == curwin && *p_spk == 'c') { // w_wrow needs to be valid. When setting 'laststatus' this may // call win_new_height() recursively. - validate_cursor(); + validate_cursor(curwin); } if (wp->w_height_inner != prev_height) { return; // Recursive call already changed the size, bail out. @@ -7057,7 +7212,7 @@ int global_stl_height(void) /// @param morewin pretend there are two or more windows if true. int last_stl_height(bool morewin) { - return (p_ls > 1 || (p_ls == 1 && (morewin || !one_nonfloat()))) ? STATUS_HEIGHT : 0; + return (p_ls > 1 || (p_ls == 1 && (morewin || !one_window(firstwin)))) ? STATUS_HEIGHT : 0; } /// Return the minimal number of rows that is needed on the screen to display diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index 8fe0315230..65d2c1306b 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -55,13 +55,26 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) api_set_error(err, kErrorTypeException, "Cannot change window from different tabpage into float"); return NULL; + } else if (cmdwin_win != NULL && !cmdwin_win->w_floating) { + // cmdwin can't become the only non-float. Check for others. + bool other_nonfloat = false; + for (win_T *wp2 = firstwin; wp2 != NULL && !wp2->w_floating; wp2 = wp2->w_next) { + if (wp2 != wp && wp2 != cmdwin_win) { + other_nonfloat = true; + break; + } + } + if (!other_nonfloat) { + api_set_error(err, kErrorTypeException, "%s", e_cmdwin); + return NULL; + } } int dir; - winframe_remove(wp, &dir, NULL); + winframe_remove(wp, &dir, NULL, NULL); XFREE_CLEAR(wp->w_frame); win_comp_pos(); // recompute window positions win_remove(wp, NULL); - win_append(lastwin_nofloating(), wp); + win_append(lastwin_nofloating(), wp, NULL); } wp->w_floating = true; wp->w_status_height = 0; @@ -306,3 +319,21 @@ win_T *win_float_find_preview(void) } return NULL; } + +/// Select an alternative window to `win` (assumed floating) in tabpage `tp`. +/// +/// Useful for finding a window to switch to if `win` is the current window, but is then closed or +/// moved to a different tabpage. +/// +/// @param tp `win`'s original tabpage, or NULL for current. +win_T *win_float_find_altwin(const win_T *win, const tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (tp == NULL) { + return (win_valid(prevwin) && prevwin != win) ? prevwin : firstwin; + } + + assert(tp != curtab); + return (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) ? tp->tp_prevwin + : tp->tp_firstwin; +} |