aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py2
-rw-r--r--src/nvim/CMakeLists.txt23
-rw-r--r--src/nvim/api/autocmd.c10
-rw-r--r--src/nvim/api/buffer.c40
-rw-r--r--src/nvim/api/command.c26
-rw-r--r--src/nvim/api/extmark.c40
-rw-r--r--src/nvim/api/private/defs.h8
-rw-r--r--src/nvim/api/private/helpers.c11
-rw-r--r--src/nvim/api/tabpage.c6
-rw-r--r--src/nvim/api/ui.c436
-rw-r--r--src/nvim/api/vim.c35
-rw-r--r--src/nvim/api/vimscript.c6
-rw-r--r--src/nvim/api/win_config.c258
-rw-r--r--src/nvim/api/window.c10
-rw-r--r--src/nvim/arglist.c10
-rw-r--r--src/nvim/autocmd.c8
-rw-r--r--src/nvim/buffer.c19
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/change.c45
-rw-r--r--src/nvim/charset.c30
-rw-r--r--src/nvim/cursor.c110
-rw-r--r--src/nvim/decoration.c4
-rw-r--r--src/nvim/diff.c15
-rw-r--r--src/nvim/drawline.c209
-rw-r--r--src/nvim/drawscreen.c32
-rw-r--r--src/nvim/edit.c90
-rw-r--r--src/nvim/eval.c4
-rw-r--r--src/nvim/eval.lua58
-rw-r--r--src/nvim/eval/buffer.c6
-rw-r--r--src/nvim/eval/funcs.c95
-rw-r--r--src/nvim/eval/window.c105
-rw-r--r--src/nvim/event/loop.c10
-rw-r--r--src/nvim/ex_cmds.c51
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_cmds2.c26
-rw-r--r--src/nvim/ex_docmd.c70
-rw-r--r--src/nvim/ex_getln.c10
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/fold.c45
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua3
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua5
-rw-r--r--src/nvim/getchar.c4
-rw-r--r--src/nvim/globals.h7
-rw-r--r--src/nvim/highlight.c2
-rw-r--r--src/nvim/highlight_group.c8
-rw-r--r--src/nvim/indent.c4
-rw-r--r--src/nvim/insexpand.c15
-rw-r--r--src/nvim/lua/converter.c6
-rw-r--r--src/nvim/lua/executor.c6
-rw-r--r--src/nvim/lua/stdlib.c6
-rw-r--r--src/nvim/lua/treesitter.c2
-rw-r--r--src/nvim/main.c7
-rw-r--r--src/nvim/mark.c12
-rw-r--r--src/nvim/marktree.c4
-rw-r--r--src/nvim/match.c2
-rw-r--r--src/nvim/memline.c60
-rw-r--r--src/nvim/memline_defs.h1
-rw-r--r--src/nvim/memory.c42
-rw-r--r--src/nvim/memory.h2
-rw-r--r--src/nvim/menu.c4
-rw-r--r--src/nvim/mouse.c12
-rw-r--r--src/nvim/move.c799
-rw-r--r--src/nvim/msgpack_rpc/channel.c247
-rw-r--r--src/nvim/msgpack_rpc/helpers.c245
-rw-r--r--src/nvim/msgpack_rpc/helpers.h17
-rw-r--r--src/nvim/msgpack_rpc/packer.c235
-rw-r--r--src/nvim/msgpack_rpc/packer.h76
-rw-r--r--src/nvim/msgpack_rpc/packer_defs.h24
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c1
-rw-r--r--src/nvim/normal.c205
-rw-r--r--src/nvim/ops.c181
-rw-r--r--src/nvim/option.c15
-rw-r--r--src/nvim/options.lua25
-rw-r--r--src/nvim/optionstr.c13
-rw-r--r--src/nvim/os/env.c2
-rw-r--r--src/nvim/os/fs.c2
-rw-r--r--src/nvim/os/pty_process_win.c26
-rw-r--r--src/nvim/plines.c10
-rw-r--r--src/nvim/popupmenu.c6
-rw-r--r--src/nvim/quickfix.c67
-rw-r--r--src/nvim/search.c32
-rw-r--r--src/nvim/shada.c1
-rw-r--r--src/nvim/spell.c4
-rw-r--r--src/nvim/spellfile.c7
-rw-r--r--src/nvim/spellsuggest.c7
-rw-r--r--src/nvim/state.c4
-rw-r--r--src/nvim/state.h1
-rw-r--r--src/nvim/statusline.c6
-rw-r--r--src/nvim/strings.c174
-rw-r--r--src/nvim/syntax.c10
-rw-r--r--src/nvim/tag.c14
-rw-r--r--src/nvim/terminal.c7
-rw-r--r--src/nvim/textformat.c28
-rw-r--r--src/nvim/textobject.c14
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--src/nvim/ui.c19
-rw-r--r--src/nvim/ui_client.c3
-rw-r--r--src/nvim/ui_compositor.c4
-rw-r--r--src/nvim/ui_defs.h65
-rw-r--r--src/nvim/undo.c10
-rw-r--r--src/nvim/version.c14
-rw-r--r--src/nvim/window.c565
-rw-r--r--src/nvim/winfloat.c35
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 = &current->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;
+}