aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/coverity-model.c26
-rw-r--r--src/nvim/CMakeLists.txt8
-rw-r--r--src/nvim/api/buffer.c2
-rw-r--r--src/nvim/api/private/helpers.c25
-rw-r--r--src/nvim/api/ui.c24
-rw-r--r--src/nvim/api/ui_events.in.h39
-rw-r--r--src/nvim/api/vim.c199
-rw-r--r--src/nvim/assert.h30
-rw-r--r--src/nvim/aucmd.c1
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/buffer.c30
-rw-r--r--src/nvim/buffer.h2
-rw-r--r--src/nvim/buffer_defs.h36
-rw-r--r--src/nvim/charset.c32
-rw-r--r--src/nvim/cursor.c8
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/edit.c65
-rw-r--r--src/nvim/eval.c53
-rw-r--r--src/nvim/eval/decode.c1
-rw-r--r--src/nvim/eval/encode.c10
-rw-r--r--src/nvim/ex_cmds.c14
-rw-r--r--src/nvim/ex_docmd.c58
-rw-r--r--src/nvim/ex_getln.c68
-rw-r--r--src/nvim/fileio.c1
-rw-r--r--src/nvim/garray.c4
-rw-r--r--src/nvim/garray.h1
-rw-r--r--src/nvim/generators/c_grammar.lua2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua1
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua30
-rw-r--r--src/nvim/getchar.c17
-rw-r--r--src/nvim/globals.h50
-rw-r--r--src/nvim/grid_defs.h15
-rw-r--r--src/nvim/highlight.c204
-rw-r--r--src/nvim/highlight.h8
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/indent.c18
-rw-r--r--src/nvim/log.c1
-rw-r--r--src/nvim/log.h2
-rw-r--r--src/nvim/lua/executor.c2
-rw-r--r--src/nvim/macros.h21
-rw-r--r--src/nvim/main.c4
-rw-r--r--src/nvim/math.c42
-rw-r--r--src/nvim/math.h7
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/memline_defs.h71
-rw-r--r--src/nvim/memory.c20
-rw-r--r--src/nvim/menu.c55
-rw-r--r--src/nvim/message.c403
-rw-r--r--src/nvim/message.h5
-rw-r--r--src/nvim/misc1.c24
-rw-r--r--src/nvim/mouse.c73
-rw-r--r--src/nvim/move.c134
-rw-r--r--src/nvim/normal.c84
-rw-r--r--src/nvim/ops.c8
-rw-r--r--src/nvim/option.c163
-rw-r--r--src/nvim/option_defs.h5
-rw-r--r--src/nvim/options.lua30
-rw-r--r--src/nvim/os/dl.c1
-rw-r--r--src/nvim/os/input.c109
-rw-r--r--src/nvim/os/lang.c1
-rw-r--r--src/nvim/os/signal.c23
-rw-r--r--src/nvim/os_unix.c2
-rw-r--r--src/nvim/path.c13
-rw-r--r--src/nvim/popupmnu.c122
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/screen.c699
-rw-r--r--src/nvim/screen.h3
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/strings.c16
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/terminal.c60
-rw-r--r--src/nvim/testdir/load.vim30
-rw-r--r--src/nvim/testdir/setup.vim3
-rw-r--r--src/nvim/testdir/test_autocmd.vim6
-rw-r--r--src/nvim/testdir/test_findfile.vim184
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim4
-rw-r--r--src/nvim/testdir/test_lambda.vim16
-rw-r--r--src/nvim/testdir/test_listchars.vim32
-rw-r--r--src/nvim/testdir/test_menu.vim23
-rw-r--r--src/nvim/testdir/test_mksession.vim14
-rw-r--r--src/nvim/testdir/test_timers.vim13
-rw-r--r--src/nvim/tui/tui.c37
-rw-r--r--src/nvim/ui.c111
-rw-r--r--src/nvim/ui.h22
-rw-r--r--src/nvim/ui_bridge.c6
-rw-r--r--src/nvim/ui_compositor.c442
-rw-r--r--src/nvim/ui_compositor.h12
-rw-r--r--src/nvim/version.c6
-rw-r--r--src/nvim/window.c167
90 files changed, 3053 insertions, 1379 deletions
diff --git a/src/coverity-model.c b/src/coverity-model.c
index 3c38e4ae4d..2fd55c332c 100644
--- a/src/coverity-model.c
+++ b/src/coverity-model.c
@@ -34,32 +34,6 @@ int uv_pipe_open(struct uv_pipe_s *handle, int fd)
return result;
}
-// Issue 2422
-//
-// Teach coverity about jemalloc functions, so that it understands
-// they are equivalent to malloc ones.
-
-void *je_malloc(size_t size)
-{
- return __coverity_alloc__(size);
-}
-
-void je_free(void *ptr)
-{
- __coverity_free__(ptr);
-}
-
-void *je_calloc(size_t count, size_t size)
-{
- return je_malloc(count * size);
-}
-
-void *je_realloc(void *ptr, size_t size)
-{
- je_free(ptr);
- return je_malloc(size);
-}
-
// Hint Coverity that adding item to d avoids losing track
// of the memory allocated for item.
typedef struct {} dictitem_T;
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index a2c4e677d4..8abc43c2aa 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -23,6 +23,7 @@ set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua)
set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua)
+set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c)
@@ -269,6 +270,7 @@ add_custom_command(
${API_HEADERS}
${MSGPACK_RPC_HEADERS}
${API_DISPATCH_GENERATOR}
+ ${GENERATOR_C_GRAMMAR}
${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua
)
@@ -300,6 +302,7 @@ add_custom_command(
${GENERATED_UI_EVENTS_METADATA}
DEPENDS
${API_UI_EVENTS_GENERATOR}
+ ${GENERATOR_C_GRAMMAR}
${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
)
@@ -397,11 +400,6 @@ endif()
set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES})
-# Don't use jemalloc in the unit test library.
-if(JEMALLOC_FOUND)
- list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES})
-endif()
-
if(POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
endif()
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 5df0f0bb47..9cd178eaeb 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -104,7 +104,7 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// the whole buffer. If so, the first notification will be a
/// `nvim_buf_lines_event`. Otherwise, the first notification will be
/// a `nvim_buf_changedtick_event`
-/// @param opts Optional parameters. Currently not used.
+/// @param opts Optional parameters. Reserved for future use.
/// @param[out] err Details of an error that may have occurred
/// @return False when updates couldn't be enabled because the buffer isn't
/// loaded or `opts` contained an invalid key; otherwise True.
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 82c9a1da67..19a3368c1c 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -27,6 +27,7 @@
#include "nvim/version.h"
#include "nvim/lib/kvec.h"
#include "nvim/getchar.h"
+#include "nvim/fileio.h"
#include "nvim/ui.h"
/// Helper structure for vim_to_object
@@ -711,6 +712,12 @@ String cbuf_to_string(const char *buf, size_t size)
};
}
+String cstrn_to_string(const char *str, size_t maxsize)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return cbuf_to_string(str, strnlen(str, maxsize));
+}
+
/// Creates a String using the given C string. Unlike
/// cstr_to_string this function DOES NOT copy the C string.
///
@@ -725,6 +732,18 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE
return (String){ .data = str, .size = strlen(str) };
}
+/// Return the owned memory of a ga as a String
+///
+/// Reinitializes the ga to a valid empty state.
+String ga_take_string(garray_T *ga)
+{
+ String str = { .data = (char *)ga->ga_data, .size = (size_t)ga->ga_len };
+ ga->ga_data = NULL;
+ ga->ga_len = 0;
+ ga->ga_maxlen = 0;
+ return str;
+}
+
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
@@ -1094,7 +1113,7 @@ static void set_option_value_for(char *key,
{
win_T *save_curwin = NULL;
tabpage_T *save_curtab = NULL;
- bufref_T save_curbuf = { NULL, 0, 0 };
+ aco_save_T aco;
try_start();
switch (opt_type)
@@ -1115,9 +1134,9 @@ static void set_option_value_for(char *key,
restore_win(save_curwin, save_curtab, true);
break;
case SREQ_BUF:
- switch_buffer(&save_curbuf, (buf_T *)from);
+ aucmd_prepbuf(&aco, (buf_T *)from);
set_option_value_err(key, numval, stringval, opt_flags, err);
- restore_buffer(&save_curbuf);
+ aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
set_option_value_err(key, numval, stringval, opt_flags, err);
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 7ba5251c60..9e9be588e3 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -113,6 +113,8 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
ui->option_set = remote_ui_option_set;
+ ui->win_scroll_over_start = remote_ui_win_scroll_over_start;
+ ui->win_scroll_over_reset = remote_ui_win_scroll_over_reset;
ui->event = remote_ui_event;
ui->inspect = remote_ui_inspect;
@@ -130,6 +132,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->ui_ext[kUILinegrid] = true;
}
+ if (ui->ui_ext[kUIMessages]) {
+ // This uses attribute indicies, so ext_linegrid is needed.
+ ui->ui_ext[kUILinegrid] = true;
+ // Cmdline uses the messages area, so it should be externalized too.
+ ui->ui_ext[kUICmdline] = true;
+ }
+
UIData *data = xmalloc(sizeof(UIData));
data->channel_id = channel_id;
data->buffer = (Array)ARRAY_DICT_INIT;
@@ -208,8 +217,9 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
return;
}
ui->rgb = value.data.boolean;
- // A little drastic, but only legacy uis need to use this option
- if (!init) {
+ // A little drastic, but only takes effect for legacy uis. For linegrid UI
+ // only changes metadata for nvim_list_uis(), no refresh needed.
+ if (!init && !ui->ui_ext[kUILinegrid]) {
ui_refresh();
}
return;
@@ -245,9 +255,8 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
name.data);
}
-/// Tell nvim to resize a grid. Nvim sends grid_resize event with the
-/// requested grid size is within size limits and with maximum allowed size
-/// otherwise.
+/// Tell Nvim to resize a grid. Triggers a grid_resize event with the requested
+/// grid size or the maximum size if it exceeds size limits.
///
/// On invalid grid handle, fails with error.
///
@@ -354,6 +363,9 @@ static void remote_ui_default_colors_set(UI *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);
+ }
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(rgb_fg));
ADD(args, INTEGER_OBJ(rgb_bg));
@@ -460,7 +472,7 @@ static void remote_ui_put(UI *ui, const char *cell)
static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
UIData *data = ui->data;
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 59a7780651..b57cf8d3ef 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -38,6 +38,9 @@ void set_icon(String icon)
FUNC_API_SINCE(3);
void option_set(String name, Object value)
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
+// Stop event is not exported as such, represented by EOF in the msgpack stream.
+void stop(void)
+ FUNC_API_NOEXPORT;
// First revison of the grid protocol, used by default
void update_fg(Integer fg)
@@ -71,28 +74,39 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
Array info)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void grid_resize(Integer grid, Integer width, Integer height)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_clear(Integer grid)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_cursor_goto(Integer grid, Integer row, Integer col)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_line(Integer grid, Integer row, Integer col_start, Array data)
FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY;
void grid_scroll(Integer grid, Integer top, Integer bot,
Integer left, Integer right, Integer rows, Integer cols)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_destroy(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+// For perfomance and simplicity, we use the dense screen representation
+// in internal code, such as compositor and TUI. The remote_ui module will
+// translate this in to the public grid_line format.
+void raw_line(Integer grid, Integer row, Integer startcol,
+ Integer endcol, Integer clearcol, Integer clearattr,
+ LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
+ FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
+
+void event(char *name, Array args, bool *args_consumed)
+ FUNC_API_NOEXPORT;
+
void win_pos(Integer grid, Integer win, Integer startrow,
Integer startcol, Integer width, Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_hide(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_scroll_over_start(void)
- FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
void win_scroll_over_reset(void)
- FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
void popupmenu_show(Array items, Integer selected,
Integer row, Integer col, Integer grid)
@@ -127,4 +141,17 @@ void wildmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+
+void msg_show(String kind, Array content, Boolean replace_last)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_clear(void)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_showcmd(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_showmode(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_ruler(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_history_show(Array entries)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index ce7ef681ef..5a4d0a11e7 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -198,6 +198,9 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
/// @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.
+///
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
/// requested if the buffer becomes full).
@@ -207,6 +210,96 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys);
}
+/// Send mouse event from GUI.
+///
+/// The call is non-blocking. It doesn't wait on any resulting action, but
+/// queues the event to be processed soon by the event loop.
+///
+/// @note Currently this doesn't support "scripting" multiple mouse events
+/// 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 limitiation.
+///
+/// @param button Mouse button: one of "left", "right", "middle", "wheel".
+/// @param action For ordinary buttons, one of "press", "drag", "release".
+/// For the wheel, one of "up", "down", "left", "right".
+/// @param modifier String of modifiers each represented by a single char.
+/// The same specifiers are used as for a key press, except
+/// that the "-" separator is optional, so "C-A-", "c-a"
+/// and "CA" can all be used to specify Ctrl+Alt+click.
+/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
+/// @param row Mouse row-position (zero-based, like redraw events)
+/// @param col Mouse column-position (zero-based, like redraw events)
+void nvim_input_mouse(String button, String action, String modifier,
+ Integer grid, Integer row, Integer col, Error *err)
+ FUNC_API_SINCE(6) FUNC_API_ASYNC
+{
+ if (button.data == NULL || action.data == NULL) {
+ goto error;
+ }
+
+ int code = 0;
+
+ if (strequal(button.data, "left")) {
+ code = KE_LEFTMOUSE;
+ } else if (strequal(button.data, "middle")) {
+ code = KE_MIDDLEMOUSE;
+ } else if (strequal(button.data, "right")) {
+ code = KE_RIGHTMOUSE;
+ } else if (strequal(button.data, "wheel")) {
+ code = KE_MOUSEDOWN;
+ } else {
+ goto error;
+ }
+
+ if (code == KE_MOUSEDOWN) {
+ if (strequal(action.data, "down")) {
+ code = KE_MOUSEUP;
+ } else if (strequal(action.data, "up")) {
+ code = KE_MOUSEDOWN;
+ } else if (strequal(action.data, "left")) {
+ code = KE_MOUSERIGHT;
+ } else if (strequal(action.data, "right")) {
+ code = KE_MOUSELEFT;
+ } else {
+ goto error;
+ }
+ } else {
+ if (strequal(action.data, "press")) {
+ // pass
+ } else if (strequal(action.data, "drag")) {
+ code += KE_LEFTDRAG - KE_LEFTMOUSE;
+ } else if (strequal(action.data, "release")) {
+ code += KE_LEFTRELEASE - KE_LEFTMOUSE;
+ } else {
+ goto error;
+ }
+ }
+
+ int modmask = 0;
+ for (size_t i = 0; i < modifier.size; i++) {
+ char byte = modifier.data[i];
+ if (byte == '-') {
+ continue;
+ }
+ int mod = name_to_mod_mask(byte);
+ if (mod == 0) {
+ api_set_error(err, kErrorTypeValidation,
+ "invalid modifier %c", byte);
+ return;
+ }
+ modmask |= mod;
+ }
+
+ input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col);
+ return;
+
+error:
+ api_set_error(err, kErrorTypeValidation,
+ "invalid button or action");
+}
+
/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
/// the internal representation.
///
@@ -598,7 +691,7 @@ void nvim_set_current_dir(String dir, Error *err)
try_end(err);
}
-/// Gets the current line
+/// Gets the current line.
///
/// @param[out] err Error details, if any
/// @return Current line string
@@ -608,7 +701,7 @@ String nvim_get_current_line(Error *err)
return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
-/// Sets the current line
+/// Sets the current line.
///
/// @param line Line contents
/// @param[out] err Error details, if any
@@ -618,7 +711,7 @@ void nvim_set_current_line(String line, Error *err)
buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
}
-/// Deletes the current line
+/// Deletes the current line.
///
/// @param[out] err Error details, if any
void nvim_del_current_line(Error *err)
@@ -627,7 +720,7 @@ void nvim_del_current_line(Error *err)
buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
-/// Gets a global (g:) variable
+/// Gets a global (g:) variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -638,7 +731,7 @@ Object nvim_get_var(String name, Error *err)
return dict_get_value(&globvardict, name, err);
}
-/// Sets a global (g:) variable
+/// Sets a global (g:) variable.
///
/// @param name Variable name
/// @param value Variable value
@@ -649,7 +742,7 @@ void nvim_set_var(String name, Object value, Error *err)
dict_set_var(&globvardict, name, value, false, false, err);
}
-/// Removes a global (g:) variable
+/// Removes a global (g:) variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -676,7 +769,7 @@ Object vim_del_var(String name, Error *err)
return dict_set_var(&globvardict, name, NIL, true, true, err);
}
-/// Gets a v: variable
+/// Gets a v: variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -687,7 +780,7 @@ Object nvim_get_vvar(String name, Error *err)
return dict_get_value(&vimvardict, name, err);
}
-/// Sets a v: variable, if it is not readonly
+/// Sets a v: variable, if it is not readonly.
///
/// @param name Variable name
/// @param value Variable value
@@ -698,7 +791,7 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
-/// Gets an option value string
+/// Gets an option value string.
///
/// @param name Option name
/// @param[out] err Error details, if any
@@ -709,7 +802,7 @@ Object nvim_get_option(String name, Error *err)
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
-/// Sets an option value
+/// Sets an option value.
///
/// @param name Option name
/// @param value New option value
@@ -777,7 +870,7 @@ ArrayOf(Buffer) nvim_list_bufs(void)
return rv;
}
-/// Gets the current buffer
+/// Gets the current buffer.
///
/// @return Buffer handle
Buffer nvim_get_current_buf(void)
@@ -786,7 +879,7 @@ Buffer nvim_get_current_buf(void)
return curbuf->handle;
}
-/// Sets the current buffer
+/// Sets the current buffer.
///
/// @param buffer Buffer handle
/// @param[out] err Error details, if any
@@ -809,7 +902,7 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
}
}
-/// Gets the current list of window handles
+/// Gets the current list of window handles.
///
/// @return List of window handles
ArrayOf(Window) nvim_list_wins(void)
@@ -831,7 +924,7 @@ ArrayOf(Window) nvim_list_wins(void)
return rv;
}
-/// Gets the current window
+/// Gets the current window.
///
/// @return Window handle
Window nvim_get_current_win(void)
@@ -840,7 +933,7 @@ Window nvim_get_current_win(void)
return curwin->handle;
}
-/// Sets the current window
+/// Sets the current window.
///
/// @param window Window handle
void nvim_set_current_win(Window window, Error *err)
@@ -862,7 +955,39 @@ void nvim_set_current_win(Window window, Error *err)
}
}
-/// Gets the current list of tabpage handles
+/// Creates a new, empty, unnamed buffer.
+///
+/// @param listed Controls 'buflisted'
+/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
+/// (always 'nomodified')
+/// @param[out] err Error details, if any
+/// @return Buffer handle, or 0 on error
+///
+/// @see buf_open_scratch
+Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
+ FUNC_API_SINCE(6)
+{
+ try_start();
+ buf_T *buf = buflist_new(NULL, NULL, (linenr_T)0,
+ BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
+ try_end(err);
+ if (buf == NULL) {
+ if (!ERROR_SET(err)) {
+ api_set_error(err, kErrorTypeException, "Failed to create buffer");
+ }
+ return 0;
+ }
+ if (scratch) {
+ WITH_BUFFER(buf, {
+ set_option_value("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ });
+ }
+ return buf->b_fnum;
+}
+
+/// Gets the current list of tabpage handles.
///
/// @return List of tabpage handles
ArrayOf(Tabpage) nvim_list_tabpages(void)
@@ -884,7 +1009,7 @@ ArrayOf(Tabpage) nvim_list_tabpages(void)
return rv;
}
-/// Gets the current tabpage
+/// Gets the current tabpage.
///
/// @return Tabpage handle
Tabpage nvim_get_current_tabpage(void)
@@ -893,7 +1018,7 @@ Tabpage nvim_get_current_tabpage(void)
return curtab->handle;
}
-/// Sets the current tabpage
+/// Sets the current tabpage.
///
/// @param tabpage Tabpage handle
/// @param[out] err Error details, if any
@@ -916,7 +1041,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
}
}
-/// Creates a new namespace, or gets an existing one
+/// Creates a new namespace, or gets an existing one.
///
/// Namespaces are used for buffer highlights and virtual text, see
/// |nvim_buf_add_highlight()| and |nvim_buf_set_virtual_text()|.
@@ -942,7 +1067,7 @@ Integer nvim_create_namespace(String name)
return (Integer)id;
}
-/// Gets existing, non-anonymous namespaces
+/// Gets existing, non-anonymous namespaces.
///
/// @return dict that maps from names to namespace ids.
Dictionary nvim_get_namespaces(void)
@@ -959,7 +1084,7 @@ Dictionary nvim_get_namespaces(void)
return retval;
}
-/// Subscribes to event broadcasts
+/// Subscribes to event broadcasts.
///
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
@@ -973,7 +1098,7 @@ void nvim_subscribe(uint64_t channel_id, String event)
rpc_subscribe(channel_id, e);
}
-/// Unsubscribes to event broadcasts
+/// Unsubscribes to event broadcasts.
///
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
@@ -1283,7 +1408,7 @@ typedef struct {
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// @endcond
-/// Parse a VimL expression
+/// Parse a VimL expression.
///
/// @param[in] expr Expression to parse. Is always treated as a single line.
/// @param[in] flags Flags:
@@ -1764,7 +1889,7 @@ static void write_msg(String message, bool to_err)
// Functions used for testing purposes
-/// Returns object given as argument
+/// Returns object given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1777,7 +1902,7 @@ Object nvim__id(Object obj)
return copy_object(obj);
}
-/// Returns array given as argument
+/// Returns array given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1790,7 +1915,7 @@ Array nvim__id_array(Array arr)
return copy_object(ARRAY_OBJ(arr)).data.array;
}
-/// Returns dictionary given as argument
+/// Returns dictionary given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1803,7 +1928,7 @@ Dictionary nvim__id_dictionary(Dictionary dct)
return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
}
-/// Returns floating-point value given as argument
+/// Returns floating-point value given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1927,19 +2052,19 @@ Object nvim_get_proc(Integer pid, Error *err)
return rvobj;
}
-/// Selects an item in the completion popupmenu
+/// Selects an item in the completion popupmenu.
///
-/// When insert completion is not active, this API call is silently ignored.
-/// It is mostly useful for an external UI using |ui-popupmenu| for instance
-/// to control the popupmenu with the mouse. But it can also be used in an
-/// insert mode mapping, use <cmd> mapping |:map-cmd| to ensure the mapping
-/// doesn't end completion mode.
+/// If |ins-completion| is not active this API call is silently ignored.
+/// Useful for an external UI using |ui-popupmenu| to control the popupmenu
+/// with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
+/// ensure the mapping doesn't end completion mode.
///
-/// @param item Index of the item to select, starting with zero. Pass in "-1"
-/// to select no item (restore original text).
+/// @param item Index (zero-based) of the item to select. Value of -1 selects
+/// nothing and restores the original text.
/// @param insert Whether the selection should be inserted in the buffer.
-/// @param finish If true, completion will be finished with this item, and the
-/// popupmenu dissmissed. Implies `insert`.
+/// @param finish Finish the completion and dismiss the popupmenu. Implies
+/// `insert`.
+/// @param opts Optional parameters. Reserved for future use.
void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish,
Dictionary opts, Error *err)
FUNC_API_SINCE(6)
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 29195a49dc..34734f294d 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -1,6 +1,8 @@
#ifndef NVIM_ASSERT_H
#define NVIM_ASSERT_H
+#include "auto/config.h"
+
// support static asserts (aka compile-time asserts)
// some compilers don't properly support short-circuiting apparently, giving
@@ -78,7 +80,7 @@
# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
- _Pragma("GCC diagnostic pop") \
+ _Pragma("GCC diagnostic pop")
// the same goes for clang in C99 mode, but we suppress a different warning
#elif defined(__clang__) && __has_extension(c_static_assert)
@@ -88,11 +90,11 @@
# undef STATIC_ASSERT_PRAGMA_START
# define STATIC_ASSERT_PRAGMA_START \
_Pragma("clang diagnostic push") \
- _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
+ _Pragma("clang diagnostic ignored \"-Wc11-extensions\"")
# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
- _Pragma("clang diagnostic pop") \
+ _Pragma("clang diagnostic pop")
// TODO(aktau): verify that this works, don't have MSVC on hand.
#elif _MSC_VER >= 1600
@@ -133,22 +135,32 @@
///
/// @param MAX Maximum value of the narrowest type of operand.
/// Not used if compiler supports __builtin_add_overflow.
-#if HAVE_BUILTIN_ADD_OVERFLOW
+#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_ADD(a, b, c, t) \
- do { if (__builtin_add_overflow(a, b, c)) { abort(); } } while (0)
+ do { \
+ if (__builtin_add_overflow(a, b, c)) { \
+ ELOG("STRICT_ADD overflow"); \
+ abort(); \
+ } \
+ } while (0)
#else
# define STRICT_ADD(a, b, c, t) \
- do { *(c) = (t)(a + b); } while (0)
+ do { *(c) = (t)((a) + (b)); } while (0)
#endif
/// @def STRICT_SUB
/// @brief Subtracts (a - b) and stores result in `c`. Aborts on overflow.
-#if HAVE_BUILTIN_ADD_OVERFLOW
+#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_SUB(a, b, c, t) \
- do { if (__builtin_sub_overflow(a, b, c)) { abort(); } } while (0)
+ do { \
+ if (__builtin_sub_overflow(a, b, c)) { \
+ ELOG("STRICT_SUB overflow"); \
+ abort(); \
+ } \
+ } while (0)
#else
# define STRICT_SUB(a, b, c, t) \
- do { *(c) = (t)(a - b); } while (0)
+ do { *(c) = (t)((a) - (b)); } while (0)
#endif
#endif // NVIM_ASSERT_H
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
index 9ad3414b79..3bb0fcec3b 100644
--- a/src/nvim/aucmd.c
+++ b/src/nvim/aucmd.c
@@ -6,6 +6,7 @@
#include "nvim/vim.h"
#include "nvim/main.h"
#include "nvim/ui.h"
+#include "nvim/aucmd.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.c.generated.h"
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 3cffd66dee..cc0ed0f587 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -74,6 +74,7 @@ return {
'SessionLoadPost', -- after loading a session file
'ShellCmdPost', -- after ":!cmd"
'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
+ 'Signal', -- after nvim process received a signal
'SourceCmd', -- sourcing a Vim script using command
'SourcePre', -- before sourcing a Vim script
'SpellFileMissing', -- spell file missing
@@ -115,6 +116,7 @@ return {
-- syntax file
nvim_specific = {
DirChanged=true,
+ Signal=true,
TabClosed=true,
TabNew=true,
TabNewEntered=true,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c15a6f1330..7fd4326914 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1472,6 +1472,10 @@ void set_curbuf(buf_T *buf, int action)
if (old_tw != curbuf->b_p_tw)
check_colorcolumn(curwin);
}
+
+ if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
+ terminal_check_size(prevbuf->terminal);
+ }
}
/*
@@ -1625,7 +1629,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
FileID file_id;
bool file_id_valid = (sfname != NULL
&& os_fileid((char *)sfname, &file_id));
- if (ffname != NULL && !(flags & BLN_DUMMY)
+ if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW))
&& (buf = buflist_findname_file_id(ffname, &file_id,
file_id_valid)) != NULL) {
xfree(ffname);
@@ -3220,6 +3224,9 @@ int build_stl_str_hl(
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u *usefmt = fmt;
+ const int save_must_redraw = must_redraw;
+ const int save_redr_type = curwin->w_redr_type;
+ const int save_highlight_shcnaged = need_highlight_changed;
// When the format starts with "%!" then evaluate it as an expression and
// use the result as the actual format string.
@@ -3628,16 +3635,16 @@ int build_stl_str_hl(
vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum);
set_internal_string_var((char_u *)"g:actual_curbuf", tmp);
- buf_T *o_curbuf = curbuf;
- win_T *o_curwin = curwin;
+ buf_T *const save_curbuf = curbuf;
+ win_T *const save_curwin = curwin;
curwin = wp;
curbuf = wp->w_buffer;
// Note: The result stored in `t` is unused.
str = eval_to_string_safe(out_p, &t, use_sandbox);
- curwin = o_curwin;
- curbuf = o_curbuf;
+ curwin = save_curwin;
+ curbuf = save_curbuf;
// Remove the variable we just stored
do_unlet(S_LEN("g:actual_curbuf"), true);
@@ -3676,10 +3683,10 @@ int build_stl_str_hl(
{
// In list mode virtcol needs to be recomputed
colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL) {
- wp->w_p_list = FALSE;
+ if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
+ wp->w_p_list = false;
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = TRUE;
+ wp->w_p_list = true;
}
++virtcol;
// Don't display %V if it's the same as %c.
@@ -4258,6 +4265,13 @@ int build_stl_str_hl(
cur_tab_rec->def.func = NULL;
}
+ // We do not want redrawing a stausline, ruler, title, etc. to trigger
+ // another redraw, it may cause an endless loop. This happens when a
+ // statusline changes a highlight group.
+ must_redraw = save_must_redraw;
+ curwin->w_redr_type = save_redr_type;
+ need_highlight_changed = save_highlight_shcnaged;
+
return width;
}
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index e61c312fb1..79bed049ea 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -31,7 +31,7 @@ enum bln_values {
BLN_CURBUF = 1, // May re-use curbuf for new buffer
BLN_LISTED = 2, // Put new buffer in buffer list
BLN_DUMMY = 4, // Allocating dummy buffer
- // TODO(mhinz): merge patch that introduces BLN_NEW
+ BLN_NEW = 8, // create a new buffer
BLN_NOOPT = 16, // Don't copy options to existing buffer
};
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 05688790c2..07d8ab2e0c 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -248,6 +248,10 @@ typedef struct {
# define w_p_scl w_onebuf_opt.wo_scl // 'signcolumn'
char_u *wo_winhl;
# define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight'
+ char_u *wo_fcs;
+# define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
+ char_u *wo_lcs;
+# define w_p_lcs w_onebuf_opt.wo_lcs // 'listchars'
LastSet wo_scriptID[WV_COUNT]; // SIDs for window-local options
# define w_p_scriptID w_onebuf_opt.wo_scriptID
@@ -1003,6 +1007,31 @@ struct window_S {
colnr_T w_old_visual_col; ///< last known start of visual part
colnr_T w_old_curswant; ///< last known value of Curswant
+ // 'listchars' characters. Defaults set in set_chars_option().
+ struct {
+ int eol;
+ int ext;
+ int prec;
+ int nbsp;
+ int space;
+ int tab1; ///< first tab character
+ int tab2; ///< second tab character
+ int tab3; ///< third tab character
+ int trail;
+ int conceal;
+ } w_p_lcs_chars;
+
+ // 'fillchars' characters. Defaults set in set_chars_option().
+ struct {
+ int stl;
+ int stlnc;
+ int vert;
+ int fold;
+ int diff;
+ int msgsep;
+ int eob;
+ } w_p_fcs_chars;
+
/*
* "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
* displaying the buffer.
@@ -1034,6 +1063,13 @@ struct window_S {
int w_width; /* Width of window, excluding separation. */
int w_vsep_width; /* Number of separator columns (0 or 1). */
+ // inner size of window, which can be overridden by external UI
+ int w_height_inner;
+ int w_width_inner;
+ // external UI request. If non-zero, the inner size will use this.
+ int w_height_request;
+ int w_width_request;
+
/*
* === start of cached values ====
*/
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index c220c4e347..08ecff149c 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -749,8 +749,8 @@ int vim_strnsize(char_u *s, int len)
///
/// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
- if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \
- const int ts = (int) (buf)->b_p_ts; \
+ if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
+ const int ts = (int)(buf)->b_p_ts; \
return (ts - (int)(col % ts)); \
} else { \
return ptr2cells(p); \
@@ -1022,12 +1022,12 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
&& vim_isbreak(c)
&& !vim_isbreak((int)s[1])
&& wp->w_p_wrap
- && (wp->w_grid.Columns != 0)) {
+ && (wp->w_width_inner != 0)) {
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
numberextra = win_col_off(wp);
col2 = col;
- colmax = (colnr_T)(wp->w_grid.Columns - numberextra - col_adj);
+ colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
if (col >= colmax) {
colmax += col_adj;
@@ -1076,9 +1076,9 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
numberextra = numberwidth;
col += numberextra + mb_added;
- if (col >= (colnr_T)wp->w_grid.Columns) {
- col -= wp->w_grid.Columns;
- numberextra = wp->w_grid.Columns - (numberextra - win_col_off2(wp));
+ if (col >= (colnr_T)wp->w_width_inner) {
+ col -= wp->w_width_inner;
+ numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
if (col >= numberextra && numberextra > 0) {
col %= numberextra;
}
@@ -1097,17 +1097,17 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
numberwidth -= win_col_off2(wp);
}
- if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_grid.Columns)) {
+ if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
added = 0;
if (*p_sbr != NUL) {
- if (size + sbrlen + numberwidth > (colnr_T)wp->w_grid.Columns) {
+ if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
// Calculate effective window width.
- int width = (colnr_T)wp->w_grid.Columns - sbrlen - numberwidth;
- int prev_width = col ? ((colnr_T)wp->w_grid.Columns - (sbrlen + col))
+ int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
+ int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
: 0;
if (width == 0) {
- width = (colnr_T)wp->w_grid.Columns;
+ width = (colnr_T)wp->w_width_inner;
}
added += ((size - prev_width) / width) * vim_strsize(p_sbr);
if ((size - prev_width) % width) {
@@ -1149,7 +1149,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
{
int n;
- if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) {
+ if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
n = (int)wp->w_buffer->b_p_ts;
return n - (col % n);
}
@@ -1176,11 +1176,11 @@ bool in_win_border(win_T *wp, colnr_T vcol)
int width1; // width of first line (after line number)
int width2; // width of further lines
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
// there is no border
return false;
}
- width1 = wp->w_grid.Columns - win_col_off(wp);
+ width1 = wp->w_width_inner - win_col_off(wp);
if ((int)vcol < width1 - 1) {
return false;
@@ -1241,7 +1241,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
// When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
// use a simple loop.
// Also use this when 'list' is set but tabs take their normal size.
- if ((!wp->w_p_list || (lcs_tab1 != NUL))
+ if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
&& !wp->w_p_lbr
&& (*p_sbr == NUL)
&& !wp->w_p_bri ) {
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6c1bd01ff5..bc14761877 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -121,11 +121,11 @@ static int coladvance2(
--curwin->w_curswant;
}
} else {
- int width = curwin->w_grid.Columns - win_col_off(curwin);
+ int width = curwin->w_width_inner - win_col_off(curwin);
if (finetune
&& curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
&& wcol >= (colnr_T)width) {
csize = linetabsize(line);
if (csize > 0)
@@ -230,7 +230,7 @@ static int coladvance2(
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_grid.Columns)) {
+ if (b > 0 && b < (MAXCOL - 2 * curwin->w_width_inner)) {
pos->coladd = b;
}
@@ -444,7 +444,7 @@ bool leftcol_changed(void)
bool retval = false;
changed_cline_bef_curs();
- lastcol = curwin->w_leftcol + curwin->w_grid.Columns - curwin_col_off() - 1;
+ lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1;
validate_virtcol();
/*
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 866161e5cf..ee4a48ff5d 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -1541,7 +1541,7 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout)
diffstyle = DIFF_ED;
} else if ((STRNCMP(line, "@@ ", 3) == 0)) {
diffstyle = DIFF_UNIFIED;
- } else if ((STRNCMP(line, "--- ", 4) == 0)
+ } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
&& (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
&& (STRNCMP(line, "+++ ", 4) == 0)
&& (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index bb3c0ec196..62b35fa708 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -473,6 +473,8 @@ static void insert_enter(InsertState *s)
o_lnum = curwin->w_cursor.lnum;
}
+ pum_check_clear();
+
foldUpdateAfterInsert();
// When CTRL-C was typed got_int will be set, with the result
// that the autocommands won't be executed. When mapped got_int
@@ -566,7 +568,7 @@ static int insert_check(VimState *state)
if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
&& curwin->w_wrow == curwin->w_winrow
- + curwin->w_grid.Rows - 1 - p_so
+ + curwin->w_height_inner - 1 - p_so
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) {
@@ -1447,6 +1449,7 @@ ins_redraw (
redrawWinline(curwin, curwin->w_cursor.lnum);
}
+ pum_check_clear();
if (must_redraw) {
update_screen(0);
} else if (clear_cmdline || redraw_cmdline) {
@@ -2490,20 +2493,6 @@ void set_completion(colnr_T startcol, list_T *list)
static pumitem_T *compl_match_array = NULL;
static int compl_match_arraysize;
-/*
- * Update the screen and when there is any scrolling remove the popup menu.
- */
-static void ins_compl_upd_pum(void)
-{
- int h;
-
- if (compl_match_array != NULL) {
- h = curwin->w_cline_height;
- update_screen(0);
- if (h != curwin->w_cline_height)
- ins_compl_del_pum();
- }
-}
/*
* Remove any popup menu.
@@ -2511,7 +2500,7 @@ static void ins_compl_upd_pum(void)
static void ins_compl_del_pum(void)
{
if (compl_match_array != NULL) {
- pum_undisplay();
+ pum_undisplay(false);
xfree(compl_match_array);
compl_match_array = NULL;
}
@@ -4305,17 +4294,14 @@ ins_compl_next (
}
if (!allow_get_expansion) {
- /* may undisplay the popup menu first */
- ins_compl_upd_pum();
-
- /* redraw to show the user what was inserted */
+ // redraw to show the user what was inserted
update_screen(0);
- /* display the updated popup menu */
+ // display the updated popup menu
ins_compl_show_pum();
- /* Delete old text to be replaced, since we're still searching and
- * don't want to match ourselves! */
+ // Delete old text to be replaced, since we're still searching and
+ // don't want to match ourselves!
ins_compl_delete();
}
@@ -4862,8 +4848,6 @@ static int ins_complete(int c, bool enable_pum)
save_w_leftcol = curwin->w_leftcol;
n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
- /* may undisplay the popup menu */
- ins_compl_upd_pum();
if (n > 1) /* all matches have been found */
compl_matches = n;
@@ -5868,7 +5852,7 @@ static void check_auto_format(
/*
* Find out textwidth to be used for formatting:
* if 'textwidth' option is set, use it
- * else if 'wrapmargin' option is set, use curwin->w_grid.Columns-'wrapmargin'
+ * else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
* if invalid value, use 0.
* Set default to window width (maximum 79) for "gq" operator.
*/
@@ -5883,7 +5867,7 @@ comp_textwidth (
if (textwidth == 0 && curbuf->b_p_wm) {
/* The width is the window width minus 'wrapmargin' minus all the
* things that add to the margin. */
- textwidth = curwin->w_grid.Columns - curbuf->b_p_wm;
+ textwidth = curwin->w_width_inner - curbuf->b_p_wm;
if (cmdwin_type != 0) {
textwidth -= 1;
}
@@ -5899,7 +5883,7 @@ comp_textwidth (
if (textwidth < 0)
textwidth = 0;
if (ff && textwidth == 0) {
- textwidth = curwin->w_grid.Columns - 1;
+ textwidth = curwin->w_width_inner - 1;
if (textwidth > 79) {
textwidth = 79;
}
@@ -7144,11 +7128,17 @@ static void ins_reg(void)
* message for it. Only call it explicitly. */
++no_u_sync;
if (regname == '=') {
- /* Sync undo when evaluating the expression calls setline() or
- * append(), so that it can be undone separately. */
+ pos_T curpos = curwin->w_cursor;
+
+ // Sync undo when evaluating the expression calls setline() or
+ // append(), so that it can be undone separately.
u_sync_once = 2;
regname = get_expr_register();
+
+ // Cursor may be moved back a column.
+ curwin->w_cursor = curpos;
+ check_cursor();
}
if (regname == NUL || !valid_yank_reg(regname, false)) {
vim_beep(BO_REG);
@@ -7939,15 +7929,13 @@ static void ins_mouse(int c)
static void ins_mousescroll(int dir)
{
win_T *const old_curwin = curwin;
- bool did_scroll = false;
pos_T tpos = curwin->w_cursor;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row = mouse_row;
- int col = mouse_col;
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
@@ -7970,7 +7958,6 @@ static void ins_mousescroll(int dir)
} else {
mouse_scroll_horiz(dir);
}
- did_scroll = true;
}
curwin->w_redr_status = TRUE;
@@ -7978,14 +7965,6 @@ static void ins_mousescroll(int dir)
curwin = old_curwin;
curbuf = curwin->w_buffer;
- /* The popup menu may overlay the window, need to redraw it.
- * TODO: Would be more efficient to only redraw the windows that are
- * overlapped by the popup menu. */
- if (pum_visible() && did_scroll) {
- redraw_all_later(NOT_VALID);
- ins_compl_show_pum();
- }
-
if (!equalpos(curwin->w_cursor, tpos)) {
start_arrow(&tpos);
can_cindent = true;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4cf8a01ddb..d63e45d3c7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5958,7 +5958,9 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
#pragma function (floor)
#endif
+PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
# include "funcs.generated.h"
+PRAGMA_DIAG_POP
#endif
/*
@@ -9521,6 +9523,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (is_mouse_key(n)) {
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
win_T *win;
linenr_T lnum;
win_T *wp;
@@ -9529,7 +9532,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (row >= 0 && col >= 0) {
/* Find the window at the mouse coordinates and compute the
* text position. */
- win = mouse_find_win(&row, &col);
+ win = mouse_find_win(&grid, &row, &col);
if (win == NULL) {
return;
}
@@ -11264,7 +11267,7 @@ void get_user_input(const typval_T *const argvars,
// Only the part of the message after the last NL is considered as
// prompt for the command line, unlsess cmdline is externalized
const char *p = prompt;
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
const char *lastnl = strrchr(prompt, '\n');
if (lastnl != NULL) {
p = lastnl+1;
@@ -16424,7 +16427,9 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) {
cchar = syn_get_sub_char();
if (cchar == NUL && curwin->w_p_cole == 1) {
- cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;
+ cchar = (curwin->w_p_lcs_chars.conceal == NUL)
+ ? ' '
+ : curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
utf_char2bytes(cchar, str);
@@ -16795,10 +16800,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- uint16_t term_width = MAX(0, curwin->w_grid.Columns - win_col_off(curwin));
+ uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
true, false, false, cwd,
- term_width, curwin->w_grid.Rows,
+ term_width, curwin->w_height_inner,
xstrdup("xterm-256color"),
&rettv->vval.v_number);
if (rettv->vval.v_number <= 0) {
@@ -19592,23 +19597,9 @@ void ex_echo(exarg_T *eap)
msg_puts_attr(" ", echo_attr);
}
char *tofree = encode_tv2echo(&rettv, NULL);
- const char *p = tofree;
- if (p != NULL) {
- for (; *p != NUL && !got_int; ++p) {
- if (*p == '\n' || *p == '\r' || *p == TAB) {
- if (*p != TAB && needclr) {
- /* remove any text still there from the command */
- msg_clr_eos();
- needclr = false;
- }
- msg_putchar_attr((uint8_t)(*p), echo_attr);
- } else {
- int i = (*mb_ptr2len)((const char_u *)p);
-
- (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
- p += i - 1;
- }
- }
+ if (*tofree != NUL) {
+ msg_ext_set_kind("echo");
+ msg_multiline_attr(tofree, echo_attr);
}
xfree(tofree);
}
@@ -19701,11 +19692,13 @@ void ex_execute(exarg_T *eap)
}
if (eap->cmdidx == CMD_echomsg) {
+ msg_ext_set_kind("echomsg");
MSG_ATTR(ga.ga_data, echo_attr);
ui_flush();
} else if (eap->cmdidx == CMD_echoerr) {
/* We don't want to abort following commands, restore did_emsg. */
save_did_emsg = did_emsg;
+ msg_ext_set_kind("echoerr");
EMSG((char_u *)ga.ga_data);
if (!force_abort)
did_emsg = save_did_emsg;
@@ -19902,13 +19895,15 @@ void ex_function(exarg_T *eap)
if (FUNCLINE(fp, j) == NULL)
continue;
msg_putchar('\n');
- msg_outnum((long)(j + 1));
- if (j < 9)
+ msg_outnum((long)j + 1);
+ if (j < 9) {
msg_putchar(' ');
- if (j < 99)
+ }
+ if (j < 99) {
msg_putchar(' ');
- msg_prt_line(FUNCLINE(fp, j), FALSE);
- ui_flush(); /* show a line at a time */
+ }
+ msg_prt_line(FUNCLINE(fp, j), false);
+ ui_flush(); // show a line at a time
os_breakcheck();
}
if (!got_int) {
@@ -19963,7 +19958,7 @@ void ex_function(exarg_T *eap)
goto errret_2;
}
- if (KeyTyped && ui_is_external(kUICmdline)) {
+ if (KeyTyped && ui_has(kUICmdline)) {
show_block = true;
ui_ext_cmdline_block_append(0, (const char *)eap->cmd);
}
@@ -20019,7 +20014,7 @@ void ex_function(exarg_T *eap)
if (!eap->skip && did_emsg)
goto erret;
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
msg_putchar('\n'); // don't overwrite the function name
}
cmdline_row = msg_row;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 4d75c7bda1..42999ddd62 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -7,6 +7,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval.h"
+#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/ascii.h"
#include "nvim/macros.h"
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 64658b52d9..6074e4ee69 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -19,6 +19,7 @@
#include "nvim/eval/typval.h"
#include "nvim/garray.h"
#include "nvim/mbyte.h"
+#include "nvim/math.h"
#include "nvim/message.h"
#include "nvim/memory.h"
#include "nvim/charset.h" // vim_isprintc()
@@ -28,11 +29,6 @@
#include "nvim/lib/kvec.h"
#include "nvim/eval/typval_encode.h"
-#ifdef __MINGW32__
-# undef fpclassify
-# define fpclassify __fpclassify
-#endif
-
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
@@ -327,7 +323,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
- switch (fpclassify(flt_)) { \
+ switch (xfpclassify(flt_)) { \
case FP_NAN: { \
ga_concat(gap, (char_u *) "str2float('nan')"); \
break; \
@@ -531,7 +527,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
- switch (fpclassify(flt_)) { \
+ switch (xfpclassify(flt_)) { \
case FP_NAN: { \
EMSG(_("E474: Unable to represent NaN value in JSON")); \
return FAIL; \
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 85844c37bd..b3ac456979 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -257,7 +257,7 @@ void ex_align(exarg_T *eap)
if (width <= 0)
width = curbuf->b_p_tw;
if (width == 0 && curbuf->b_p_wm > 0) {
- width = curwin->w_grid.Columns - curbuf->b_p_wm;
+ width = curwin->w_width_inner - curbuf->b_p_wm;
}
if (width <= 0) {
width = 80;
@@ -2678,6 +2678,10 @@ int do_ecmd(
theend:
+ if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->terminal != NULL) {
+ terminal_check_size(old_curbuf.br_buf->terminal);
+ }
+
if (did_inc_redrawing_disabled) {
RedrawingDisabled--;
}
@@ -2872,11 +2876,11 @@ void ex_z(exarg_T *eap)
// Vi compatible: ":z!" uses display height, without a count uses
// 'scroll'
if (eap->forceit) {
- bigness = curwin->w_grid.Rows;
+ bigness = curwin->w_height_inner;
} else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2;
} else {
- bigness = curwin->w_grid.Rows - 3;
+ bigness = curwin->w_height_inner - 3;
}
if (bigness < 1) {
bigness = 1;
@@ -4131,7 +4135,7 @@ skip:
buf_T *preview_buf = NULL;
size_t subsize = preview_lines.subresults.size;
if (preview && !aborting()) {
- if (got_quit) { // Substitution is too slow, disable 'inccommand'.
+ if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
SID_NONE);
} else if (*p_icm != NUL && pat != NULL) {
@@ -4895,7 +4899,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
}
}
- *matches = (char_u **)"";
+ *matches = NULL;
*num_matches = 0;
int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
if (keep_lang) {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index f5c16d883a..cda80dad39 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9191,33 +9191,38 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
if (restore_size && (ssop_flags & SSOP_WINSIZE)) {
for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
- if (!ses_do_win(wp))
+ if (!ses_do_win(wp)) {
continue;
- ++n;
+ }
+ n++;
- /* restore height when not full height */
+ // restore height when not full height
if (wp->w_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")",
- n, (int64_t)wp->w_grid.Rows,
- (int64_t)(Rows / 2), (int64_t)Rows) < 0
- || put_eol(fd) == FAIL))
+ n, (int64_t)wp->w_height,
+ (int64_t)Rows / 2, (int64_t)Rows) < 0
+ || put_eol(fd) == FAIL)) {
return FAIL;
+ }
- /* restore width when not full width */
+ // restore width when not full width
if (wp->w_width < Columns
- && (fprintf(fd, "exe 'vert %dresize ' . ((&columns * %" PRId64
- " + %" PRId64 ") / %" PRId64 ")",
- n, (int64_t)wp->w_width, (int64_t)(Columns / 2),
+ && (fprintf(fd,
+ "exe 'vert %dresize ' . ((&columns * %" PRId64
+ " + %" PRId64 ") / %" PRId64 ")",
+ n, (int64_t)wp->w_width, (int64_t)Columns / 2,
(int64_t)Columns) < 0
- || put_eol(fd) == FAIL))
+ || put_eol(fd) == FAIL)) {
return FAIL;
+ }
}
} else {
- /* Just equalise window sizes */
- if (put_line(fd, "wincmd =") == FAIL)
+ // Just equalise window sizes
+ if (put_line(fd, "wincmd =") == FAIL) {
return FAIL;
+ }
}
return OK;
}
@@ -9365,10 +9370,11 @@ put_view(
* arguments may have been deleted, check if the index is valid. */
if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp)
&& flagp == &ssop_flags) {
- if (fprintf(fd, "%" PRId64 "argu", (int64_t)(wp->w_arg_idx + 1)) < 0
- || put_eol(fd) == FAIL)
+ if (fprintf(fd, "%" PRId64 "argu", (int64_t)wp->w_arg_idx + 1) < 0
+ || put_eol(fd) == FAIL) {
return FAIL;
- did_next = TRUE;
+ }
+ did_next = true;
}
/* Edit the file. Skip this when ":next" already did it. */
@@ -9385,9 +9391,9 @@ put_view(
// Note, if a buffer for that file already exists, use :badd to
// edit that buffer, to not lose folding information (:edit resets
// folds in other buffers)
- if (fputs("if bufexists('", fd) < 0
+ if (fputs("if bufexists(\"", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
- || fputs("') | buffer ", fd) < 0
+ || fputs("\") | buffer ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
|| fputs(" | else | edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
@@ -9463,8 +9469,8 @@ put_view(
" * winheight(0) + %" PRId64 ") / %" PRId64 ")",
(int64_t)wp->w_cursor.lnum,
(int64_t)(wp->w_cursor.lnum - wp->w_topline),
- (int64_t)(wp->w_grid.Rows / 2),
- (int64_t)wp->w_grid.Rows) < 0
+ (int64_t)(wp->w_height_inner / 2),
+ (int64_t)wp->w_height_inner) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
|| put_line(fd, "exe s:l") == FAIL
@@ -9478,21 +9484,23 @@ put_view(
return FAIL;
} else {
if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) {
- if (fprintf(fd, "let s:c = %" PRId64 " - ((%" PRId64
- " * winwidth(0) + %" PRId64 ") / %" PRId64 ")",
- (int64_t)(wp->w_virtcol + 1),
+ if (fprintf(fd,
+ "let s:c = %" PRId64 " - ((%" PRId64
+ " * winwidth(0) + %" PRId64 ") / %" PRId64 ")",
+ (int64_t)wp->w_virtcol + 1,
(int64_t)(wp->w_virtcol - wp->w_leftcol),
(int64_t)(wp->w_width / 2),
(int64_t)wp->w_width) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "if s:c > 0") == FAIL
|| fprintf(fd, " exe 'normal! ' . s:c . '|zs' . %" PRId64 " . '|'",
- (int64_t)(wp->w_virtcol + 1)) < 0
+ (int64_t)wp->w_virtcol + 1) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "else") == FAIL
|| put_view_curpos(fd, wp, " ") == FAIL
- || put_line(fd, "endif") == FAIL)
+ || put_line(fd, "endif") == FAIL) {
return FAIL;
+ }
} else if (put_view_curpos(fd, wp, "") == FAIL) {
return FAIL;
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8efb027575..d70b81409d 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -51,6 +51,7 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
@@ -307,6 +308,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
gotocmdline(true);
redrawcmdprompt(); // draw prompt or indent
set_cmdspos();
+ if (!msg_scroll) {
+ msg_ext_clear(false);
+ }
}
s->xpc.xp_context = EXPAND_NOTHING;
s->xpc.xp_backslash = XP_BS_NONE;
@@ -493,8 +497,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
char_u *p = ccline.cmdbuff;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ui_call_cmdline_hide(ccline.level);
+ if (msg_ext_is_visible()) {
+ msg_ext_did_cmdline = true;
+ if (must_redraw < VALID) {
+ must_redraw = VALID;
+ }
+ }
}
cmdline_level--;
@@ -613,7 +623,7 @@ static int command_line_execute(VimState *state, int key)
if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
ui_call_wildmenu_hide();
}
if (s->xpc.xp_numfiles != -1) {
@@ -895,7 +905,7 @@ static int command_line_execute(VimState *state, int key)
}
if (!cmd_silent) {
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
ui_cursor_goto(msg_row, 0);
}
ui_flush();
@@ -1253,7 +1263,7 @@ static int command_line_handle_key(CommandLineState *s)
xfree(ccline.cmdbuff); // no commandline to return
ccline.cmdbuff = NULL;
- if (!cmd_silent && !ui_is_external(kUICmdline)) {
+ if (!cmd_silent && !ui_has(kUICmdline)) {
if (cmdmsg_rl) {
msg_col = Columns;
} else {
@@ -1703,7 +1713,7 @@ static int command_line_handle_key(CommandLineState *s)
s->do_abbr = false; // don't do abbreviation now
// may need to remove ^ when composing char was typed
if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
// TODO(bfredl): why not make unputcmdline also work with true?
unputcmdline();
} else {
@@ -1967,7 +1977,7 @@ static int command_line_changed(CommandLineState *s)
// Do it only when there are no characters left to read
// to avoid useless intermediate redraws.
// if cmdline is external the ui handles shaping, no redraw needed.
- if (!ui_is_external(kUICmdline) && vpeekc() == NUL) {
+ if (!ui_has(kUICmdline) && vpeekc() == NUL) {
redrawcmd();
}
}
@@ -2834,7 +2844,7 @@ static void draw_cmdline(int start, int len)
return;
}
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.special_char = NUL;
ccline.redraw_state = kCmdRedrawAll;
return;
@@ -3027,7 +3037,7 @@ void ui_ext_cmdline_block_leave(void)
/// assumes "redrawcmdline()" will already be invoked
void cmdline_screen_cleared(void)
{
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
return;
}
@@ -3052,7 +3062,7 @@ void cmdline_screen_cleared(void)
/// called by ui_flush, do what redraws neccessary to keep cmdline updated.
void cmdline_ui_flush(void)
{
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
return;
}
int level = ccline.level;
@@ -3081,7 +3091,7 @@ void putcmdline(int c, int shift)
if (cmd_silent) {
return;
}
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
msg_no_more = true;
msg_putchar(c);
if (shift) {
@@ -3107,7 +3117,7 @@ void unputcmdline(void)
return;
}
msg_no_more = true;
- if (ccline.cmdlen == ccline.cmdpos && !ui_is_external(kUICmdline)) {
+ if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
msg_putchar(' ');
} else {
draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos));
@@ -3393,14 +3403,11 @@ void cmdline_paste_str(char_u *s, int literally)
}
}
-/*
- * Delete characters on the command line, from "from" to the current
- * position.
- */
+/// Delete characters on the command line, from "from" to the current position.
static void cmdline_del(int from)
{
memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos,
- (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+ (size_t)ccline.cmdlen - ccline.cmdpos + 1);
ccline.cmdlen -= ccline.cmdpos - from;
ccline.cmdpos = from;
}
@@ -3425,7 +3432,7 @@ static void redrawcmdprompt(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.redraw_state = kCmdRedrawAll;
return;
}
@@ -3454,7 +3461,7 @@ void redrawcmd(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
draw_cmdline(0, ccline.cmdlen);
return;
}
@@ -3502,7 +3509,7 @@ static void cursorcmd(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
if (ccline.redraw_state < kCmdRedrawPos) {
ccline.redraw_state = kCmdRedrawPos;
}
@@ -3527,7 +3534,7 @@ static void cursorcmd(void)
void gotocmdline(int clr)
{
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
return;
}
msg_start();
@@ -3615,7 +3622,7 @@ nextwild (
return FAIL;
}
- if (!ui_is_external(kUIWildmenu)) {
+ if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
MSG_PUTS("..."); // show that we are busy
ui_flush();
}
@@ -3661,8 +3668,8 @@ nextwild (
xp->xp_pattern = ccline.cmdbuff + i;
}
memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
- &ccline.cmdbuff[ccline.cmdpos],
- (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+ &ccline.cmdbuff[ccline.cmdpos],
+ (size_t)ccline.cmdlen - ccline.cmdpos + 1);
memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
ccline.cmdlen += difflen;
ccline.cmdpos += difflen;
@@ -3765,7 +3772,7 @@ ExpandOne (
findex = -1;
}
if (p_wmnu) {
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
ui_call_wildmenu_select(findex);
} else {
win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
@@ -4120,7 +4127,7 @@ static int showmatches(expand_T *xp, int wildmenu)
showtail = cmd_showtail;
}
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
Array args = ARRAY_DICT_INIT;
for (i = 0; i < num_files; i++) {
ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i])));
@@ -4705,7 +4712,7 @@ ExpandFromContext (
return ret;
}
- *file = (char_u **)"";
+ *file = NULL;
*num_file = 0;
if (xp->xp_context == EXPAND_HELP) {
/* With an empty argument we would get all the help tags, which is
@@ -6054,7 +6061,12 @@ static int open_cmdwin(void)
/* Don't execute autocommands while creating the window. */
block_autocmds();
- /* don't use a new tab page */
+
+ // When using completion in Insert mode with <C-R>=<C-F> one can open the
+ // command line window, but we don't want the popup menu then.
+ pum_undisplay(true);
+
+ // don't use a new tab page
cmdmod.tab = 0;
cmdmod.noswapfile = 1;
@@ -6121,7 +6133,7 @@ static int open_cmdwin(void)
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
invalidate_botline();
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index ba154ea36a..6356290b9c 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6917,6 +6917,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_REMOTEREPLY
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX
+ || event == EVENT_SIGNAL
|| event == EVENT_TABCLOSED) {
fname = vim_strsave(fname);
} else {
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 2d2af54c95..74fd9d89cb 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -91,8 +91,8 @@ void ga_grow(garray_T *gap, int n)
}
int new_maxlen = gap->ga_len + n;
- size_t new_size = (size_t)(gap->ga_itemsize * new_maxlen);
- size_t old_size = (size_t)(gap->ga_itemsize * gap->ga_maxlen);
+ size_t new_size = (size_t)gap->ga_itemsize * (size_t)new_maxlen;
+ size_t old_size = (size_t)gap->ga_itemsize * (size_t)gap->ga_maxlen;
// reallocate and clear the new memory
char *pp = xrealloc(gap->ga_data, new_size);
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 58738df691..e2cbdd4eab 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -18,6 +18,7 @@ typedef struct growarray {
} garray_T;
#define GA_EMPTY_INIT_VALUE { 0, 0, 0, 1, NULL }
+#define GA_INIT(itemsize, growsize) { 0, 0, (itemsize), (growsize), NULL }
#define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0)
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index d3047e1a9c..40bdc3e30c 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -25,6 +25,7 @@ local c_id = (
local c_void = P('void')
local c_param_type = (
((P('Error') * fill * P('*') * fill) * Cc('error')) +
+ C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
(C(c_id) * (ws ^ 1))
)
local c_type = (C(c_void) * (ws ^ 1)) + c_param_type
@@ -43,6 +44,7 @@ local c_proto = Ct(
(fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) *
(fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) *
(fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) *
+ (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) *
fill * P(';')
)
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index bd9650e4d1..3703b76973 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -448,6 +448,7 @@ for _, fn in ipairs(functions) do
end
output:write(string.format([[
+void nlua_add_api_functions(lua_State *lstate); // silence -Wmissing-prototypes
void nlua_add_api_functions(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index e76b601d8a..c8ab310b02 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -54,7 +54,7 @@ for i = 1, #events do
ev = events[i]
assert(ev.return_type == 'void')
- if ev.since == nil then
+ if ev.since == nil and not ev.noexport then
print("Ui event "..ev.name.." lacks since field.\n")
os.exit(1)
end
@@ -65,7 +65,7 @@ for i = 1, #events do
write_signature(proto_output, ev, 'UI *ui')
proto_output:write(';\n')
- if not ev.remote_impl then
+ if not ev.remote_impl and not ev.noexport then
remote_output:write('static void remote_ui_'..ev.name)
write_signature(remote_output, ev, 'UI *ui')
remote_output:write('\n{\n')
@@ -74,8 +74,7 @@ for i = 1, #events do
remote_output:write('}\n\n')
end
- if not ev.bridge_impl then
-
+ if not ev.bridge_impl and not ev.noexport then
send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
argc = 1
for j = 1, #ev.parameters do
@@ -138,21 +137,36 @@ for i = 1, #events do
call_output:write('\n{\n')
if ev.remote_only then
write_arglist(call_output, ev, false)
- call_output:write(' UI_LOG('..ev.name..', 0);\n')
+ call_output:write(' UI_LOG('..ev.name..');\n')
call_output:write(' ui_event("'..ev.name..'", args);\n')
+ elseif ev.compositor_impl then
+ call_output:write(' UI_CALL')
+ write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
+ call_output:write(";\n")
else
call_output:write(' UI_CALL')
- write_signature(call_output, ev, ev.name, true)
+ write_signature(call_output, ev, 'true, '..ev.name..', ui', true)
call_output:write(";\n")
end
call_output:write("}\n\n")
end
+ if ev.compositor_impl then
+ call_output:write('void ui_composed_call_'..ev.name)
+ write_signature(call_output, ev, '')
+ call_output:write('\n{\n')
+ call_output:write(' UI_CALL')
+ write_signature(call_output, ev, 'ui->composed, '..ev.name..', ui', true)
+ call_output:write(";\n")
+ call_output:write("}\n\n")
+ end
+
end
proto_output:close()
call_output:close()
remote_output:close()
+bridge_output:close()
-- don't expose internal attributes like "impl_name" in public metadata
exported_attributes = {'name', 'parameters',
@@ -168,7 +182,9 @@ for _,ev in ipairs(events) do
p[1] = 'Dictionary'
end
end
- exported_events[#exported_events+1] = ev_exported
+ if not ev.noexport then
+ exported_events[#exported_events+1] = ev_exported
+ end
end
packed = mpack.pack(exported_events)
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 53e9846c2d..b24acb5ebb 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1187,10 +1187,11 @@ void save_typebuf(void)
alloc_typebuf();
}
-static int old_char = -1; /* character put back by vungetc() */
-static int old_mod_mask; /* mod_mask for ungotten character */
-static int old_mouse_row; /* mouse_row related to old_char */
-static int old_mouse_col; /* mouse_col related to old_char */
+static int old_char = -1; // character put back by vungetc()
+static int old_mod_mask; // mod_mask for ungotten character
+static int old_mouse_grid; // mouse_grid related to old_char
+static int old_mouse_row; // mouse_row related to old_char
+static int old_mouse_col; // mouse_col related to old_char
/*
@@ -1391,6 +1392,7 @@ int vgetc(void)
c = old_char;
old_char = -1;
mod_mask = old_mod_mask;
+ mouse_grid = old_mouse_grid;
mouse_row = old_mouse_row;
mouse_col = old_mouse_col;
} else {
@@ -1585,6 +1587,7 @@ vungetc ( /* unget one character (can only be done once!) */
{
old_char = c;
old_mod_mask = mod_mask;
+ old_mouse_grid = mouse_grid;
old_mouse_row = mouse_row;
old_mouse_col = mouse_col;
}
@@ -2120,8 +2123,8 @@ static int vgetorpeek(int advance)
++col;
}
curwin->w_wrow = curwin->w_cline_row
- + curwin->w_wcol / curwin->w_grid.Columns;
- curwin->w_wcol %= curwin->w_grid.Columns;
+ + curwin->w_wcol / curwin->w_width_inner;
+ curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
col = 0; /* no correction needed */
} else {
@@ -2130,7 +2133,7 @@ static int vgetorpeek(int advance)
}
} else if (curwin->w_p_wrap && curwin->w_wrow) {
curwin->w_wrow--;
- curwin->w_wcol = curwin->w_grid.Columns - 1;
+ curwin->w_wcol = curwin->w_width_inner - 1;
col = curwin->w_cursor.col - 1;
}
if (col > 0 && curwin->w_wcol > 0) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 2550fb8163..f47697b190 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -194,12 +194,15 @@ EXTERN int compl_cont_status INIT(= 0);
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
EXTERN int msg_col;
EXTERN int msg_row;
-EXTERN int msg_scrolled; /* Number of screen lines that windows have
- * scrolled because of printing messages. */
-EXTERN int msg_scrolled_ign INIT(= FALSE);
-/* when TRUE don't set need_wait_return in
- msg_puts_attr() when msg_scrolled is
- non-zero */
+EXTERN int msg_scrolled; // Number of screen lines that windows have
+ // scrolled because of printing messages.
+// when true don't set need_wait_return in msg_puts_attr()
+// when msg_scrolled is non-zero
+EXTERN bool msg_scrolled_ign INIT(= false);
+// Whether the screen is damaged due to scrolling. Sometimes msg_scrolled
+// is reset before the screen is redrawn, so we need to keep track of this.
+EXTERN bool msg_did_scroll INIT(= false);
+
EXTERN char_u *keep_msg INIT(= NULL); /* msg to be shown after redraw */
EXTERN int keep_msg_attr INIT(= 0); /* highlight attr for keep_msg */
@@ -394,9 +397,8 @@ EXTERN bufref_T au_new_curbuf INIT(= { NULL, 0, 0 });
EXTERN buf_T *au_pending_free_buf INIT(= NULL);
EXTERN win_T *au_pending_free_win INIT(= NULL);
-/*
- * Mouse coordinates, set by check_termcode()
- */
+// Mouse coordinates, set by handle_mouse_event()
+EXTERN int mouse_grid;
EXTERN int mouse_row;
EXTERN int mouse_col;
EXTERN bool mouse_past_bottom INIT(= false); /* mouse below last line */
@@ -843,26 +845,6 @@ extern char_u *compiled_sys;
* directory is not a local directory, globaldir is NULL. */
EXTERN char_u *globaldir INIT(= NULL);
-// 'listchars' characters. Defaults are overridden in set_chars_option().
-EXTERN int lcs_eol INIT(= '$');
-EXTERN int lcs_ext INIT(= NUL);
-EXTERN int lcs_prec INIT(= NUL);
-EXTERN int lcs_nbsp INIT(= NUL);
-EXTERN int lcs_space INIT(= NUL);
-EXTERN int lcs_tab1 INIT(= NUL);
-EXTERN int lcs_tab2 INIT(= NUL);
-EXTERN int lcs_trail INIT(= NUL);
-EXTERN int lcs_conceal INIT(= ' ');
-
-// 'fillchars' characters. Defaults are overridden in set_chars_option().
-EXTERN int fill_stl INIT(= ' ');
-EXTERN int fill_stlnc INIT(= ' ');
-EXTERN int fill_vert INIT(= 9474); // │
-EXTERN int fill_fold INIT(= 183); // ·
-EXTERN int fill_diff INIT(= '-');
-EXTERN int fill_msgsep INIT(= ' ');
-EXTERN int fill_eob INIT(= '~');
-
/* Whether 'keymodel' contains "stopsel" and "startsel". */
EXTERN int km_stopsel INIT(= FALSE);
EXTERN int km_startsel INIT(= FALSE);
@@ -1072,16 +1054,14 @@ EXTERN char_u e_cmdmap_key[] INIT(=N_(
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
-/* For undo we need to know the lowest time possible. */
+// For undo we need to know the lowest time possible.
EXTERN time_t starttime;
EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */
-/*
- * Some compilers warn for not using a return value, but in some situations we
- * can't do anything useful with the value. Assign to this variable to avoid
- * the warning.
- */
+// Some compilers warn for not using a return value, but in some situations we
+// can't do anything useful with the value. Assign to this variable to avoid
+// the warning.
EXTERN int vim_ignored;
// Start a msgpack-rpc channel over stdin/stdout.
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 37d85ead0c..d9ccdbbdab 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -1,6 +1,8 @@
#ifndef NVIM_GRID_DEFS_H
#define NVIM_GRID_DEFS_H
+#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include "nvim/types.h"
@@ -49,11 +51,14 @@ typedef struct {
int row_offset;
int col_offset;
- // grid size requested by the UI. Used for window grids only.
- int requested_rows;
- int requested_cols;
-
- int was_resized;
+ // state owned by the compositor.
+ int comp_row;
+ int comp_col;
+ size_t comp_index;
+ bool comp_disabled;
} ScreenGrid;
+#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \
+ false }
+
#endif // NVIM_GRID_DEFS_H
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 41d60fa3ea..4c5fca6d39 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -23,11 +23,15 @@ static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE;
static Map(HlEntry, int) *attr_entry_ids;
static Map(int, int) *combine_attr_entries;
+static Map(int, int) *blend_attr_entries;
+static Map(int, int) *blendthrough_attr_entries;
void highlight_init(void)
{
attr_entry_ids = map_new(HlEntry, int)();
combine_attr_entries = map_new(int, int)();
+ blend_attr_entries = map_new(int, int)();
+ blendthrough_attr_entries = map_new(int, int)();
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
@@ -101,6 +105,9 @@ static int get_attr_entry(HlEntry entry)
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
+ if (!ui->hl_attr_define) {
+ return;
+ }
for (size_t i = 1; i < kv_size(attr_entries); i++) {
Array inspect = hl_inspect((int)i);
ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
@@ -210,6 +217,8 @@ void clear_hl_tables(bool reinit)
kv_size(attr_entries) = 1;
map_clear(HlEntry, int)(attr_entry_ids);
map_clear(int, int)(combine_attr_entries);
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
highlight_attr_set_all();
highlight_changed();
screen_invalidate_highlights();
@@ -217,15 +226,22 @@ void clear_hl_tables(bool reinit)
kv_destroy(attr_entries);
map_free(HlEntry, int)(attr_entry_ids);
map_free(int, int)(combine_attr_entries);
+ map_free(int, int)(blend_attr_entries);
+ map_free(int, int)(blendthrough_attr_entries);
}
}
+void hl_invalidate_blends(void)
+{
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
+}
+
// Combine special attributes (e.g., for spelling) with other attributes
// (e.g., for syntax highlighting).
// "prim_attr" overrules "char_attr".
// This creates a new group when required.
-// Since we expect there to be few spelling mistakes we don't cache the
-// result.
+// Since we expect there to be a lot of spelling mistakes we cache the result.
// Return the resulting attributes.
int hl_combine_attr(int char_attr, int prim_attr)
{
@@ -280,6 +296,188 @@ int hl_combine_attr(int char_attr, int prim_attr)
return id;
}
+/// Get the used rgb colors for an attr group.
+///
+/// If colors are unset, use builtin default colors. Never returns -1
+/// Cterm colors are unchanged.
+static HlAttrs get_colors_force(int attr)
+{
+ HlAttrs attrs = syn_attr2entry(attr);
+ if (attrs.rgb_bg_color == -1) {
+ attrs.rgb_bg_color = normal_bg;
+ }
+ if (attrs.rgb_fg_color == -1) {
+ attrs.rgb_fg_color = normal_fg;
+ }
+ if (attrs.rgb_sp_color == -1) {
+ attrs.rgb_sp_color = normal_sp;
+ }
+ HL_SET_DEFAULT_COLORS(attrs.rgb_fg_color, attrs.rgb_bg_color,
+ attrs.rgb_sp_color);
+
+ if (attrs.rgb_ae_attr & HL_INVERSE) {
+ int temp = attrs.rgb_bg_color;
+ attrs.rgb_bg_color = attrs.rgb_fg_color;
+ attrs.rgb_fg_color = temp;
+ attrs.rgb_ae_attr &= ~HL_INVERSE;
+ }
+
+ return attrs;
+}
+
+/// Blend overlay attributes (for popupmenu) with other attributes
+///
+/// This creates a new group when required.
+/// This is called per-cell, so cache the result.
+///
+/// @return the resulting attributes.
+int hl_blend_attrs(int back_attr, int front_attr, bool through)
+{
+ int combine_tag = (back_attr << 16) + front_attr;
+ Map(int, int) *map = through ? blendthrough_attr_entries : blend_attr_entries;
+ int id = map_get(int, int)(map, combine_tag);
+ if (id > 0) {
+ return id;
+ }
+
+ HlAttrs battrs = get_colors_force(back_attr);
+ HlAttrs fattrs = get_colors_force(front_attr);
+ HlAttrs cattrs;
+ if (through) {
+ cattrs = battrs;
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb, battrs.rgb_fg_color,
+ fattrs.rgb_bg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb, battrs.rgb_sp_color,
+ fattrs.rgb_bg_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+
+ cattrs.cterm_bg_color = fattrs.cterm_bg_color;
+ cattrs.cterm_fg_color = cterm_blend((int)p_pb, battrs.cterm_fg_color,
+ fattrs.cterm_bg_color);
+ } else {
+ cattrs = fattrs;
+ if (p_pb >= 50) {
+ cattrs.rgb_ae_attr |= battrs.rgb_ae_attr;
+ }
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb/2, battrs.rgb_fg_color,
+ fattrs.rgb_fg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb/2, battrs.rgb_bg_color,
+ fattrs.rgb_sp_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+ }
+ cattrs.rgb_bg_color = rgb_blend((int)p_pb, battrs.rgb_bg_color,
+ fattrs.rgb_bg_color);
+
+ HlKind kind = through ? kHlBlendThrough : kHlBlend;
+ id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind,
+ .id1 = back_attr, .id2 = front_attr });
+ if (id > 0) {
+ map_put(int, int)(map, combine_tag, id);
+ }
+ return id;
+}
+
+static int rgb_blend(int ratio, int rgb1, int rgb2)
+{
+ int a = ratio, b = 100-ratio;
+ int r1 = (rgb1 & 0xFF0000) >> 16;
+ int g1 = (rgb1 & 0x00FF00) >> 8;
+ int b1 = (rgb1 & 0x0000FF) >> 0;
+ int r2 = (rgb2 & 0xFF0000) >> 16;
+ int g2 = (rgb2 & 0x00FF00) >> 8;
+ int b2 = (rgb2 & 0x0000FF) >> 0;
+ int mr = (a * r1 + b * r2)/100;
+ int mg = (a * g1 + b * g2)/100;
+ int mb = (a * b1 + b * b2)/100;
+ return (mr << 16) + (mg << 8) + mb;
+}
+
+static int cterm_blend(int ratio, int c1, int c2)
+{
+ // 1. Convert cterm color numbers to RGB.
+ // 2. Blend the RGB colors.
+ // 3. Convert the RGB result to a cterm color.
+ int rgb1 = hl_cterm2rgb_color(c1);
+ int rgb2 = hl_cterm2rgb_color(c2);
+ int rgb_blended = rgb_blend(ratio, rgb1, rgb2);
+ return hl_rgb2cterm_color(rgb_blended);
+}
+
+/// Converts RGB color to 8-bit color (0-255).
+static int hl_rgb2cterm_color(int rgb)
+{
+ int r = (rgb & 0xFF0000) >> 16;
+ int g = (rgb & 0x00FF00) >> 8;
+ int b = (rgb & 0x0000FF) >> 0;
+
+ return (r * 6 / 256) * 36 + (g * 6 / 256) * 6 + (b * 6 / 256);
+}
+
+/// Converts 8-bit color (0-255) to RGB color.
+/// This is compatible with xterm.
+static int hl_cterm2rgb_color(int nr)
+{
+ static int cube_value[] = {
+ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF
+ };
+ static int grey_ramp[] = {
+ 0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
+ 0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
+ };
+ static char_u ansi_table[16][4] = {
+ // R G B idx
+ { 0, 0, 0, 1 } , // black
+ { 224, 0, 0, 2 } , // dark red
+ { 0, 224, 0, 3 } , // dark green
+ { 224, 224, 0, 4 } , // dark yellow / brown
+ { 0, 0, 224, 5 } , // dark blue
+ { 224, 0, 224, 6 } , // dark magenta
+ { 0, 224, 224, 7 } , // dark cyan
+ { 224, 224, 224, 8 } , // light grey
+
+ { 128, 128, 128, 9 } , // dark grey
+ { 255, 64, 64, 10 } , // light red
+ { 64, 255, 64, 11 } , // light green
+ { 255, 255, 64, 12 } , // yellow
+ { 64, 64, 255, 13 } , // light blue
+ { 255, 64, 255, 14 } , // light magenta
+ { 64, 255, 255, 15 } , // light cyan
+ { 255, 255, 255, 16 } , // white
+ };
+
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int idx;
+ // *ansi_idx = 0;
+
+ if (nr < 16) {
+ r = ansi_table[nr][0];
+ g = ansi_table[nr][1];
+ b = ansi_table[nr][2];
+ // *ansi_idx = ansi_table[nr][3];
+ } else if (nr < 232) { // 216 color-cube
+ idx = nr - 16;
+ r = cube_value[idx / 36 % 6];
+ g = cube_value[idx / 6 % 6];
+ b = cube_value[idx % 6];
+ // *ansi_idx = -1;
+ } else if (nr < 256) { // 24 greyscale ramp
+ idx = nr - 232;
+ r = grey_ramp[idx];
+ g = grey_ramp[idx];
+ b = grey_ramp[idx];
+ // *ansi_idx = -1;
+ }
+ return (r << 16) + (g << 8) + b;
+}
+
/// Get highlight attributes for a attribute code
HlAttrs syn_attr2entry(int attr)
{
@@ -403,6 +601,8 @@ static void hl_inspect_impl(Array *arr, int attr)
break;
case kHlCombine:
+ case kHlBlend:
+ case kHlBlendThrough:
// attribute combination is associative, so flatten to an array
hl_inspect_impl(arr, e.id1);
hl_inspect_impl(arr, e.id2);
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 6be0d6200b..a237ddbc34 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -10,4 +10,12 @@
# include "highlight.h.generated.h"
#endif
+# define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \
+ do { \
+ bool dark_ = (*p_bg == 'd'); \
+ rgb_fg = rgb_fg != -1 ? rgb_fg : (dark_ ? 0xFFFFFF : 0x000000); \
+ rgb_bg = rgb_bg != -1 ? rgb_bg : (dark_ ? 0x000000 : 0xFFFFFF); \
+ rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; \
+ } while (0);
+
#endif // NVIM_HIGHLIGHT_H
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 40025fcbbb..1da33bfea5 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -160,6 +160,8 @@ typedef enum {
kHlSyntax,
kHlTerminal,
kHlCombine,
+ kHlBlend,
+ kHlBlendThrough,
} HlKind;
typedef struct {
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 13534ac1a9..8e20aa5be4 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -61,7 +61,8 @@ int get_indent_str(char_u *ptr, int ts, int list)
for (; *ptr; ++ptr) {
// Count a tab for what it is worth.
if (*ptr == TAB) {
- if (!list || lcs_tab1) { // count a tab for what it is worth
+ if (!list || curwin->w_p_lcs_chars.tab1) {
+ // count a tab for what it is worth
count += ts - (count % ts);
} else {
// In list mode, when tab is not set, count screen char width
@@ -204,13 +205,12 @@ int set_indent(int size, int flags)
// characters and allocate accordingly. We will fill the rest with spaces
// after the if (!curbuf->b_p_et) below.
if (orig_char_len != -1) {
- assert(orig_char_len + size - ind_done + line_len >= 0);
- size_t n; // = orig_char_len + size - ind_done + line_len
- size_t n2;
- STRICT_ADD(orig_char_len, size, &n, size_t);
- STRICT_ADD(ind_done, line_len, &n2, size_t);
- STRICT_SUB(n, n2, &n, size_t);
- newline = xmalloc(n);
+ int newline_size; // = orig_char_len + size - ind_done + line_len
+ STRICT_ADD(orig_char_len, size, &newline_size, int);
+ STRICT_SUB(newline_size, ind_done, &newline_size, int);
+ STRICT_ADD(newline_size, line_len, &newline_size, int);
+ assert(newline_size >= 0);
+ newline = xmalloc((size_t)newline_size);
todo = size - ind_done;
// Set total length of indent in characters, which may have been
@@ -474,7 +474,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
static varnumber_T prev_tick = 0; // Changedtick of cached value.
int bri = 0;
// window width minus window margin space, i.e. what rests for text
- const int eff_wwidth = wp->w_grid.Columns
+ const int eff_wwidth = wp->w_width_inner
- ((wp->w_p_nu || wp->w_p_rnu)
&& (vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
? number_width(wp) + 1 : 0);
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 4d912c452b..8066b6e828 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -12,6 +12,7 @@
#endif
#include <uv.h>
+#include "auto/config.h"
#include "nvim/log.h"
#include "nvim/types.h"
#include "nvim/os/os.h"
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 7d4c033565..17ff095473 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -4,6 +4,8 @@
#include <stdio.h>
#include <stdbool.h>
+#include "auto/config.h"
+
#define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1
#define WARN_LOG_LEVEL 2
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index a7bda9d037..93069893cf 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -50,7 +50,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
size_t len;
const char *const str = lua_tolstring(lstate, -1, &len);
- emsgf(msg, (int)len, str);
+ emsgf_multiline(msg, (int)len, str);
lua_pop(lstate, 1);
}
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index d447bff765..61009528a8 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -198,4 +198,25 @@
# define IO_COUNT(x) (x)
#endif
+///
+/// PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
+///
+#if defined(__clang__) && __clang__ == 1
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"")
+# define PRAGMA_DIAG_POP \
+ _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+# define PRAGMA_DIAG_POP \
+ _Pragma("GCC diagnostic pop")
+#else
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
+# define PRAGMA_DIAG_POP
+#endif
+
+
#endif // NVIM_MACROS_H
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 17f3a894d4..c0dea196fb 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -154,6 +154,7 @@ void event_init(void)
remote_ui_init();
api_vim_init();
terminal_init();
+ ui_init();
}
/// @returns false if main_loop could not be closed gracefully
@@ -221,6 +222,7 @@ void early_init(void)
}
#ifdef MAKE_LIB
+int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes
int nvim_main(int argc, char **argv)
#elif defined(WIN32)
int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
@@ -298,7 +300,7 @@ int main(int argc, char **argv)
assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
msg_row = cmdline_row;
- screenalloc(false); // allocate screen buffers
+ screenalloc(); // allocate screen buffers
set_init_2(headless_mode);
TIME_MSG("inits 2");
diff --git a/src/nvim/math.c b/src/nvim/math.c
new file mode 100644
index 0000000000..b51f335ed7
--- /dev/null
+++ b/src/nvim/math.c
@@ -0,0 +1,42 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <math.h>
+
+#include "nvim/math.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "math.c.generated.h"
+#endif
+
+#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6
+// Workaround glibc + Clang 6+ bug. #8274
+// https://bugzilla.redhat.com/show_bug.cgi?id=1472437
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wconversion"
+#endif
+int xfpclassify(double d)
+{
+#if defined(__MINGW32__)
+ // Workaround mingw warning. #7863
+ return __fpclassify(d);
+#else
+ return fpclassify(d);
+#endif
+}
+int xisinf(double d)
+{
+ return isinf(d);
+}
+int xisnan(double d)
+{
+#if defined(__MINGW32__)
+ // Workaround mingw warning. #7863
+ return _isnan(d);
+#else
+ return isnan(d);
+#endif
+}
+#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6
+# pragma clang diagnostic pop
+#endif
diff --git a/src/nvim/math.h b/src/nvim/math.h
new file mode 100644
index 0000000000..7969323905
--- /dev/null
+++ b/src/nvim/math.h
@@ -0,0 +1,7 @@
+#ifndef NVIM_MATH_H
+#define NVIM_MATH_H
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "math.h.generated.h"
+#endif
+#endif // NVIM_MATH_H
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 4c4f7d65bd..662eda3c7c 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3412,7 +3412,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
# endif
xfree(name);
- /* pretend screen didn't scroll, need redraw anyway */
+ // pretend screen didn't scroll, need redraw anyway
+ // TODO(bfredl): when doing the message grid refactor,
+ // simplify this special case.
msg_scrolled = 0;
redraw_all_later(NOT_VALID);
}
diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h
index 34a002af5d..edd933b2cd 100644
--- a/src/nvim/memline_defs.h
+++ b/src/nvim/memline_defs.h
@@ -3,54 +3,65 @@
#include "nvim/memfile_defs.h"
-/*
- * When searching for a specific line, we remember what blocks in the tree
- * are the branches leading to that block. This is stored in ml_stack. Each
- * entry is a pointer to info in a block (may be data block or pointer block)
- */
+///
+/// When searching for a specific line, we remember what blocks in the tree
+/// are the branches leading to that block. This is stored in ml_stack. Each
+/// entry is a pointer to info in a block (may be data block or pointer block)
+///
typedef struct info_pointer {
- blocknr_T ip_bnum; /* block number */
- linenr_T ip_low; /* lowest lnum in this block */
- linenr_T ip_high; /* highest lnum in this block */
- int ip_index; /* index for block with current lnum */
-} infoptr_T; /* block/index pair */
+ blocknr_T ip_bnum; // block number
+ linenr_T ip_low; // lowest lnum in this block
+ linenr_T ip_high; // highest lnum in this block
+ int ip_index; // index for block with current lnum
+} infoptr_T; // block/index pair
typedef struct ml_chunksize {
int mlcs_numlines;
long mlcs_totalsize;
} chunksize_T;
-/* Flags when calling ml_updatechunk() */
-
+// Flags when calling ml_updatechunk()
#define ML_CHNK_ADDLINE 1
#define ML_CHNK_DELLINE 2
#define ML_CHNK_UPDLINE 3
-/*
- * the memline structure holds all the information about a memline
- */
+/// memline structure: the contents of a buffer.
+/// Essentially a tree with a branch factor of 128.
+/// Lines are stored at leaf nodes.
+/// Nodes are stored on ml_mfp (memfile_T):
+/// pointer_block: internal nodes
+/// data_block: leaf nodes
+///
+/// Memline also has "chunks" of 800 lines that are separate from the 128-tree
+/// structure, primarily used to speed up line2byte() and byte2line().
+///
+/// Motivation: If you have a file that is 10000 lines long, and you insert
+/// a line at linenr 1000, you don't want to move 9000 lines in
+/// memory. With this structure it is roughly (N * 128) pointer
+/// moves, where N is the height (typically 1-3).
+///
typedef struct memline {
- linenr_T ml_line_count; /* number of lines in the buffer */
+ linenr_T ml_line_count; // number of lines in the buffer
- memfile_T *ml_mfp; /* pointer to associated memfile */
+ memfile_T *ml_mfp; // pointer to associated memfile
-#define ML_EMPTY 1 /* empty buffer */
-#define ML_LINE_DIRTY 2 /* cached line was changed and allocated */
-#define ML_LOCKED_DIRTY 4 /* ml_locked was changed */
-#define ML_LOCKED_POS 8 /* ml_locked needs positive block number */
+#define ML_EMPTY 1 // empty buffer
+#define ML_LINE_DIRTY 2 // cached line was changed and allocated
+#define ML_LOCKED_DIRTY 4 // ml_locked was changed
+#define ML_LOCKED_POS 8 // ml_locked needs positive block number
int ml_flags;
- infoptr_T *ml_stack; /* stack of pointer blocks (array of IPTRs) */
- int ml_stack_top; /* current top of ml_stack */
- int ml_stack_size; /* total number of entries in ml_stack */
+ infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs)
+ int ml_stack_top; // current top of ml_stack
+ int ml_stack_size; // total number of entries in ml_stack
- linenr_T ml_line_lnum; /* line number of cached line, 0 if not valid */
- char_u *ml_line_ptr; /* pointer to cached line */
+ linenr_T ml_line_lnum; // line number of cached line, 0 if not valid
+ char_u *ml_line_ptr; // pointer to cached line
- bhdr_T *ml_locked; /* block used by last ml_get */
- linenr_T ml_locked_low; /* first line in ml_locked */
- linenr_T ml_locked_high; /* last line in ml_locked */
- int ml_locked_lineadd; /* number of lines inserted in ml_locked */
+ bhdr_T *ml_locked; // block used by last ml_get
+ linenr_T ml_locked_low; // first line in ml_locked
+ linenr_T ml_locked_high; // last line in ml_locked
+ int ml_locked_lineadd; // number of lines inserted in ml_locked
chunksize_T *ml_chunksize;
int ml_numchunks;
int ml_usedchunks;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index d38079ca72..b49b521bc9 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -18,35 +18,15 @@
#include "nvim/ui.h"
#include "nvim/api/vim.h"
-#ifdef HAVE_JEMALLOC
-// Force je_ prefix on jemalloc functions.
-# define JEMALLOC_NO_DEMANGLE
-# include <jemalloc/jemalloc.h>
-#endif
-
#ifdef UNIT_TESTING
# define malloc(size) mem_malloc(size)
# define calloc(count, size) mem_calloc(count, size)
# define realloc(ptr, size) mem_realloc(ptr, size)
# define free(ptr) mem_free(ptr)
-# ifdef HAVE_JEMALLOC
-MemMalloc mem_malloc = &je_malloc;
-MemFree mem_free = &je_free;
-MemCalloc mem_calloc = &je_calloc;
-MemRealloc mem_realloc = &je_realloc;
-# else
MemMalloc mem_malloc = &malloc;
MemFree mem_free = &free;
MemCalloc mem_calloc = &calloc;
MemRealloc mem_realloc = &realloc;
-# endif
-#else
-# ifdef HAVE_JEMALLOC
-# define malloc(size) je_malloc(size)
-# define calloc(count, size) je_calloc(count, size)
-# define realloc(ptr, size) je_realloc(ptr, size)
-# define free(ptr) je_free(ptr)
-# endif
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 1c54db10eb..aea297fce2 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -660,7 +660,8 @@ static void free_menu_string(vimmenu_T *menu, int idx)
///
/// @param[in] menu if null, starts from root_menu
/// @param modes, a choice of \ref MENU_MODES
-/// @return a dict with name/commands
+/// @return dict with name/commands
+/// @see show_menus_recursive
/// @see menu_get
static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
{
@@ -715,10 +716,10 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
// visit recursively all children
list_T *const children_list = tv_list_alloc(kListLenMayKnow);
for (menu = menu->children; menu != NULL; menu = menu->next) {
- dict_T *dic = menu_get_recursive(menu, modes);
- if (tv_dict_len(dict) > 0) {
- tv_list_append_dict(children_list, dic);
- }
+ dict_T *d = menu_get_recursive(menu, modes);
+ if (tv_dict_len(d) > 0) {
+ tv_list_append_dict(children_list, d);
+ }
}
tv_dict_add_list(dict, S_LEN("submenus"), children_list);
}
@@ -734,42 +735,51 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
/// @return false if could not find path_name
bool menu_get(char_u *const path_name, int modes, list_T *list)
{
- vimmenu_T *menu;
- menu = find_menu(root_menu, path_name, modes);
+ vimmenu_T *menu = find_menu(root_menu, path_name, modes);
if (!menu) {
return false;
}
for (; menu != NULL; menu = menu->next) {
- dict_T *dict = menu_get_recursive(menu, modes);
- if (dict && tv_dict_len(dict) > 0) {
- tv_list_append_dict(list, dict);
+ dict_T *d = menu_get_recursive(menu, modes);
+ if (d && tv_dict_len(d) > 0) {
+ tv_list_append_dict(list, d);
+ }
+ if (*path_name != NUL) {
+ // If a (non-empty) path query was given, only the first node in the
+ // find_menu() result is relevant. Else we want all nodes.
+ break;
}
}
return true;
}
-/// Find menu matching required name and modes
+/// Find menu matching `name` and `modes`.
///
/// @param menu top menu to start looking from
/// @param name path towards the menu
/// @return menu if \p name is null, found menu or NULL
-vimmenu_T *
-find_menu(vimmenu_T *menu, char_u * name, int modes)
+static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
{
char_u *p;
while (*name) {
+ // find the end of one dot-separated name and put a NUL at the dot
p = menu_name_skip(name);
while (menu != NULL) {
if (menu_name_equal(name, menu)) {
- /* Found menu */
+ // Found menu
if (*p != NUL && menu->children == NULL) {
- EMSG(_(e_notsubmenu));
- return NULL;
- } else if ((menu->modes & modes) == 0x0) {
- EMSG(_(e_othermode));
- return NULL;
+ if (*p != NUL) {
+ EMSG(_(e_notsubmenu));
+ return NULL;
+ } else if ((menu->modes & modes) == 0x0) {
+ EMSG(_(e_othermode));
+ return NULL;
+ }
+ }
+ if (*p == NUL) { // found a full match
+ return menu;
}
break;
}
@@ -780,6 +790,7 @@ find_menu(vimmenu_T *menu, char_u * name, int modes)
EMSG2(_(e_nomenu), name);
return NULL;
}
+ // Found a match, search the sub-menu.
name = p;
menu = menu->children;
}
@@ -1235,7 +1246,7 @@ static char_u *popup_mode_name(char_u *name, int idx)
///
/// @return a pointer to allocated memory.
static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *p;
@@ -1520,7 +1531,7 @@ static char_u *menutrans_lookup(char_u *name, int len)
char_u *dname;
for (int i = 0; i < menutrans_ga.ga_len; i++) {
- if (STRNCMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) {
+ if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) {
return tp[i].to;
}
}
@@ -1531,7 +1542,7 @@ static char_u *menutrans_lookup(char_u *name, int len)
dname = menu_text(name, NULL, NULL);
name[len] = c;
for (int i = 0; i < menutrans_ga.ga_len; i++) {
- if (STRCMP(dname, tp[i].from_noamp) == 0) {
+ if (STRICMP(dname, tp[i].from_noamp) == 0) {
xfree(dname);
return tp[i].to;
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 37e40c3cc1..b22508c23f 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -34,11 +34,13 @@
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
+#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
+#include "nvim/api/private/helpers.h"
/*
* To be able to scroll back at the "more" and "hit-enter" prompts we need to
@@ -108,6 +110,19 @@ static int verbose_did_open = FALSE;
* This is an allocated string or NULL when not used.
*/
+
+// Extended msg state, currently used for external UIs with ext_messages
+static const char *msg_ext_kind = NULL;
+static Array msg_ext_chunks = ARRAY_DICT_INIT;
+static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
+static sattr_T msg_ext_last_attr = -1;
+
+static bool msg_ext_overwrite = false; ///< will overwrite last message
+static int msg_ext_visible = 0; ///< number of messages currently visible
+
+/// Shouldn't clear message after leaving cmdline
+static bool msg_ext_keep_after_cmdline = false;
+
/*
* msg(s) - displays the string 's' on the status line
* When terminal not initialized (yet) mch_errmsg(..) is used.
@@ -115,7 +130,7 @@ static int verbose_did_open = FALSE;
*/
int msg(char_u *s)
{
- return msg_attr_keep(s, 0, FALSE);
+ return msg_attr_keep(s, 0, false, false);
}
/*
@@ -126,29 +141,62 @@ int verb_msg(char_u *s)
int n;
verbose_enter();
- n = msg_attr_keep(s, 0, FALSE);
+ n = msg_attr_keep(s, 0, false, false);
verbose_leave();
return n;
}
-int msg_attr(const char *s, const int attr) FUNC_ATTR_NONNULL_ARG(1)
+int msg_attr(const char *s, const int attr)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- return msg_attr_keep((char_u *)s, attr, false);
+ return msg_attr_keep((char_u *)s, attr, false, false);
}
-int
-msg_attr_keep (
- char_u *s,
- int attr,
- int keep /* TRUE: set keep_msg if it doesn't scroll */
-)
- FUNC_ATTR_NONNULL_ARG(1)
+/// similar to msg_outtrans_attr, but support newlines and tabs.
+void msg_multiline_attr(const char *s, int attr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *next_spec = s;
+
+ while (next_spec != NULL) {
+ next_spec = strpbrk(s, "\t\n\r");
+
+ if (next_spec != NULL) {
+ // Printing all char that are before the char found by strpbrk
+ msg_outtrans_len_attr((char_u *)s, next_spec - s, attr);
+
+ if (*next_spec != TAB) {
+ msg_clr_eos();
+ }
+ msg_putchar_attr((uint8_t)(*next_spec), attr);
+ s = next_spec + 1;
+ }
+ }
+
+ // Print the rest of the message. We know there is no special
+ // character because strpbrk returned NULL
+ if (*s != NUL) {
+ msg_outtrans_attr((char_u *)s, attr);
+ }
+}
+
+
+/// @param keep set keep_msg if it doesn't scroll
+bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline)
+ FUNC_ATTR_NONNULL_ALL
{
static int entered = 0;
int retval;
char_u *buf = NULL;
+ if (keep && multiline) {
+ // Not implemented. 'multiline' is only used by nvim-added messages,
+ // which should avoid 'keep' behavior (just show the message at
+ // the correct time already).
+ abort();
+ }
+
// Skip messages not match ":filter pattern".
// Don't filter when there is an error.
if (!emsg_on_display && message_filtered(s)) {
@@ -175,7 +223,7 @@ msg_attr_keep (
&& last_msg_hist != NULL
&& last_msg_hist->msg != NULL
&& STRCMP(s, last_msg_hist->msg))) {
- add_msg_hist((const char *)s, -1, attr);
+ add_msg_hist((const char *)s, -1, attr, multiline);
}
/* When displaying keep_msg, don't let msg_start() free it, caller must do
@@ -189,13 +237,18 @@ msg_attr_keep (
if (buf != NULL)
s = buf;
- msg_outtrans_attr(s, attr);
+ if (multiline) {
+ msg_multiline_attr((char *)s, attr);
+ } else {
+ msg_outtrans_attr(s, attr);
+ }
msg_clr_eos();
retval = msg_end();
if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1)
- * Columns + sc_col)
+ * Columns + sc_col) {
set_keep_msg(s, 0);
+ }
xfree(buf);
--entered;
@@ -218,7 +271,8 @@ msg_strtrunc (
/* May truncate message to avoid a hit-return prompt */
if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
- && !exmode_active && msg_silent == 0) || force) {
+ && !exmode_active && msg_silent == 0 && !ui_has(kUIMessages))
+ || force) {
len = vim_strsize(s);
if (msg_scrolled != 0)
/* Use all the columns. */
@@ -468,17 +522,8 @@ int emsg_not_now(void)
return FALSE;
}
-/*
- * emsg() - display an error message
- *
- * Rings the bell, if appropriate, and calls message() to do the real work
- * When terminal not initialized (yet) mch_errmsg(..) is used.
- *
- * return TRUE if wait_return not called
- */
-int emsg(const char_u *s_)
+static bool emsg_multiline(const char *s, bool multiline)
{
- const char *s = (const char *)s_;
int attr;
int ignore = false;
int severe;
@@ -565,6 +610,9 @@ int emsg(const char_u *s_)
} // wait_return has reset need_wait_return
// and a redraw is expected because
// msg_scrolled is non-zero
+ if (msg_ext_kind == NULL) {
+ msg_ext_set_kind("emsg");
+ }
/*
* Display name and line number for the source of the error.
@@ -573,7 +621,18 @@ int emsg(const char_u *s_)
// Display the error message itself.
msg_nowait = false; // Wait for this msg.
- return msg_attr(s, attr);
+ return msg_attr_keep((char_u *)s, attr, false, multiline);
+}
+
+/// emsg() - display an error message
+///
+/// Rings the bell, if appropriate, and calls message() to do the real work
+/// When terminal not initialized (yet) mch_errmsg(..) is used.
+///
+/// @return true if wait_return not called
+bool emsg(const char_u *s)
+{
+ return emsg_multiline((const char *)s, false);
}
void emsg_invreg(int name)
@@ -595,6 +654,28 @@ bool emsgf(const char *const fmt, ...)
return ret;
}
+#define MULTILINE_BUFSIZE 8192
+
+bool emsgf_multiline(const char *const fmt, ...)
+{
+ bool ret;
+ va_list ap;
+
+
+ static char errbuf[MULTILINE_BUFSIZE];
+ if (emsg_not_now()) {
+ return true;
+ }
+
+ va_start(ap, fmt);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ va_end(ap);
+
+ ret = emsg_multiline(errbuf, true);
+
+ return ret;
+}
+
/// Print an error message with unknown number of arguments
static bool emsgfv(const char *fmt, va_list ap)
{
@@ -669,7 +750,7 @@ char_u *msg_trunc_attr(char_u *s, int force, int attr)
int n;
// Add message to history before truncating.
- add_msg_hist((const char *)s, -1, attr);
+ add_msg_hist((const char *)s, -1, attr, false);
s = msg_may_trunc(force, s);
@@ -715,7 +796,7 @@ char_u *msg_may_trunc(int force, char_u *s)
}
/// @param[in] len Length of s or -1.
-static void add_msg_hist(const char *s, int len, int attr)
+static void add_msg_hist(const char *s, int len, int attr, bool multiline)
{
if (msg_hist_off || msg_silent != 0)
return;
@@ -739,12 +820,16 @@ static void add_msg_hist(const char *s, int len, int attr)
p->msg = (char_u *)xmemdupz(s, (size_t)len);
p->next = NULL;
p->attr = attr;
- if (last_msg_hist != NULL)
+ p->multiline = multiline;
+ p->kind = msg_ext_kind;
+ if (last_msg_hist != NULL) {
last_msg_hist->next = p;
+ }
last_msg_hist = p;
- if (first_msg_hist == NULL)
+ if (first_msg_hist == NULL) {
first_msg_hist = last_msg_hist;
- ++msg_hist_len;
+ }
+ msg_hist_len++;
}
/*
@@ -791,7 +876,6 @@ void ex_messages(void *const eap_p)
return;
}
- msg_hist_off = true;
p = first_msg_hist;
@@ -809,13 +893,31 @@ void ex_messages(void *const eap_p)
}
// Display what was not skipped.
- for (; p != NULL && !got_int; p = p->next) {
- if (p->msg != NULL) {
- msg_attr((const char *)p->msg, p->attr);
+ if (ui_has(kUIMessages)) {
+ Array entries = ARRAY_DICT_INIT;
+ for (; p != NULL; p = p->next) {
+ if (p->msg != NULL && p->msg[0] != NUL) {
+ Array entry = ARRAY_DICT_INIT;
+ ADD(entry, STRING_OBJ(cstr_to_string(p->kind)));
+ Array content_entry = ARRAY_DICT_INIT;
+ ADD(content_entry, INTEGER_OBJ(p->attr));
+ ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg))));
+ Array content = ARRAY_DICT_INIT;
+ ADD(content, ARRAY_OBJ(content_entry));
+ ADD(entry, ARRAY_OBJ(content));
+ ADD(entries, ARRAY_OBJ(entry));
+ }
+ }
+ ui_call_msg_history_show(entries);
+ } else {
+ msg_hist_off = true;
+ for (; p != NULL && !got_int; p = p->next) {
+ if (p->msg != NULL) {
+ msg_attr_keep(p->msg, p->attr, false, p->multiline);
+ }
}
+ msg_hist_off = false;
}
-
- msg_hist_off = false;
}
/*
@@ -881,9 +983,9 @@ void wait_return(int redraw)
c = CAR; /* no need for a return in ex mode */
got_int = FALSE;
} else {
- /* Make sure the hit-return prompt is on screen when 'guioptions' was
- * just changed. */
- screenalloc(false);
+ // Make sure the hit-return prompt is on screen when 'guioptions' was
+ // just changed.
+ screenalloc();
State = HITRETURN;
setmouse();
@@ -993,8 +1095,9 @@ void wait_return(int redraw)
if (c == ':' || c == '?' || c == '/') {
if (!exmode_active)
cmdline_row = msg_row;
- skip_redraw = TRUE; /* skip redraw once */
- do_redraw = FALSE;
+ skip_redraw = true; // skip redraw once
+ do_redraw = false;
+ msg_ext_keep_after_cmdline = true;
}
/*
@@ -1019,9 +1122,13 @@ void wait_return(int redraw)
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
ui_refresh();
- } else if (!skip_redraw
- && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) {
- redraw_later(VALID);
+ } else if (!skip_redraw) {
+ if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
+ redraw_later(VALID);
+ }
+ if (ui_has(kUIMessages)) {
+ msg_ext_clear(true);
+ }
}
}
@@ -1035,8 +1142,10 @@ static void hit_return_msg(void)
p_more = FALSE; /* don't want see this message when scrolling back */
if (msg_didout) /* start on a new line */
msg_putchar('\n');
- if (got_int)
+ msg_ext_set_kind("return_prompt");
+ if (got_int) {
MSG_PUTS(_("Interrupt: "));
+ }
MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
if (!msg_use_printf()) {
@@ -1059,6 +1168,17 @@ void set_keep_msg(char_u *s, int attr)
keep_msg_attr = attr;
}
+void msg_ext_set_kind(const char *msg_kind)
+{
+ // Don't change the label of an existing batch:
+ msg_ext_ui_flush();
+
+ // TODO(bfredl): would be nice to avoid dynamic scoping, but that would
+ // need refactoring the msg_ interface to not be "please pretend nvim is
+ // a terminal for a moment"
+ msg_ext_kind = msg_kind;
+}
+
/*
* Prepare for outputting characters in the command line.
*/
@@ -1095,6 +1215,14 @@ void msg_start(void)
msg_didout = FALSE; /* no output on current line yet */
}
+ if (ui_has(kUIMessages)) {
+ msg_ext_ui_flush();
+ if (!msg_scroll && msg_ext_visible) {
+ // Will overwrite last message.
+ msg_ext_overwrite = true;
+ }
+ }
+
// When redirecting, may need to start a new line.
if (!did_return) {
redir_write("\n", 1);
@@ -1205,7 +1333,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
/* if MSG_HIST flag set, add message to history */
if (attr & MSG_HIST) {
- add_msg_hist(str, len, attr);
+ add_msg_hist(str, len, attr, false);
attr &= ~MSG_HIST;
}
@@ -1452,31 +1580,36 @@ void msg_prt_line(char_u *s, int list)
int col = 0;
int n_extra = 0;
int c_extra = 0;
- char_u *p_extra = NULL; /* init to make SASC shut up */
+ int c_final = 0;
+ char_u *p_extra = NULL; // init to make SASC shut up
int n;
int attr = 0;
- char_u *trail = NULL;
+ char_u *trail = NULL;
int l;
- if (curwin->w_p_list)
- list = TRUE;
+ if (curwin->w_p_list) {
+ list = true;
+ }
- /* find start of trailing whitespace */
- if (list && lcs_trail) {
+ // find start of trailing whitespace
+ if (list && curwin->w_p_lcs_chars.trail) {
trail = s + STRLEN(s);
- while (trail > s && ascii_iswhite(trail[-1]))
- --trail;
+ while (trail > s && ascii_iswhite(trail[-1])) {
+ trail--;
+ }
}
- /* output a space for an empty line, otherwise the line will be
- * overwritten */
- if (*s == NUL && !(list && lcs_eol != NUL))
+ // output a space for an empty line, otherwise the line will be overwritten
+ if (*s == NUL && !(list && curwin->w_p_lcs_chars.eol != NUL)) {
msg_putchar(' ');
+ }
while (!got_int) {
if (n_extra > 0) {
n_extra--;
- if (c_extra) {
+ if (n_extra == 0 && c_final) {
+ c = c_final;
+ } else if (c_extra) {
c = c_extra;
} else {
assert(p_extra != NULL);
@@ -1485,9 +1618,9 @@ void msg_prt_line(char_u *s, int list)
} else if ((l = utfc_ptr2len(s)) > 1) {
col += utf_ptr2cells(s);
char buf[MB_MAXBYTES + 1];
- if (lcs_nbsp != NUL && list
+ if (curwin->w_p_lcs_chars.nbsp != NUL && list
&& (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) {
- utf_char2bytes(lcs_nbsp, (char_u *)buf);
+ utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf);
buf[utfc_ptr2len((char_u *)buf)] = NUL;
} else {
memmove(buf, s, (size_t)l);
@@ -1499,40 +1632,46 @@ void msg_prt_line(char_u *s, int list)
} else {
attr = 0;
c = *s++;
- if (c == TAB && (!list || lcs_tab1)) {
- /* tab amount depends on current column */
+ if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
+ // tab amount depends on current column
n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
if (!list) {
c = ' ';
c_extra = ' ';
+ c_final = NUL;
} else {
- c = lcs_tab1;
- c_extra = lcs_tab2;
+ c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3)
+ ? curwin->w_p_lcs_chars.tab3
+ : curwin->w_p_lcs_chars.tab1;
+ c_extra = curwin->w_p_lcs_chars.tab2;
+ c_final = curwin->w_p_lcs_chars.tab3;
attr = HL_ATTR(HLF_8);
}
- } else if (c == 160 && list && lcs_nbsp != NUL) {
- c = lcs_nbsp;
+ } else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) {
+ c = curwin->w_p_lcs_chars.nbsp;
attr = HL_ATTR(HLF_8);
- } else if (c == NUL && list && lcs_eol != NUL) {
+ } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
p_extra = (char_u *)"";
c_extra = NUL;
+ c_final = NUL;
n_extra = 1;
- c = lcs_eol;
+ c = curwin->w_p_lcs_chars.eol;
attr = HL_ATTR(HLF_AT);
s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1;
p_extra = transchar_byte(c);
c_extra = NUL;
+ c_final = NUL;
c = *p_extra++;
/* Use special coloring to be able to distinguish <hex> from
* the same in plain text. */
attr = HL_ATTR(HLF_8);
} else if (c == ' ' && trail != NULL && s > trail) {
- c = lcs_trail;
+ c = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_8);
- } else if (c == ' ' && list && lcs_space != NUL) {
- c = lcs_space;
+ } else if (c == ' ' && list && curwin->w_p_lcs_chars.space != NUL) {
+ c = curwin->w_p_lcs_chars.space;
attr = HL_ATTR(HLF_8);
}
}
@@ -1643,7 +1782,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
// if MSG_HIST flag set, add message to history
if (attr & MSG_HIST) {
- add_msg_hist(str, (int)len, attr);
+ add_msg_hist(str, (int)len, attr, false);
attr &= ~MSG_HIST;
}
@@ -1651,7 +1790,18 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
// wait-return prompt later. Needed when scrolling, resetting
// need_wait_return after some prompt, and then outputting something
// without scrolling
- if (msg_scrolled != 0 && !msg_scrolled_ign) {
+ bool overflow = false;
+ if (ui_has(kUIMessages)) {
+ int count = msg_ext_visible + (msg_ext_overwrite ? 0 : 1);
+ // TODO(bfredl): possible extension point, let external UI control this
+ if (count > 1) {
+ overflow = true;
+ }
+ } else {
+ overflow = msg_scrolled != 0;
+ }
+
+ if (overflow && !msg_scrolled_ign) {
need_wait_return = true;
}
msg_didany = true; // remember that something was outputted
@@ -1689,6 +1839,20 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
}
+static void msg_ext_emit_chunk(void)
+{
+ // Color was changed or a message flushed, end current chunk.
+ if (msg_ext_last_attr == -1) {
+ return; // no chunk
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, INTEGER_OBJ(msg_ext_last_attr));
+ msg_ext_last_attr = -1;
+ String text = ga_take_string(&msg_ext_last_chunk);
+ ADD(chunk, STRING_OBJ(text));
+ ADD(msg_ext_chunks, ARRAY_OBJ(chunk));
+}
+
/*
* The display part of msg_puts_attr_len().
* May be called recursively to display scroll-back text.
@@ -1707,6 +1871,18 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
int did_last_char;
did_wait_return = false;
+
+ if (ui_has(kUIMessages)) {
+ if (attr != msg_ext_last_attr) {
+ msg_ext_emit_chunk();
+ msg_ext_last_attr = attr;
+ }
+ // Concat pieces with the same highlight
+ ga_concat_len(&msg_ext_last_chunk, (char *)str,
+ strnlen((char *)str, maxlen));
+ return;
+ }
+
while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
// We are at the end of the screen line when:
// - When outputting a newline.
@@ -1884,13 +2060,15 @@ int msg_scrollsize(void)
*/
static void msg_scroll_up(void)
{
- if (msg_scrolled == 0) {
+ if (!msg_did_scroll) {
ui_call_win_scroll_over_start();
+ msg_did_scroll = true;
}
if (dy_flags & DY_MSGSEP) {
if (msg_scrolled == 0) {
grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns,
- fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP));
+ curwin->w_p_fcs_chars.msgsep, curwin->w_p_fcs_chars.msgsep,
+ HL_ATTR(HLF_MSGSEP));
}
int nscroll = MIN(msg_scrollsize()+1, Rows);
grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns);
@@ -2314,9 +2492,8 @@ static int do_more_prompt(int typed_char)
mp_last = msg_sb_start(mp_last->sb_prev);
}
- if (toscroll == -1
- && grid_ins_lines(&default_grid, 0, 1, (int)Rows,
- 0, (int)Columns) == OK) {
+ if (toscroll == -1) {
+ grid_ins_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns);
grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0);
// display line at top
(void)disp_sb_line(0, mp);
@@ -2530,6 +2707,9 @@ void msg_clr_eos(void)
*/
void msg_clr_eos_force(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
@@ -2566,8 +2746,66 @@ int msg_end(void)
wait_return(FALSE);
return FALSE;
}
- ui_flush();
- return TRUE;
+
+ // @TODO(bfredl): calling flush here inhibits substantial performance
+ // improvements. Caller should call ui_flush before waiting on user input or
+ // CPU busywork.
+ ui_flush(); // calls msg_ext_ui_flush
+ return true;
+}
+
+void msg_ext_ui_flush(void)
+{
+ if (!ui_has(kUIMessages)) {
+ return;
+ }
+
+ msg_ext_emit_chunk();
+ if (msg_ext_chunks.size > 0) {
+ ui_call_msg_show(cstr_to_string(msg_ext_kind),
+ msg_ext_chunks, msg_ext_overwrite);
+ if (!msg_ext_overwrite) {
+ msg_ext_visible++;
+ }
+ msg_ext_kind = NULL;
+ msg_ext_chunks = (Array)ARRAY_DICT_INIT;
+ msg_ext_overwrite = false;
+ }
+}
+
+void msg_ext_flush_showmode(void)
+{
+ // Showmode messages doesn't interrupt normal message flow, so we use
+ // separate event. Still reuse the same chunking logic, for simplicity.
+ msg_ext_emit_chunk();
+ ui_call_msg_showmode(msg_ext_chunks);
+ msg_ext_chunks = (Array)ARRAY_DICT_INIT;
+}
+
+void msg_ext_clear(bool force)
+{
+ if (msg_ext_visible && (!msg_ext_keep_after_cmdline || force)) {
+ ui_call_msg_clear();
+ msg_ext_visible = 0;
+ msg_ext_overwrite = false; // nothing to overwrite
+ }
+
+ // Only keep once.
+ msg_ext_keep_after_cmdline = false;
+}
+
+void msg_ext_check_prompt(void)
+{
+ // Redraw after cmdline is expected to clear messages.
+ if (msg_ext_did_cmdline) {
+ msg_ext_clear(true);
+ msg_ext_did_cmdline = false;
+ }
+}
+
+bool msg_ext_is_visible(void)
+{
+ return ui_has(kUIMessages) && msg_ext_visible > 0;
}
/*
@@ -2576,6 +2814,9 @@ int msg_end(void)
*/
void msg_check(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
if (msg_row == Rows - 1 && msg_col >= sc_col) {
need_wait_return = TRUE;
redraw_cmdline = TRUE;
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 82935a36a9..7938fd91d3 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -5,6 +5,7 @@
#include <stdarg.h>
#include <stddef.h>
+#include "nvim/macros.h"
#include "nvim/types.h"
/*
@@ -77,7 +78,9 @@
typedef struct msg_hist {
struct msg_hist *next; ///< Next message.
char_u *msg; ///< Message text.
+ const char *kind; ///< Message kind (for msg_ext)
int attr; ///< Message highlighting.
+ bool multiline; ///< Multiline message.
} MessageHistoryEntry;
/// First message
@@ -85,6 +88,8 @@ extern MessageHistoryEntry *first_msg_hist;
/// Last message
extern MessageHistoryEntry *last_msg_hist;
+EXTERN bool msg_ext_did_cmdline INIT(= false);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index a66ded13f1..e752910a4b 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -1251,7 +1251,7 @@ int plines_win_nofill(
return 1;
}
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
return 1;
}
@@ -1261,8 +1261,8 @@ int plines_win_nofill(
}
const int lines = plines_win_nofold(wp, lnum);
- if (winheight && lines > wp->w_grid.Rows) {
- return wp->w_grid.Rows;
+ if (winheight && lines > wp->w_height_inner) {
+ return wp->w_height_inner;
}
return lines;
}
@@ -1282,17 +1282,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
return 1;
col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
- /*
- * If list mode is on, then the '$' at the end of the line may take up one
- * extra column.
- */
- if (wp->w_p_list && lcs_eol != NUL)
+ // If list mode is on, then the '$' at the end of the line may take up one
+ // extra column.
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) {
col += 1;
+ }
/*
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
*/
- width = wp->w_grid.Columns - win_col_off(wp);
+ width = wp->w_width_inner - win_col_off(wp);
if (width <= 0 || col > 32000) {
return 32000; // bigger than the number of screen columns
}
@@ -1318,7 +1317,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
if (!wp->w_p_wrap)
return lines + 1;
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
return lines + 1;
}
@@ -1336,12 +1335,13 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
// screen position of the TAB. This only fixes an error when the TAB wraps
// from one screen line to the next (when 'columns' is not a multiple of
// 'ts') -- webb.
- if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) {
+ if (*s == TAB && (State & NORMAL)
+ && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
}
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
- int width = wp->w_grid.Columns - win_col_off(wp);
+ int width = wp->w_width_inner - win_col_off(wp);
if (width <= 0) {
return 9999;
}
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 887cbde921..50dba92eca 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -70,6 +70,7 @@ int jump_to_mouse(int flags,
bool first;
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
int mouse_char;
mouse_past_bottom = false;
@@ -125,20 +126,22 @@ retnomove:
return IN_UNKNOWN;
// find the window where the row is in
- wp = mouse_find_win(&row, &col);
+ wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
dragwin = NULL;
// winpos and height may change in win_enter()!
- if (row >= wp->w_height) { // In (or below) status line
+ if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) {
+ // In (or below) status line
on_status_line = row - wp->w_height + 1;
dragwin = wp;
} else {
on_status_line = 0;
}
- if (col >= wp->w_width) { // In separator line
+ if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) {
+ // In separator line
on_sep_line = col - wp->w_width + 1;
dragwin = wp;
} else {
@@ -160,12 +163,10 @@ retnomove:
&& (wp->w_buffer != curwin->w_buffer
|| (!on_status_line
&& !on_sep_line
- && (
- wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
- col >= wp->w_p_fdc
- + (cmdwin_type == 0 && wp ==
- curwin ? 0 : 1)
- )
+ && (wp->w_p_rl
+ ? col < wp->w_width_inner - wp->w_p_fdc
+ : col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin
+ ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode();
redraw_curbuf_later(INVERTED); // delete the inversion
@@ -257,7 +258,7 @@ retnomove:
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
redraw_later(VALID);
row = 0;
- } else if (row >= curwin->w_height) {
+ } else if (row >= curwin->w_height_inner) {
count = 0;
for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count; ) {
if (curwin->w_topfill > 0) {
@@ -266,7 +267,7 @@ retnomove:
count += plines(curwin->w_topline);
}
- if (!first && count > row - curwin->w_height + 1) {
+ if (!first && count > row - curwin->w_height_inner + 1) {
break;
}
first = false;
@@ -288,7 +289,7 @@ retnomove:
redraw_later(VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- row = curwin->w_height - 1;
+ row = curwin->w_height_inner - 1;
} else if (row == 0) {
// When dragging the mouse, while the text has been scrolled up as
// far as it goes, moving the mouse in the top line should scroll
@@ -303,7 +304,7 @@ retnomove:
}
// Check for position outside of the fold column.
- if (curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
+ if (curwin->w_p_rl ? col < curwin->w_width_inner - curwin->w_p_fdc :
col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)) {
mouse_char = ' ';
}
@@ -369,8 +370,9 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
int off;
int count;
- if (win->w_p_rl)
- col = win->w_width - 1 - col;
+ if (win->w_p_rl) {
+ col = win->w_width_inner - 1 - col;
+ }
lnum = win->w_topline;
@@ -407,7 +409,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
off = win_col_off(win) - win_col_off2(win);
if (col < off)
col = off;
- col += row * (win->w_width - off);
+ col += row * (win->w_width_inner - off);
// add skip column (for long wrapping line)
col += win->w_skipcol;
}
@@ -428,11 +430,23 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
return retval;
}
-// Find the window at screen position "*rowp" and "*colp". The positions are
-// updated to become relative to the top-left of the window.
-// Returns NULL when something is wrong.
-win_T *mouse_find_win(int *rowp, int *colp)
+/// Find the window at "grid" position "*rowp" and "*colp". The positions are
+/// updated to become relative to the top-left of the window.
+///
+/// @return NULL when something is wrong.
+win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
{
+ win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp);
+ if (wp_grid) {
+ return wp_grid;
+ }
+
+ // TODO(bfredl): grid zero will have floats displayed on it, and will
+ // be adjusted to float grids.
+ if (*gridp == 0) {
+ *gridp = DEFAULT_GRID_HANDLE;
+ }
+
frame_T *fp;
fp = topframe;
@@ -464,6 +478,19 @@ win_T *mouse_find_win(int *rowp, int *colp)
return NULL;
}
+static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp)
+{
+ if (*grid > 1) {
+ win_T *wp = get_win_by_grid_handle(*grid);
+ if (wp && wp->w_grid.chars) {
+ *rowp = MIN(*rowp, wp->w_grid.Rows-1);
+ *colp = MIN(*colp, wp->w_grid.Columns-1);
+ return wp;
+ }
+ }
+ return NULL;
+}
+
/*
* setmouse() - switch mouse on/off depending on current mode and 'mouse'
*/
@@ -595,7 +622,7 @@ bool mouse_scroll_horiz(int dir)
int step = 6;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- step = curwin->w_width;
+ step = curwin->w_width_inner;
}
int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
@@ -647,7 +674,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// Find the offset where scanning should begin.
int offset = wp->w_leftcol;
if (row > 0) {
- offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) -
+ offset += row * (wp->w_width_inner - win_col_off(wp) - win_col_off2(wp) -
wp->w_leftcol + wp->w_skipcol);
}
@@ -704,7 +731,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
} else {
if (!(row > 0 && ptr == ptr_row_offset)
&& (wp->w_p_cole == 1 || (wp->w_p_cole == 2
- && (lcs_conceal != NUL
+ && (wp->w_p_lcs_chars.conceal != NUL
|| syn_get_sub_char() != NUL)))) {
// At least one placeholder character will be displayed.
decr();
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 07b355e603..7aa7f922c1 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -83,7 +83,7 @@ static void comp_botline(win_T *wp)
redraw_for_cursorline(wp);
wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
}
- if (done + n > wp->w_grid.Rows) {
+ if (done + n > wp->w_height_inner) {
break;
}
done += n;
@@ -150,12 +150,9 @@ void update_topline(void)
bool check_botline = false;
long save_so = p_so;
- // need to have w_grid.Rows/Columns updated
- win_grid_alloc(curwin);
-
// If there is no valid screen and when the window height is zero just use
// the cursor line.
- if (!screen_valid(true) || curwin->w_grid.Rows == 0) {
+ if (!default_grid.chars || curwin->w_height_inner == 0) {
curwin->w_topline = curwin->w_cursor.lnum;
curwin->w_botline = curwin->w_topline;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
@@ -204,7 +201,7 @@ void update_topline(void)
check_topline = true;
if (check_topline) {
- int halfheight = curwin->w_grid.Rows / 2 - 1;
+ int halfheight = curwin->w_height_inner / 2 - 1;
if (halfheight < 2) {
halfheight = 2;
}
@@ -297,7 +294,7 @@ void update_topline(void)
lnum >= curwin->w_botline - p_so; lnum--) {
line_count++;
// stop at end of file or when we know we are far off
- if (lnum <= 0 || line_count > curwin->w_grid.Rows + 1) {
+ if (lnum <= 0 || line_count > curwin->w_height_inner + 1) {
break;
}
(void)hasFolding(lnum, &lnum, NULL);
@@ -305,7 +302,7 @@ void update_topline(void)
} else
line_count = curwin->w_cursor.lnum - curwin->w_botline
+ 1 + p_so;
- if (line_count <= curwin->w_grid.Rows + 1) {
+ if (line_count <= curwin->w_height_inner + 1) {
scroll_cursor_bot(scrolljump_value(), false);
} else {
scroll_cursor_halfway(false);
@@ -353,7 +350,7 @@ void update_topline_win(win_T* win)
*/
static int scrolljump_value(void)
{
- long result = p_sj >= 0 ? p_sj : (curwin->w_grid.Rows * -p_sj) / 100;
+ long result = p_sj >= 0 ? p_sj : (curwin->w_height_inner * -p_sj) / 100;
assert(result <= INT_MAX);
return (int)result;
}
@@ -529,7 +526,6 @@ int cursor_valid(void)
*/
void validate_cursor(void)
{
- win_grid_alloc(curwin); // we need to have w_grid.Rows/Columns updated
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(true);
@@ -668,13 +664,13 @@ void validate_cursor_col(void)
colnr_T col = curwin->w_virtcol;
colnr_T off = curwin_col_off();
col += off;
- int width = curwin->w_grid.Columns - off + curwin_col_off2();
+ int width = curwin->w_width_inner - off + curwin_col_off2();
// long line wrapping, adjust curwin->w_wrow
- if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_grid.Columns
+ if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner
&& width > 0) {
// use same formula as what is used in curs_columns()
- col -= ((col - curwin->w_grid.Columns) / width + 1) * width;
+ col -= ((col - curwin->w_width_inner) / width + 1) * width;
}
if (col > (int)curwin->w_leftcol) {
col -= curwin->w_leftcol;
@@ -769,20 +765,20 @@ void curs_columns(
*/
curwin->w_wrow = curwin->w_cline_row;
- int textwidth = curwin->w_grid.Columns - extra;
+ int textwidth = curwin->w_width_inner - extra;
if (textwidth <= 0) {
// No room for text, put cursor in last char of window.
- curwin->w_wcol = curwin->w_grid.Columns - 1;
- curwin->w_wrow = curwin->w_grid.Rows - 1;
+ curwin->w_wcol = curwin->w_width_inner - 1;
+ curwin->w_wrow = curwin->w_height_inner - 1;
} else if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
width = textwidth + curwin_col_off2();
// long line wrapping, adjust curwin->w_wrow
- if (curwin->w_wcol >= curwin->w_grid.Columns) {
+ if (curwin->w_wcol >= curwin->w_width_inner) {
// this same formula is used in validate_cursor_col()
- n = (curwin->w_wcol - curwin->w_grid.Columns) / width + 1;
+ n = (curwin->w_wcol - curwin->w_width_inner) / width + 1;
curwin->w_wcol -= n * width;
curwin->w_wrow += n;
@@ -809,7 +805,7 @@ void curs_columns(
assert(p_siso <= INT_MAX);
int off_left = startcol - curwin->w_leftcol - (int)p_siso;
int off_right =
- endcol - curwin->w_leftcol - curwin->w_grid.Columns + (int)p_siso + 1;
+ endcol - curwin->w_leftcol - curwin->w_width_inner + (int)p_siso + 1;
if (off_left < 0 || off_right > 0) {
int diff = (off_left < 0) ? -off_left: off_right;
@@ -852,16 +848,16 @@ void curs_columns(
prev_skipcol = curwin->w_skipcol;
int p_lines = 0;
- if ((curwin->w_wrow >= curwin->w_grid.Rows
+ if ((curwin->w_wrow >= curwin->w_height_inner
|| ((prev_skipcol > 0
- || curwin->w_wrow + p_so >= curwin->w_grid.Rows)
+ || curwin->w_wrow + p_so >= curwin->w_height_inner)
&& (p_lines =
plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1
- >= curwin->w_grid.Rows))
- && curwin->w_grid.Rows != 0
+ >= curwin->w_height_inner))
+ && curwin->w_height_inner != 0
&& curwin->w_cursor.lnum == curwin->w_topline
&& width > 0
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
/* Cursor past end of screen. Happens with a single line that does
* not fit on screen. Find a skipcol to show the text around the
@@ -883,21 +879,21 @@ void curs_columns(
}
else
n = p_lines;
- if ((colnr_T)n >= curwin->w_grid.Rows + curwin->w_skipcol / width) {
+ if ((colnr_T)n >= curwin->w_height_inner + curwin->w_skipcol / width) {
extra += 2;
}
if (extra == 3 || p_lines < p_so * 2) {
// not enough room for 'scrolloff', put cursor in the middle
n = curwin->w_virtcol / width;
- if (n > curwin->w_grid.Rows / 2) {
- n -= curwin->w_grid.Rows / 2;
+ if (n > curwin->w_height_inner / 2) {
+ n -= curwin->w_height_inner / 2;
} else {
n = 0;
}
// don't skip more than necessary
- if (n > p_lines - curwin->w_grid.Rows + 1) {
- n = p_lines - curwin->w_grid.Rows + 1;
+ if (n > p_lines - curwin->w_height_inner + 1) {
+ n = p_lines - curwin->w_height_inner + 1;
}
curwin->w_skipcol = n * width;
} else if (extra == 1) {
@@ -912,7 +908,7 @@ void curs_columns(
}
} else if (extra == 2) {
// less then 'scrolloff' lines below, increase skipcol
- endcol = (n - curwin->w_grid.Rows + 1) * width;
+ endcol = (n - curwin->w_height_inner + 1) * width;
while (endcol > curwin->w_virtcol) {
endcol -= width;
}
@@ -922,19 +918,16 @@ void curs_columns(
}
curwin->w_wrow -= curwin->w_skipcol / width;
- if (curwin->w_wrow >= curwin->w_grid.Rows) {
+ if (curwin->w_wrow >= curwin->w_height_inner) {
// small window, make sure cursor is in it
- extra = curwin->w_wrow - curwin->w_grid.Rows + 1;
+ extra = curwin->w_wrow - curwin->w_height_inner + 1;
curwin->w_skipcol += extra * width;
curwin->w_wrow -= extra;
}
+ // extra could be either positive or negative
extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
- if (extra > 0) {
- win_ins_lines(curwin, 0, extra);
- } else if (extra < 0) {
- win_del_lines(curwin, 0, -extra);
- }
+ win_scroll_lines(curwin, 0, extra);
} else {
curwin->w_skipcol = 0;
}
@@ -966,7 +959,7 @@ scrolldown (
validate_cursor(); /* w_wrow needs to be valid */
while (line_count-- > 0) {
if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
- && curwin->w_topfill < curwin->w_grid.Rows - 1) {
+ && curwin->w_topfill < curwin->w_height_inner - 1) {
curwin->w_topfill++;
done++;
} else {
@@ -1001,15 +994,15 @@ scrolldown (
*/
int wrow = curwin->w_wrow;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_virtcol();
validate_cheight();
wrow += curwin->w_cline_height - 1 -
- curwin->w_virtcol / curwin->w_grid.Columns;
+ curwin->w_virtcol / curwin->w_width_inner;
}
bool moved = false;
- while (wrow >= curwin->w_grid.Rows && curwin->w_cursor.lnum > 1) {
+ while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) {
linenr_T first;
if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) {
--wrow;
@@ -1094,12 +1087,12 @@ check_topfill (
{
if (wp->w_topfill > 0) {
int n = plines_win_nofill(wp, wp->w_topline, true);
- if (wp->w_topfill + n > wp->w_grid.Rows) {
+ if (wp->w_topfill + n > wp->w_height_inner) {
if (down && wp->w_topline > 1) {
--wp->w_topline;
wp->w_topfill = 0;
} else {
- wp->w_topfill = wp->w_grid.Rows - n;
+ wp->w_topfill = wp->w_height_inner - n;
if (wp->w_topfill < 0) {
wp->w_topfill = 0;
}
@@ -1115,12 +1108,12 @@ check_topfill (
static void max_topfill(void)
{
int n = plines_nofill(curwin->w_topline);
- if (n >= curwin->w_grid.Rows) {
+ if (n >= curwin->w_height_inner) {
curwin->w_topfill = 0;
} else {
curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
- if (curwin->w_topfill + n > curwin->w_grid.Rows) {
- curwin->w_topfill = curwin->w_grid.Rows - n;
+ if (curwin->w_topfill + n > curwin->w_height_inner) {
+ curwin->w_topfill = curwin->w_height_inner - n;
}
}
}
@@ -1152,14 +1145,14 @@ void scrolldown_clamp(void)
else
end_row += plines_nofill(curwin->w_topline - 1);
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_cheight();
validate_virtcol();
end_row += curwin->w_cline_height - 1 -
- curwin->w_virtcol / curwin->w_grid.Columns;
+ curwin->w_virtcol / curwin->w_width_inner;
}
- if (end_row < curwin->w_grid.Rows - p_so) {
+ if (end_row < curwin->w_height_inner - p_so) {
if (can_fill) {
++curwin->w_topfill;
check_topfill(curwin, true);
@@ -1194,10 +1187,10 @@ void scrollup_clamp(void)
int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
- curwin->w_topfill;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_virtcol();
- start_row -= curwin->w_virtcol / curwin->w_grid.Columns;
+ start_row -= curwin->w_virtcol / curwin->w_width_inner;
}
if (start_row >= p_so) {
if (curwin->w_topfill > 0)
@@ -1351,7 +1344,7 @@ void scroll_cursor_top(int min_scroll, int always)
else
used += plines(bot);
}
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (top < curwin->w_topline) {
@@ -1376,7 +1369,7 @@ void scroll_cursor_top(int min_scroll, int always)
* This makes sure we get the same position when using "k" and "j"
* in a small window.
*/
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
scroll_cursor_halfway(false);
} else {
/*
@@ -1413,7 +1406,7 @@ void set_empty_rows(win_T *wp, int used)
if (used == 0) {
wp->w_empty_rows = 0; // single line that doesn't fit
} else {
- wp->w_empty_rows = wp->w_grid.Rows - used;
+ wp->w_empty_rows = wp->w_height_inner - used;
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
if (wp->w_empty_rows > wp->w_filler_rows)
@@ -1456,7 +1449,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_topline = loff.lnum) {
loff.lnum = curwin->w_topline;
topline_back(&loff);
- if (loff.height == MAXCOL || used + loff.height > curwin->w_grid.Rows) {
+ if (loff.height == MAXCOL
+ || used + loff.height > curwin->w_height_inner) {
break;
}
used += loff.height;
@@ -1520,7 +1514,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
} else {
used += loff.height;
}
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (loff.lnum >= curwin->w_botline
@@ -1539,7 +1533,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Add one line below */
botline_forw(&boff);
used += boff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (extra < (
@@ -1566,7 +1560,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
if (scrolled <= 0) {
line_count = 0;
// more than a screenfull, don't scroll but redraw
- } else if (used > curwin->w_grid.Rows) {
+ } else if (used > curwin->w_height_inner) {
line_count = used;
// scroll minimal number of lines
} else {
@@ -1587,7 +1581,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
* 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_grid.Rows && line_count > min_scroll) {
+ if (line_count >= curwin->w_height_inner && line_count > min_scroll) {
scroll_cursor_halfway(false);
} else {
scrollup(line_count, true);
@@ -1630,7 +1624,7 @@ void scroll_cursor_halfway(int atend)
if (boff.lnum < curbuf->b_ml.ml_line_count) {
botline_forw(&boff);
used += boff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
below += boff.height;
@@ -1647,7 +1641,7 @@ void scroll_cursor_halfway(int atend)
used = MAXCOL;
else
used += loff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
above += loff.height;
@@ -1658,7 +1652,7 @@ void scroll_cursor_halfway(int atend)
if (!hasFolding(topline, &curwin->w_topline, NULL))
curwin->w_topline = topline;
curwin->w_topfill = topfill;
- if (old_topline > curwin->w_topline + curwin->w_grid.Rows) {
+ if (old_topline > curwin->w_topline + curwin->w_height_inner) {
curwin->w_botfill = false;
}
check_topfill(curwin, false);
@@ -1687,7 +1681,7 @@ void cursor_correct(void)
}
if (curwin->w_topline == 1) {
above_wanted = 0;
- int max_off = curwin->w_grid.Rows / 2;
+ int max_off = curwin->w_height_inner / 2;
if (below_wanted > max_off) {
below_wanted = max_off;
}
@@ -1697,7 +1691,7 @@ void cursor_correct(void)
&& mouse_dragging == 0
) {
below_wanted = 0;
- int max_off = (curwin->w_grid.Rows - 1) / 2;
+ int max_off = (curwin->w_height_inner - 1) / 2;
if (above_wanted > max_off) {
above_wanted = max_off;
}
@@ -1871,7 +1865,7 @@ int onepage(Direction dir, long count)
/* Find the line just above the new topline to get the right line
* at the bottom of the window. */
n = 0;
- while (n <= curwin->w_grid.Rows && loff.lnum >= 1) {
+ while (n <= curwin->w_height_inner && loff.lnum >= 1) {
topline_back(&loff);
if (loff.height == MAXCOL)
n = MAXCOL;
@@ -1962,7 +1956,7 @@ int onepage(Direction dir, long count)
*/
static void get_scroll_overlap(lineoff_T *lp, int dir)
{
- int min_height = curwin->w_grid.Rows - 2;
+ int min_height = curwin->w_height_inner - 2;
if (lp->fill > 0)
lp->height = 1;
@@ -2017,12 +2011,12 @@ void halfpage(bool flag, linenr_T Prenum)
int i;
if (Prenum) {
- curwin->w_p_scr = (Prenum > curwin->w_grid.Rows) ? curwin->w_grid.Rows
+ curwin->w_p_scr = (Prenum > curwin->w_height_inner) ? curwin->w_height_inner
: Prenum;
}
assert(curwin->w_p_scr <= INT_MAX);
- int n = curwin->w_p_scr <= curwin->w_grid.Rows ? (int)curwin->w_p_scr
- : curwin->w_grid.Rows;
+ int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr
+ : curwin->w_height_inner;
update_topline();
validate_botline();
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0e3946740a..d361d81ac7 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -61,6 +61,7 @@
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
typedef struct normal_state {
VimState state;
@@ -1258,8 +1259,9 @@ static void normal_redraw(NormalState *s)
maketitle();
}
- // display message after redraw
- if (keep_msg != NULL) {
+ // Display message after redraw. If an external message is still visible,
+ // it contains the kept message already.
+ if (keep_msg != NULL && !msg_ext_is_visible()) {
// msg_attr_keep() will set keep_msg to NULL, must free the string here.
// Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
char *p = (char *)keep_msg;
@@ -3284,11 +3286,11 @@ void clear_showcmd(void)
p_sbr = empty_option;
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
p_sbr = saved_sbr;
- sprintf((char *)showcmd_buf, "%" PRId64 "x%" PRId64,
- (int64_t)lines, (int64_t)(rightcol - leftcol + 1));
- } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
- sprintf((char *)showcmd_buf, "%" PRId64, (int64_t)lines);
- else {
+ snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
+ (int64_t)lines, (int64_t)rightcol - leftcol + 1);
+ } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) {
+ snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
+ } else {
char_u *s, *e;
int l;
int bytes = 0;
@@ -3317,7 +3319,8 @@ void clear_showcmd(void)
else
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
}
- showcmd_buf[SHOWCMD_COLS] = NUL; /* truncate */
+ int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ showcmd_buf[limit] = NUL; // truncate
showcmd_visual = true;
} else {
showcmd_buf[0] = NUL;
@@ -3370,8 +3373,9 @@ bool add_to_showcmd(int c)
STRCPY(p, "<20>");
size_t old_len = STRLEN(showcmd_buf);
size_t extra_len = STRLEN(p);
- if (old_len + extra_len > SHOWCMD_COLS) {
- size_t overflow = old_len + extra_len - SHOWCMD_COLS;
+ size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ if (old_len + extra_len > limit) {
+ size_t overflow = old_len + extra_len - limit;
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
}
STRCAT(showcmd_buf, p);
@@ -3432,13 +3436,24 @@ void pop_showcmd(void)
static void display_showcmd(void)
{
int len;
-
len = (int)STRLEN(showcmd_buf);
- if (len == 0) {
- showcmd_is_clear = true;
- } else {
+ showcmd_is_clear = (len == 0);
+
+ if (ui_has(kUIMessages)) {
+ Array content = ARRAY_DICT_INIT;
+ if (len > 0) {
+ Array chunk = ARRAY_DICT_INIT;
+ // placeholder for future highlight support
+ ADD(chunk, INTEGER_OBJ(0));
+ ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
+ ADD(content, ARRAY_OBJ(chunk));
+ }
+ ui_call_msg_showcmd(content);
+ return;
+ }
+
+ if (!showcmd_is_clear) {
grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0);
- showcmd_is_clear = false;
}
/*
@@ -3875,14 +3890,14 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
col_off1 = curwin_col_off();
col_off2 = col_off1 - curwin_col_off2();
- width1 = curwin->w_grid.Columns - col_off1;
- width2 = curwin->w_grid.Columns - col_off2;
+ width1 = curwin->w_width_inner - col_off1;
+ width2 = curwin->w_width_inner - col_off2;
if (width2 == 0) {
width2 = 1; // Avoid divide by zero.
}
- if (curwin->w_grid.Columns != 0) {
+ if (curwin->w_width_inner != 0) {
// Instead of sticking at the last character of the buffer line we
// try to stick in the last column of the screen.
if (curwin->w_curswant == MAXCOL) {
@@ -3997,13 +4012,14 @@ static void nv_mousescroll(cmdarg_T *cap)
win_T *old_curwin = curwin;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row, col;
+ int grid, row, col;
+ grid = mouse_grid;
row = mouse_row;
col = mouse_col;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
@@ -4225,7 +4241,7 @@ dozet:
/* "zH" - scroll screen right half-page */
case 'H':
- cap->count1 *= curwin->w_grid.Columns / 2;
+ cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH;
/* "zh" - scroll screen to the right */
@@ -4241,7 +4257,7 @@ dozet:
break;
// "zL" - scroll screen left half-page
- case 'L': cap->count1 *= curwin->w_grid.Columns / 2;
+ case 'L': cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH;
/* "zl" - scroll screen to the left */
@@ -4277,7 +4293,7 @@ dozet:
col = 0; /* like the cursor is in col 0 */
else
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
- n = curwin->w_grid.Columns - curwin_col_off();
+ n = curwin->w_width_inner - curwin_col_off();
if (col + l_p_siso < n) {
col = 0;
} else {
@@ -4930,10 +4946,10 @@ get_visual_text (
} else {
if (lt(curwin->w_cursor, VIsual)) {
*pp = ml_get_pos(&curwin->w_cursor);
- *lenp = (size_t)(VIsual.col - curwin->w_cursor.col + 1);
+ *lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
} else {
*pp = ml_get_pos(&VIsual);
- *lenp = (size_t)(curwin->w_cursor.col - VIsual.col + 1);
+ *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
if (has_mbyte)
/* Correct the length to include the whole last character. */
@@ -4988,7 +5004,7 @@ static void nv_scroll(cmdarg_T *cap)
used -= diff_check_fill(curwin, curwin->w_topline)
- curwin->w_topfill;
validate_botline(); // make sure w_empty_rows is valid
- half = (curwin->w_grid.Rows - curwin->w_empty_rows + 1) / 2;
+ half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
// Count half he number of filler lines to be "below this
// line" and half to be "above the next line".
@@ -5003,7 +5019,7 @@ static void nv_scroll(cmdarg_T *cap)
if (hasFolding(curwin->w_topline + n, NULL, &lnum))
n = lnum - curwin->w_topline;
}
- if (n > 0 && used > curwin->w_grid.Rows) {
+ if (n > 0 && used > curwin->w_height_inner) {
n--;
}
} else { // (cap->cmdchar == 'H')
@@ -6715,9 +6731,9 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise;
oap->inclusive = false;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
- int width1 = curwin->w_grid.Columns - curwin_col_off();
+ int width1 = curwin->w_width_inner - curwin_col_off();
int width2 = width1 + curwin_col_off2();
validate_virtcol();
@@ -6730,7 +6746,7 @@ static void nv_g_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_grid.Columns - curwin_col_off()
+ i += (curwin->w_width_inner - curwin_col_off()
+ ((curwin->w_p_wrap && i > 0)
? curwin_col_off2() : 0)) / 2;
}
@@ -6777,11 +6793,11 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise;
oap->inclusive = true;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cap->count1 == 1) {
- int width1 = curwin->w_grid.Columns - col_off;
+ int width1 = curwin->w_width_inner - col_off;
int width2 = width1 + curwin_col_off2();
validate_virtcol();
@@ -6807,7 +6823,7 @@ static void nv_g_cmd(cmdarg_T *cap)
} else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false)
clearopbeep(oap);
} else {
- i = curwin->w_leftcol + curwin->w_grid.Columns - col_off - 1;
+ i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
coladvance((colnr_T)i);
/* Make sure we stick in this column. */
@@ -7917,7 +7933,7 @@ static void get_op_vcol(
colnr_T end;
if (VIsual_mode != Ctrl_V
- || (!initial && oap->end.col < curwin->w_grid.Columns)) {
+ || (!initial && oap->end.col < curwin->w_width_inner)) {
return;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index e9cb480647..674a9244f0 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -865,8 +865,12 @@ int do_record(int c)
* needs to be removed again to put it in a register. exec_reg then
* adds the escaping back later.
*/
- Recording = FALSE;
- MSG("");
+ Recording = false;
+ if (ui_has(kUIMessages)) {
+ showmode();
+ } else {
+ MSG("");
+ }
p = get_recorded();
if (p == NULL)
retval = FAIL;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index b8f5957c09..b85ec6dbcc 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -50,6 +50,7 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/hardcopy.h"
+#include "nvim/highlight.h"
#include "nvim/indent_c.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
@@ -65,6 +66,7 @@
#include "nvim/normal.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
@@ -2121,10 +2123,10 @@ static void didset_options2(void)
(void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
// Parse default for 'fillchars'.
- (void)set_chars_option(&p_fcs);
+ (void)set_chars_option(curwin, &curwin->w_p_fcs);
// Parse default for 'listchars'.
- (void)set_chars_option(&p_lcs);
+ (void)set_chars_option(curwin, &curwin->w_p_lcs);
// Parse default for 'wildmode'.
check_opt_wim();
@@ -2567,10 +2569,19 @@ did_set_string_option (
// 'ambiwidth'
if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
errmsg = e_invarg;
- } else if (set_chars_option(&p_lcs) != NULL) {
- errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'");
- } else if (set_chars_option(&p_fcs) != NULL) {
- errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'");
+ } else {
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (set_chars_option(wp, &wp->w_p_lcs) != NULL) {
+ errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'");
+ goto ambw_end;
+ }
+ if (set_chars_option(wp, &wp->w_p_fcs) != NULL) {
+ errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'");
+ goto ambw_end;
+ }
+ }
+ambw_end:
+ {} // clint prefers {} over ; as an empty statement
}
}
/* 'background' */
@@ -2756,14 +2767,10 @@ did_set_string_option (
}
s = skip_to_option_part(s);
}
- }
- /* 'listchars' */
- else if (varp == &p_lcs) {
- errmsg = set_chars_option(varp);
- }
- /* 'fillchars' */
- else if (varp == &p_fcs) {
- errmsg = set_chars_option(varp);
+ } else if (varp == &curwin->w_p_lcs) { // 'listchars'
+ errmsg = set_chars_option(curwin, varp);
+ } else if (varp == &curwin->w_p_fcs) { // 'fillchars'
+ errmsg = set_chars_option(curwin, varp);
}
/* 'cedit' */
else if (varp == &p_cedit) {
@@ -3394,53 +3401,55 @@ skip:
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
-/// @param varp either &p_lcs ('listchars') or &p_fcs ('fillchar')
+/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
/// @return error message, NULL if it's OK.
-static char_u *set_chars_option(char_u **varp)
+static char_u *set_chars_option(win_T *wp, char_u **varp)
{
int round, i, len, entries;
- char_u *p, *s;
- int c1, c2 = 0;
- struct charstab {
+ char_u *p, *s;
+ int c1 = 0, c2 = 0, c3 = 0;
+
+ struct chars_tab {
int *cp; ///< char value
char *name; ///< char id
int def; ///< default value
};
- static struct charstab filltab[] = {
- { &fill_stl, "stl" , ' ' },
- { &fill_stlnc, "stlnc", ' ' },
- { &fill_vert, "vert" , 9474 }, // │
- { &fill_fold, "fold" , 183 }, // ·
- { &fill_diff, "diff" , '-' },
- { &fill_msgsep, "msgsep", ' ' },
- { &fill_eob, "eob", '~' },
+ struct chars_tab *tab;
+
+ struct chars_tab fcs_tab[] = {
+ { &wp->w_p_fcs_chars.stl, "stl", ' ' },
+ { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
+ { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
+ { &wp->w_p_fcs_chars.diff, "diff", '-' },
+ { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
+ { &wp->w_p_fcs_chars.eob, "eob", '~' },
};
- static struct charstab lcstab[] = {
- { &lcs_eol, "eol", NUL },
- { &lcs_ext, "extends", NUL },
- { &lcs_nbsp, "nbsp", NUL },
- { &lcs_prec, "precedes", NUL },
- { &lcs_space, "space", NUL },
- { &lcs_tab2, "tab", NUL },
- { &lcs_trail, "trail", NUL },
- { &lcs_conceal, "conceal", NUL },
+ struct chars_tab lcs_tab[] = {
+ { &wp->w_p_lcs_chars.eol, "eol", NUL },
+ { &wp->w_p_lcs_chars.ext, "extends", NUL },
+ { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
+ { &wp->w_p_lcs_chars.prec, "precedes", NUL },
+ { &wp->w_p_lcs_chars.space, "space", NUL },
+ { &wp->w_p_lcs_chars.tab2, "tab", NUL },
+ { &wp->w_p_lcs_chars.trail, "trail", NUL },
+ { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
};
- struct charstab *tab;
- if (varp == &p_lcs) {
- tab = lcstab;
- entries = ARRAY_SIZE(lcstab);
+ if (varp == &wp->w_p_lcs) {
+ tab = lcs_tab;
+ entries = ARRAY_SIZE(lcs_tab);
} else {
- tab = filltab;
- entries = ARRAY_SIZE(filltab);
+ tab = fcs_tab;
+ entries = ARRAY_SIZE(fcs_tab);
if (*p_ambw == 'd') {
// XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
// forbidden (TUI limitation?). Set old defaults.
- filltab[2].def = '|';
- filltab[3].def = '-';
+ fcs_tab[2].def = '|';
+ fcs_tab[3].def = '-';
} else {
- filltab[2].def = 9474; // │
- filltab[3].def = 183; // ·
+ fcs_tab[2].def = 9474; // │
+ fcs_tab[3].def = 183; // ·
}
}
@@ -3453,8 +3462,9 @@ static char_u *set_chars_option(char_u **varp)
*(tab[i].cp) = tab[i].def;
}
}
- if (varp == &p_lcs) {
- lcs_tab1 = NUL;
+ if (varp == &wp->w_p_lcs) {
+ wp->w_p_lcs_chars.tab1 = NUL;
+ wp->w_p_lcs_chars.tab3 = NUL;
}
}
p = *varp;
@@ -3464,6 +3474,7 @@ static char_u *set_chars_option(char_u **varp)
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
+ c1 = c2 = c3 = 0;
s = p + len + 1;
// TODO(bfredl): use schar_T representation and utfc_ptr2len
@@ -3472,7 +3483,7 @@ static char_u *set_chars_option(char_u **varp)
if (mb_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) {
continue;
}
- if (tab[i].cp == &lcs_tab2) {
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
if (*s == NUL) {
continue;
}
@@ -3481,15 +3492,23 @@ static char_u *set_chars_option(char_u **varp)
if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) {
continue;
}
+ if (!(*s == ',' || *s == NUL)) {
+ int c3len = utf_ptr2len(s);
+ c3 = mb_cptr2char_adv((const char_u **)&s);
+ if (mb_char2cells(c3) > 1 || (c3len == 1 && c3 > 127)) {
+ continue;
+ }
+ }
}
if (*s == ',' || *s == NUL) {
if (round) {
- if (tab[i].cp == &lcs_tab2) {
- lcs_tab1 = c1;
- lcs_tab2 = c2;
- } else if (tab[i].cp != NULL)
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ wp->w_p_lcs_chars.tab1 = c1;
+ wp->w_p_lcs_chars.tab2 = c2;
+ wp->w_p_lcs_chars.tab3 = c3;
+ } else if (tab[i].cp != NULL) {
*(tab[i].cp) = c1;
-
+ }
}
p = s;
break;
@@ -4149,7 +4168,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
errmsg = e_positive;
}
} else if (pp == &p_ch) {
- if (value < 1) {
+ int minval = ui_has(kUIMessages) ? 0 : 1;
+ if (value < minval) {
errmsg = e_positive;
}
} else if (pp == &p_tm) {
@@ -4223,8 +4243,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
} else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
errmsg = e_invarg;
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
- if (value < -1 || value > SB_MAX
- || (value != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) {
+ if (value < -1 || value > SB_MAX) {
errmsg = e_invarg;
}
} else if (pp == &curbuf->b_p_sw || pp == &p_sw) {
@@ -4258,6 +4277,9 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
p_window = Rows - 1;
}
} else if (pp == &p_ch) {
+ if (ui_has(kUIMessages)) {
+ p_ch = 0;
+ }
if (p_ch > Rows - min_rows() + 1) {
p_ch = Rows - min_rows() + 1;
}
@@ -4322,6 +4344,14 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
if (p_uc && !old_value) {
ml_open_files();
}
+ } else if (pp == &p_pb) {
+ p_pb = MAX(MIN(p_pb, 100), 0);
+ if (old_value != 0) {
+ hl_invalidate_blends();
+ }
+ if (pum_drawn()) {
+ pum_recompose();
+ }
} else if (pp == &p_pyx) {
if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
errmsg = e_invarg;
@@ -4339,7 +4369,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
if (curbuf->terminal) {
// Force the scrollback to take effect.
- terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX);
+ terminal_check_size(curbuf->terminal);
}
} else if (pp == &curwin->w_p_nuw) {
curwin->w_nrwidth_line_count = 0;
@@ -4418,11 +4448,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
*(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp;
}
- if (pp == &curbuf->b_p_scbk && !curbuf->terminal) {
- // Normal buffer: reset local 'scrollback' after updating the global value.
- curbuf->b_p_scbk = -1;
- }
-
options[opt_idx].flags |= P_WAS_SET;
// Don't do this while starting up, failure or recursively.
@@ -5613,6 +5638,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
+ case PV_FCS: return (char_u *)&(curwin->w_p_fcs);
+ case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
default: IEMSG(_("E356: get_varp ERROR"));
}
/* always return a valid pointer to avoid a crash! */
@@ -5691,6 +5718,8 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_fmr = vim_strsave(from->wo_fmr);
to->wo_scl = vim_strsave(from->wo_scl);
to->wo_winhl = vim_strsave(from->wo_winhl);
+ to->wo_fcs = vim_strsave(from->wo_fcs);
+ to->wo_lcs = vim_strsave(from->wo_lcs);
check_winopt(to); // don't want NULL pointers
}
@@ -5721,6 +5750,8 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_cocu);
check_string_option(&wop->wo_briopt);
check_string_option(&wop->wo_winhl);
+ check_string_option(&wop->wo_fcs);
+ check_string_option(&wop->wo_lcs);
}
/*
@@ -5741,12 +5772,16 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_cocu);
clear_string_option(&wop->wo_briopt);
clear_string_option(&wop->wo_winhl);
+ clear_string_option(&wop->wo_fcs);
+ clear_string_option(&wop->wo_lcs);
}
void didset_window_options(win_T *wp)
{
check_colorcolumn(wp);
briopt_check(wp);
+ set_chars_option(wp, &wp->w_p_fcs);
+ set_chars_option(wp, &wp->w_p_lcs);
parse_winhl_opt(wp);
}
@@ -5835,7 +5870,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_ai = p_ai;
buf->b_p_ai_nopaste = p_ai_nopaste;
buf->b_p_sw = p_sw;
- buf->b_p_scbk = -1;
+ buf->b_p_scbk = p_scbk;
buf->b_p_tw = p_tw;
buf->b_p_tw_nopaste = p_tw_nopaste;
buf->b_p_tw_nobin = p_tw_nobin;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 7b99b6f266..ccb0496495 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -375,6 +375,7 @@ EXTERN int p_confirm; // 'confirm'
EXTERN int p_cp; // 'compatible'
EXTERN char_u *p_cot; // 'completeopt'
EXTERN long p_ph; // 'pumheight'
+EXTERN long p_pb; // 'pumblend'
EXTERN char_u *p_cpo; // 'cpoptions'
EXTERN char_u *p_csprg; // 'cscopeprg'
EXTERN int p_csre; // 'cscoperelative'
@@ -486,7 +487,6 @@ EXTERN long *p_linespace; // 'linespace'
EXTERN char_u *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
-EXTERN char_u *p_lcs; // 'listchars'
EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
@@ -638,7 +638,6 @@ EXTERN long p_ul; ///< 'undolevels'
EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
-EXTERN char_u *p_fcs; ///< 'fillchar'
EXTERN char_u *p_shada; ///< 'shada'
EXTERN char_u *p_vdir; ///< 'viewdir'
EXTERN char_u *p_vop; ///< 'viewoptions'
@@ -814,6 +813,8 @@ enum {
, WV_WRAP
, WV_SCL
, WV_WINHL
+ , WV_FCS
+ , WV_LCS
, WV_COUNT // must be the last one
};
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index aba8f8b893..b8f128103c 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -806,11 +806,11 @@ return {
},
{
full_name='fillchars', abbreviation='fcs',
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vi_def=true,
- redraw={'all_windows'},
- varname='p_fcs',
+ alloced=true,
+ redraw={'current_window'},
defaults={if_true={vi=''}}
},
{
@@ -1173,9 +1173,7 @@ return {
vi_def=true,
varname='p_iminsert', pv_name='p_imi',
defaults={
- condition='B_IMODE_IM',
- if_true={vi=macros('B_IMODE_IM')},
- if_false={vi=macros('B_IMODE_NONE')},
+ if_true={vi=macros('B_IMODE_NONE')},
}
},
{
@@ -1184,9 +1182,7 @@ return {
vi_def=true,
varname='p_imsearch', pv_name='p_ims',
defaults={
- condition='B_IMODE_IM',
- if_true={vi=macros('B_IMODE_IM')},
- if_false={vi=macros('B_IMODE_NONE')},
+ if_true={vi=macros('B_IMODE_USE_INSERT')},
}
},
{
@@ -1425,11 +1421,11 @@ return {
},
{
full_name='listchars', abbreviation='lcs',
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vim=true,
- redraw={'all_windows'},
- varname='p_lcs',
+ alloced=true,
+ redraw={'current_window'},
defaults={if_true={vi="eol:$", vim="tab:> ,trail:-,nbsp:+"}}
},
{
@@ -1810,6 +1806,14 @@ return {
defaults={if_true={vi=0}}
},
{
+ full_name='pumblend', abbreviation='pb',
+ type='number', scope={'global'},
+ vi_def=true,
+ redraw={'ui_option'},
+ varname='p_pb',
+ defaults={if_true={vi=0}}
+ },
+ {
full_name='pyxversion', abbreviation='pyx',
type='number', scope={'global'},
secure=true,
@@ -1934,7 +1938,7 @@ return {
vi_def=true,
varname='p_scbk',
redraw={'current_buffer'},
- defaults={if_true={vi=10000}}
+ defaults={if_true={vi=-1}}
},
{
full_name='scrollbind', abbreviation='scb',
diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c
index 267cf5ae4b..bbd0424a82 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <uv.h>
+#include "nvim/os/dl.h"
#include "nvim/os/os.h"
#include "nvim/memory.h"
#include "nvim/message.h"
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index f62253cbce..5e2c9ecb36 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -236,6 +236,53 @@ size_t input_enqueue(String keys)
return rv;
}
+static uint8_t check_multiclick(int code, int grid, int row, int col)
+{
+ static int orig_num_clicks = 0;
+ static int orig_mouse_code = 0;
+ static int orig_mouse_grid = 0;
+ static int orig_mouse_col = 0;
+ static int orig_mouse_row = 0;
+ static uint64_t orig_mouse_time = 0; // time of previous mouse click
+
+ if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE
+ || code == KE_MIDDLERELEASE) {
+ return 0;
+ }
+ uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
+
+ // compute the time elapsed since the previous mouse click and
+ // convert p_mouse from ms to ns
+ uint64_t timediff = mouse_time - orig_mouse_time;
+ uint64_t mouset = (uint64_t)p_mouset * 1000000;
+ if (code == orig_mouse_code
+ && timediff < mouset
+ && orig_num_clicks != 4
+ && orig_mouse_grid == grid
+ && orig_mouse_col == col
+ && orig_mouse_row == row) {
+ orig_num_clicks++;
+ } else {
+ orig_num_clicks = 1;
+ }
+ orig_mouse_code = code;
+ orig_mouse_grid = grid;
+ orig_mouse_col = col;
+ orig_mouse_row = row;
+ orig_mouse_time = mouse_time;
+
+ uint8_t modifiers = 0;
+ if (orig_num_clicks == 2) {
+ modifiers |= MOD_MASK_2CLICK;
+ } else if (orig_num_clicks == 3) {
+ modifiers |= MOD_MASK_3CLICK;
+ } else if (orig_num_clicks == 4) {
+ modifiers |= MOD_MASK_4CLICK;
+ }
+ return modifiers;
+}
+
+
// Mouse event handling code(Extract row/col if available and detect multiple
// clicks)
static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
@@ -274,48 +321,16 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
if (row >= Rows) {
row = (int)Rows - 1;
}
+ mouse_grid = 0;
mouse_row = row;
mouse_col = col;
}
*ptr += advance;
}
- static int orig_num_clicks = 0;
- if (mouse_code != KE_LEFTRELEASE && mouse_code != KE_RIGHTRELEASE
- && mouse_code != KE_MIDDLERELEASE) {
- static int orig_mouse_code = 0;
- static int orig_mouse_col = 0;
- static int orig_mouse_row = 0;
- static uint64_t orig_mouse_time = 0; // time of previous mouse click
- uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
-
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
- uint64_t timediff = mouse_time - orig_mouse_time;
- uint64_t mouset = (uint64_t)p_mouset * 1000000;
- if (mouse_code == orig_mouse_code
- && timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_col == mouse_col
- && orig_mouse_row == mouse_row) {
- orig_num_clicks++;
- } else {
- orig_num_clicks = 1;
- }
- orig_mouse_code = mouse_code;
- orig_mouse_col = mouse_col;
- orig_mouse_row = mouse_row;
- orig_mouse_time = mouse_time;
- }
+ uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
+ mouse_row, mouse_col);
- uint8_t modifiers = 0;
- if (orig_num_clicks == 2) {
- modifiers |= MOD_MASK_2CLICK;
- } else if (orig_num_clicks == 3) {
- modifiers |= MOD_MASK_3CLICK;
- } else if (orig_num_clicks == 4) {
- modifiers |= MOD_MASK_4CLICK;
- }
if (modifiers) {
if (buf[1] != KS_MODIFIER) {
@@ -334,6 +349,30 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
return bufsize;
}
+size_t input_enqueue_mouse(int code, uint8_t modifier,
+ int grid, int row, int col)
+{
+ modifier |= check_multiclick(code, grid, row, col);
+ uint8_t buf[7], *p = buf;
+ if (modifier) {
+ p[0] = K_SPECIAL;
+ p[1] = KS_MODIFIER;
+ p[2] = modifier;
+ p += 3;
+ }
+ p[0] = K_SPECIAL;
+ p[1] = KS_EXTRA;
+ p[2] = (uint8_t)code;
+
+ mouse_grid = grid;
+ mouse_row = row;
+ mouse_col = col;
+
+ size_t written = 3 + (size_t)(p-buf);
+ rbuffer_write(input_buffer, (char *)buf, written);
+ return written;
+}
+
/// @return true if the main loop is blocked and waiting for input.
bool input_blocking(void)
{
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index 108a9c6c39..fe2d7986bf 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -11,6 +11,7 @@
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
+#include "nvim/os/lang.h"
#include "nvim/os/os.h"
void lang_init(void)
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index fc7f9cefd1..20f68233e7 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -15,6 +15,7 @@
#include "nvim/globals.h"
#include "nvim/memline.h"
#include "nvim/eval.h"
+#include "nvim/fileio.h"
#include "nvim/main.h"
#include "nvim/memory.h"
#include "nvim/misc1.h"
@@ -22,7 +23,7 @@
#include "nvim/os/signal.h"
#include "nvim/event/loop.h"
-static SignalWatcher spipe, shup, squit, sterm;
+static SignalWatcher spipe, shup, squit, sterm, susr1;
#ifdef SIGPWR
static SignalWatcher spwr;
#endif
@@ -61,6 +62,10 @@ void signal_init(void)
signal_watcher_init(&main_loop, &spwr, NULL);
signal_watcher_start(&spwr, on_signal, SIGPWR);
#endif
+#ifdef SIGUSR1
+ signal_watcher_init(&main_loop, &susr1, NULL);
+ signal_watcher_start(&susr1, on_signal, SIGUSR1);
+#endif
}
void signal_teardown(void)
@@ -73,6 +78,9 @@ void signal_teardown(void)
#ifdef SIGPWR
signal_watcher_close(&spwr, NULL);
#endif
+#ifdef SIGUSR1
+ signal_watcher_close(&susr1, NULL);
+#endif
}
void signal_stop(void)
@@ -84,6 +92,9 @@ void signal_stop(void)
#ifdef SIGPWR
signal_watcher_stop(&spwr);
#endif
+#ifdef SIGUSR1
+ signal_watcher_stop(&susr1);
+#endif
}
void signal_reject_deadly(void)
@@ -115,6 +126,10 @@ static char * signal_name(int signum)
#endif
case SIGHUP:
return "SIGHUP";
+#ifdef SIGUSR1
+ case SIGUSR1:
+ return "SIGUSR1";
+#endif
default:
return "Unknown";
}
@@ -162,6 +177,12 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
deadly_signal(signum);
}
break;
+#ifdef SIGUSR1
+ case SIGUSR1:
+ apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGUSR1", curbuf->b_fname, true,
+ curbuf);
+ break;
+#endif
default:
ELOG("invalid signal: %d", signum);
break;
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 09ba718302..351350d939 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -139,7 +139,7 @@ void mch_exit(int r)
exiting = true;
ui_flush();
- ui_builtin_stop();
+ ui_call_stop();
ml_close_all(true); // remove all memfiles
if (!event_teardown() && r == 0) {
diff --git a/src/nvim/path.c b/src/nvim/path.c
index de697642c7..7903e3f4f4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1267,7 +1267,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
}
*num_file = ga.ga_len;
- *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : (char_u **)"";
+ *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : NULL;
recursive = false;
@@ -2039,6 +2039,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
char_u *ffname;
// check all files in (*files)[]
+ assert(*num_files == 0 || *files != NULL);
for (i = 0; i < *num_files; i++) {
ffname = (char_u *)FullName_save((char *)(*files)[i], false);
assert((*files)[i] != NULL);
@@ -2056,16 +2057,16 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
}
}
- /*
- * Move the names where 'suffixes' match to the end.
- */
+ //
+ // Move the names where 'suffixes' match to the end.
+ //
+ assert(*num_files == 0 || *files != NULL);
if (*num_files > 1) {
non_suf_match = 0;
for (i = 0; i < *num_files; i++) {
if (!match_suffix((*files)[i])) {
//
- // Move the name without matching suffix to the front
- // of the list.
+ // Move the name without matching suffix to the front of the list.
//
p = (*files)[i];
for (j = i; j > non_suf_match; j--) {
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 056770f2c0..3c10b7ae0f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -19,6 +19,7 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/screen.h"
+#include "nvim/ui_compositor.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/memory.h"
@@ -41,7 +42,11 @@ static int pum_row; // top row of pum
static int pum_col; // left column of pum
static bool pum_is_visible = false;
+static bool pum_is_drawn = false;
static bool pum_external = false;
+static bool pum_invalid = false; // the screen was just cleared
+
+static ScreenGrid pum_grid = SCREEN_GRID_INIT;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "popupmnu.c.generated.h"
@@ -78,13 +83,14 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
if (!pum_is_visible) {
// To keep the code simple, we only allow changing the
// draw mode when the popup menu is not being displayed
- pum_external = ui_is_external(kUIPopupmenu);
+ pum_external = ui_has(kUIPopupmenu);
}
do {
// Mark the pum as visible already here,
// to avoid that must_redraw is set when 'cursorcolumn' is on.
pum_is_visible = true;
+ pum_is_drawn = true;
validate_cursor_col();
above_row = 0;
below_row = cmdline_row;
@@ -98,7 +104,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
}
int grid = (int)curwin->w_grid.handle;
- if (!ui_is_external(kUIMultigrid)) {
+ if (!ui_has(kUIMultigrid)) {
grid = (int)default_grid.handle;
row += curwin->w_winrow;
col += curwin->w_wincol;
@@ -317,7 +323,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
/// Redraw the popup menu, using "pum_first" and "pum_selected".
void pum_redraw(void)
{
- int row = pum_row;
+ int row = 0;
int col;
int attr_norm = win_hl_attr(curwin, HLF_PNI);
int attr_select = win_hl_attr(curwin, HLF_PSI);
@@ -334,6 +340,39 @@ void pum_redraw(void)
int round;
int n;
+ int grid_width = pum_width;
+ int col_off = 0;
+ bool extra_space = false;
+ if (curwin->w_p_rl) {
+ col_off = pum_width;
+ if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
+ grid_width += 1;
+ extra_space = true;
+ }
+ } else if (pum_col > 0) {
+ grid_width += 1;
+ col_off = 1;
+ extra_space = true;
+ }
+ if (pum_scrollbar > 0) {
+ grid_width++;
+ }
+
+ grid_assign_handle(&pum_grid);
+ bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off,
+ pum_height, grid_width);
+ bool invalid_grid = moved || pum_invalid;
+ pum_invalid = false;
+
+ if (!pum_grid.chars
+ || pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) {
+ grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false);
+ ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows);
+ } else if (invalid_grid) {
+ grid_invalidate(&pum_grid);
+ }
+
+
// Never display more than we have
if (pum_first > pum_size - pum_height) {
pum_first = pum_size - pum_height;
@@ -356,17 +395,17 @@ void pum_redraw(void)
screen_puts_line_start(row);
// prepend a space if there is room
- if (curwin->w_p_rl) {
- if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
- grid_putchar(&default_grid, ' ', row, pum_col + 1, attr);
+ if (extra_space) {
+ if (curwin->w_p_rl) {
+ grid_putchar(&pum_grid, ' ', row, col_off + 1, attr);
+ } else {
+ grid_putchar(&pum_grid, ' ', row, col_off - 1, attr);
}
- } else if (pum_col > 0) {
- grid_putchar(&default_grid, ' ', row, pum_col - 1, attr);
}
// Display each entry, use two spaces for a Tab.
// Do this 3 times: For the main text, kind and extra info
- col = pum_col;
+ col = col_off;
totwidth = 0;
for (round = 1; round <= 3; ++round) {
@@ -423,13 +462,13 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&default_grid, rt, (int)STRLEN(rt), row,
+ grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
col - size + 1, attr);
xfree(rt_start);
xfree(st);
col -= width;
} else {
- grid_puts_len(&default_grid, st, (int)STRLEN(st), row, col, attr);
+ grid_puts_len(&pum_grid, st, (int)STRLEN(st), row, col, attr);
xfree(st);
col += width;
}
@@ -440,11 +479,11 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (curwin->w_p_rl) {
- grid_puts_len(&default_grid, (char_u *)" ", 2, row, col - 1,
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
attr);
col -= 2;
} else {
- grid_puts_len(&default_grid, (char_u *)" ", 2, row, col, attr);
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
col += 2;
}
totwidth += 2;
@@ -475,37 +514,37 @@ void pum_redraw(void)
}
if (curwin->w_p_rl) {
- grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
col + 1, ' ', ' ', attr);
- col = pum_col - pum_base_width - n + 1;
+ col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&default_grid, row, row + 1, col,
- pum_col + pum_base_width + n, ' ', ' ', attr);
- col = pum_col + pum_base_width + n;
+ grid_fill(&pum_grid, row, row + 1, col,
+ col_off + pum_base_width + n, ' ', ' ', attr);
+ col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (curwin->w_p_rl) {
- grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
' ', ' ', attr);
} else {
- grid_fill(&default_grid, row, row + 1, col, pum_col + pum_width, ' ', ' ',
+ grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
attr);
}
if (pum_scrollbar > 0) {
if (curwin->w_p_rl) {
- grid_putchar(&default_grid, ' ', row, pum_col - pum_width,
+ grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll);
} else {
- grid_putchar(&default_grid, ' ', row, pum_col + pum_width,
+ grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll);
}
}
- grid_puts_line_flush(&default_grid, false);
+ grid_puts_line_flush(&pum_grid, false);
row++;
}
}
@@ -696,6 +735,8 @@ static int pum_set_selected(int n, int repeat)
// Update the screen before drawing the popup menu.
// Enable updating the status lines.
+ // TODO(bfredl): can simplify, get rid of the flag munging?
+ // or at least eliminate extra redraw before win_enter()?
pum_is_visible = false;
update_screen(0);
pum_is_visible = true;
@@ -725,17 +766,27 @@ static int pum_set_selected(int n, int repeat)
}
/// Undisplay the popup menu (later).
-void pum_undisplay(void)
+void pum_undisplay(bool immediate)
{
pum_is_visible = false;
pum_array = NULL;
- if (pum_external) {
- ui_call_popupmenu_hide();
- } else {
- redraw_all_later(SOME_VALID);
- redraw_tabline = true;
- status_redraw_all();
+ if (immediate) {
+ pum_check_clear();
+ }
+}
+
+void pum_check_clear(void)
+{
+ if (!pum_is_visible && pum_is_drawn) {
+ if (pum_external) {
+ ui_call_popupmenu_hide();
+ } else {
+ ui_comp_remove_grid(&pum_grid);
+ // TODO(bfredl): consider keeping float grids allocated.
+ grid_free(&pum_grid);
+ }
+ pum_is_drawn = false;
}
}
@@ -758,6 +809,17 @@ bool pum_drawn(void)
return pum_visible() && !pum_external;
}
+/// Screen was cleared, need to redraw next time
+void pum_invalidate(void)
+{
+ pum_invalid = true;
+}
+
+void pum_recompose(void)
+{
+ ui_comp_compose_grid(&pum_grid);
+}
+
/// Gets the height of the menu.
///
/// @return the height of the popup menu, the number of entries visible.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 4eeddf1d5a..f0c37c0e38 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -44,6 +44,7 @@
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
struct dir_stack_T {
@@ -2155,7 +2156,8 @@ win_found:
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
msg_scroll = false;
}
- msg_attr_keep(IObuff, 0, true);
+ msg_ext_set_kind("quickfix");
+ msg_attr_keep(IObuff, 0, true, false);
msg_scroll = (int)i;
}
} else {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 0b9e1cfdec..a70b150e9b 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -230,6 +230,7 @@
#define UPPER 47 /* Match uppercase char */
#define NUPPER 48 /* Match non-uppercase char */
#define LAST_NL NUPPER + ADD_NL
+// -V:WITH_NL:560
#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
#define MOPEN 80 // -89 Mark this point in input as start of
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2467cf192f..5ac90ab601 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -110,6 +110,7 @@
#include "nvim/syntax.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
@@ -153,6 +154,8 @@ static bool highlights_invalid = false;
static bool conceal_cursor_used = false;
+static bool redraw_popupmenu = false;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
#endif
@@ -271,9 +274,10 @@ void update_screen(int type)
static int did_intro = FALSE;
int did_one;
- /* Don't do anything if the screen structures are (not yet) valid. */
- if (!screen_valid(TRUE))
+ // Don't do anything if the screen structures are (not yet) valid.
+ if (!default_grid.chars) {
return;
+ }
if (must_redraw) {
if (type < must_redraw) /* use maximal type */
@@ -304,11 +308,15 @@ void update_screen(int type)
++display_tick; /* let syntax code know we're in a next round of
* display updating */
- /*
- * if the screen was scrolled up when displaying a message, scroll it down
- */
- if (msg_scrolled) {
+ // Tricky: vim code can reset msg_scrolled behind our back, so need
+ // separate bookkeeping for now.
+ if (msg_did_scroll) {
ui_call_win_scroll_over_reset();
+ msg_did_scroll = false;
+ }
+
+ // if the screen was scrolled up when displaying a message, scroll it down
+ if (msg_scrolled) {
clear_cmdline = true;
if (dy_flags & DY_MSGSEP) {
int valid = MAX(Rows - msg_scrollsize(), 0);
@@ -324,14 +332,12 @@ void update_screen(int type)
wp->w_redr_status = true;
}
}
- } else if (msg_scrolled > default_grid.Rows - 5) { // clearing is faster
+ } else if (msg_scrolled > Rows - 5) { // clearing is faster
type = CLEAR;
} else if (type != CLEAR) {
check_for_delay(false);
- if (grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows,
- 0, (int)Columns) == FAIL) {
- type = CLEAR;
- }
+ grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows,
+ 0, (int)Columns);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_winrow < msg_scrolled) {
if (W_ENDROW(wp) > msg_scrolled
@@ -356,6 +362,8 @@ void update_screen(int type)
need_wait_return = FALSE;
}
+ msg_ext_check_prompt();
+
/* reset cmdline_row now (may have been changed temporarily) */
compute_cmdrow();
@@ -453,17 +461,20 @@ void update_screen(int type)
/* redraw status line after the window to minimize cursor movement */
if (wp->w_redr_status) {
- win_redr_status(wp, true); // any popup menu will be redrawn below
+ win_redr_status(wp);
}
}
- send_grid_resize = false;
- highlights_invalid = false;
+
end_search_hl();
// May need to redraw the popup menu.
- if (pum_drawn()) {
+ if (pum_drawn() && redraw_popupmenu) {
pum_redraw();
}
+ send_grid_resize = false;
+ highlights_invalid = false;
+ redraw_popupmenu = false;
+
/* Reset b_mod_set flags. Going through all windows is probably faster
* than going through all buffers (there could be many buffers). */
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -474,8 +485,9 @@ void update_screen(int type)
/* Clear or redraw the command line. Done last, because scrolling may
* mess up the command line. */
- if (clear_cmdline || redraw_cmdline)
+ if (clear_cmdline || redraw_cmdline) {
showmode();
+ }
/* May put up an introductory message when not editing a file */
if (!did_intro)
@@ -629,9 +641,7 @@ static void win_update(win_T *wp)
wp->w_nrwidth = i;
if (buf->terminal) {
- terminal_resize(buf->terminal,
- (uint16_t)(MAX(0, wp->w_grid.Columns - win_col_off(wp))),
- (uint16_t)wp->w_grid.Rows);
+ terminal_check_size(buf->terminal);
}
} else if (buf->b_mod_set
&& buf->b_mod_xlines != 0
@@ -820,31 +830,31 @@ static void win_update(win_T *wp)
// Try to insert the correct number of lines.
// If not the last window, delete the lines at the bottom.
// win_ins_lines may fail when the terminal can't do it.
- if (win_ins_lines(wp, 0, i) == OK) {
- if (wp->w_lines_valid != 0) {
- /* Need to update rows that are new, stop at the
- * first one that scrolled down. */
- top_end = i;
- scrolled_down = TRUE;
-
- /* Move the entries that were scrolled, disable
- * the entries for the lines to be redrawn. */
- if ((wp->w_lines_valid += j) > wp->w_grid.Rows) {
- wp->w_lines_valid = wp->w_grid.Rows;
- }
- for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
- wp->w_lines[idx] = wp->w_lines[idx - j];
- }
- while (idx >= 0) {
- wp->w_lines[idx--].wl_valid = false;
- }
+ win_scroll_lines(wp, 0, i);
+ if (wp->w_lines_valid != 0) {
+ // Need to update rows that are new, stop at the
+ // first one that scrolled down.
+ top_end = i;
+ scrolled_down = true;
+
+ // Move the entries that were scrolled, disable
+ // the entries for the lines to be redrawn.
+ if ((wp->w_lines_valid += j) > wp->w_grid.Rows) {
+ wp->w_lines_valid = wp->w_grid.Rows;
}
- } else
- mid_start = 0; /* redraw all lines */
- } else
- mid_start = 0; /* redraw all lines */
- } else
- mid_start = 0; /* redraw all lines */
+ for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
+ wp->w_lines[idx] = wp->w_lines[idx - j];
+ }
+ while (idx >= 0) {
+ wp->w_lines[idx--].wl_valid = false;
+ }
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
} else {
/*
* New topline is at or below old topline: May scroll up.
@@ -881,11 +891,8 @@ static void win_update(win_T *wp)
/* ... but don't delete new filler lines. */
row -= wp->w_topfill;
if (row > 0) {
- if (win_del_lines(wp, 0, row) == OK) {
- bot_start = wp->w_grid.Rows - row;
- } else {
- mid_start = 0; // redraw all lines
- }
+ win_scroll_lines(wp, 0, -row);
+ bot_start = wp->w_grid.Rows - row;
}
if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
/*
@@ -1140,7 +1147,7 @@ static void win_update(win_T *wp)
// Update a line when it is in an area that needs updating, when it
// has changes or w_lines[idx] is invalid.
// "bot_start" may be halfway a wrapped line after using
- // win_del_lines(), check if the current line includes it.
+ // win_scroll_lines(), check if the current line includes it.
// When syntax folding is being used, the saved syntax states will
// already have been updated, we can't see where the syntax state is
// the same again, just update until the end of the window.
@@ -1236,11 +1243,8 @@ static void win_update(win_T *wp)
if (row - xtra_rows >= wp->w_grid.Rows - 2) {
mod_bot = MAXLNUM;
} else {
- if (win_del_lines(wp, row, -xtra_rows) == FAIL) {
- mod_bot = MAXLNUM;
- } else {
- bot_start = wp->w_grid.Rows + xtra_rows;
- }
+ win_scroll_lines(wp, row, xtra_rows);
+ bot_start = wp->w_grid.Rows + xtra_rows;
}
} else if (xtra_rows > 0) {
/* May scroll text down. If there is not enough
@@ -1249,9 +1253,8 @@ static void win_update(win_T *wp)
if (row + xtra_rows >= wp->w_grid.Rows - 2) {
mod_bot = MAXLNUM;
} else {
- if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) {
- mod_bot = MAXLNUM;
- } else if (top_end > row + old_rows) {
+ win_scroll_lines(wp, row + old_rows, xtra_rows);
+ if (top_end > row + old_rows) {
// Scrolled the part at the top that requires
// updating down.
top_end += xtra_rows;
@@ -1448,13 +1451,12 @@ static void win_update(win_T *wp)
wp->w_botline = buf->b_ml.ml_line_count + 1;
j = diff_check_fill(wp, wp->w_botline);
if (j > 0 && !wp->w_botfill) {
- /*
- * Display filler lines at the end of the file
- */
- if (char2cells(fill_diff) > 1)
+ // display filler lines at the end of the file
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
i = '-';
- else
- i = fill_diff;
+ } else {
+ i = wp->w_p_fcs_chars.diff;
+ }
if (row + j > wp->w_grid.Rows) {
j = wp->w_grid.Rows - row;
}
@@ -1465,8 +1467,8 @@ static void win_update(win_T *wp)
wp->w_botline = lnum;
// make sure the rest of the screen is blank
- // write the 'fill_eob' character to rows that aren't part of the file.
- win_draw_end(wp, fill_eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
+ // write the 'eob' character to rows that aren't part of the file.
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
}
if (wp->w_redr_type >= REDRAW_TOP) {
@@ -1817,7 +1819,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
txtcol = col; /* remember where text starts */
- // 5. move the text to linebuf_char[off]. Fill up with "fill_fold".
+ // 5. move the text to linebuf_char[off]. Fill up with "fold".
// Right-left text is put in columns 0 - number-col, normal text is put
// in columns number-col - window-width.
int idx;
@@ -1849,7 +1851,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
col -= txtcol;
schar_T sc;
- schar_from_char(sc, fill_fold);
+ schar_from_char(sc, wp->w_p_fcs_chars.fold);
while (col < wp->w_grid.Columns
- (wp->w_p_rl ? txtcol : 0)
) {
@@ -2042,30 +2044,33 @@ win_line (
bool number_only // only update the number column
)
{
- int c = 0; // init for GCC
- long vcol = 0; // virtual column (for tabs)
- long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; // "vcol" of previous character
- char_u *line; // current line
- char_u *ptr; // current position in "line"
- int row; // row in the window, excl w_winrow
- ScreenGrid *grid = &wp->w_grid; // grid specfic to the window
-
- char_u extra[18]; /* line number and 'fdc' must fit in here */
- int n_extra = 0; /* number of extra chars */
- char_u *p_extra = NULL; /* string of extra chars, plus NUL */
- char_u *p_extra_free = NULL; /* p_extra needs to be freed */
- int c_extra = NUL; /* extra chars, all the same */
- int extra_attr = 0; /* attributes when n_extra != 0 */
- static char_u *at_end_str = (char_u *)""; /* used for p_extra when
- displaying lcs_eol at end-of-line */
- int lcs_eol_one = lcs_eol; /* lcs_eol until it's been used */
- int lcs_prec_todo = lcs_prec; /* lcs_prec until it's been used */
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
+ long vcol_sbr = -1; // virtual column after showbreak
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ ScreenGrid *grid = &wp->w_grid; // grid specfic to the window
+
+ char_u extra[18]; // line number and 'fdc' must fit in here
+ int n_extra = 0; // number of extra chars
+ char_u *p_extra = NULL; // string of extra chars, plus NUL
+ char_u *p_extra_free = NULL; // p_extra needs to be freed
+ int c_extra = NUL; // extra chars, all the same
+ int c_final = NUL; // final char, mandatory if set
+ int extra_attr = 0; // attributes when n_extra != 0
+ static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
+ // curwin->w_p_lcs_chars.eol at
+ // end-of-line
+ int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
+ int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
/* saved "extra" items for when draw_state becomes WL_LINE (again) */
int saved_n_extra = 0;
char_u *saved_p_extra = NULL;
int saved_c_extra = 0;
+ int saved_c_final = 0;
int saved_char_attr = 0;
int n_attr = 0; /* chars with special attr */
@@ -2432,11 +2437,11 @@ win_line (
}
if (wp->w_p_list) {
- if (lcs_space || lcs_trail) {
+ if (curwin->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail) {
extra_check = true;
}
// find start of trailing whitespace
- if (lcs_trail) {
+ if (wp->w_p_lcs_chars.trail) {
trailcol = (colnr_T)STRLEN(ptr);
while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
trailcol--;
@@ -2644,6 +2649,7 @@ win_line (
/* Draw the cmdline character. */
n_extra = 1;
c_extra = cmdwin_type;
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_AT);
}
}
@@ -2662,6 +2668,7 @@ win_line (
p_extra_free[n_extra] = NUL;
p_extra = p_extra_free;
c_extra = NUL;
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_FC);
}
}
@@ -2675,6 +2682,7 @@ win_line (
int text_sign;
// Draw cells with the sign value or blank.
c_extra = ' ';
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_SC);
n_extra = win_signcol_width(wp);
@@ -2685,6 +2693,7 @@ win_line (
int symbol_blen = (int)STRLEN(p_extra);
if (p_extra != NULL) {
c_extra = NUL;
+ c_final = NUL;
// symbol(s) bytes + (filling spaces) (one byte each)
n_extra = symbol_blen +
(win_signcol_width(wp) - mb_string2cells(p_extra));
@@ -2736,8 +2745,11 @@ win_line (
rl_mirror(extra);
p_extra = extra;
c_extra = NUL;
- } else
+ c_final = NUL;
+ } else {
c_extra = ' ';
+ c_final = NUL;
+ }
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
@@ -2792,11 +2804,14 @@ win_line (
if (draw_state == WL_SBR - 1 && n_extra == 0) {
draw_state = WL_SBR;
if (filler_todo > 0) {
- /* Draw "deleted" diff line(s). */
- if (char2cells(fill_diff) > 1)
+ // draw "deleted" diff line(s)
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
c_extra = '-';
- else
- c_extra = fill_diff;
+ c_final = NUL;
+ } else {
+ c_extra = wp->w_p_fcs_chars.diff;
+ c_final = NUL;
+ }
if (wp->w_p_rl) {
n_extra = col + 1;
} else {
@@ -2808,6 +2823,7 @@ win_line (
/* Draw 'showbreak' at the start of each broken line. */
p_extra = p_sbr;
c_extra = NUL;
+ c_final = NUL;
n_extra = (int)STRLEN(p_sbr);
char_attr = win_hl_attr(wp, HLF_AT);
need_showbreak = false;
@@ -2829,6 +2845,7 @@ win_line (
/* Continue item from end of wrapped line. */
n_extra = saved_n_extra;
c_extra = saved_c_extra;
+ c_final = saved_c_final;
p_extra = saved_p_extra;
char_attr = saved_char_attr;
} else {
@@ -3026,20 +3043,18 @@ win_line (
}
}
- /*
- * Get the next character to put on the screen.
- */
- /*
- * The "p_extra" points to the extra stuff that is inserted to
- * represent special characters (non-printable stuff) and other
- * things. When all characters are the same, c_extra is used.
- * "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
- * "p_extra[n_extra]".
- * For the '$' of the 'list' option, n_extra == 1, p_extra == "".
- */
+ // Get the next character to put on the screen.
+ //
+ // The "p_extra" points to the extra stuff that is inserted to
+ // represent special characters (non-printable stuff) and other
+ // things. When all characters are the same, c_extra is used.
+ // If c_final is set, it will compulsorily be used at the end.
+ // "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
+ // "p_extra[n_extra]".
+ // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
if (n_extra > 0) {
- if (c_extra != NUL) {
- c = c_extra;
+ if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
+ c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
mb_c = c; // doesn't handle non-utf-8 multi-byte!
if (enc_utf8 && utf_char2len(c) > 1) {
mb_utf8 = true;
@@ -3146,6 +3161,7 @@ win_line (
mb_utf8 = (c >= 0x80);
n_extra = (int)STRLEN(p_extra);
c_extra = NUL;
+ c_final = NUL;
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
extra_attr = win_hl_attr(wp, HLF_8);
@@ -3198,6 +3214,7 @@ win_line (
p_extra = extra;
n_extra = (int)STRLEN(extra) - 1;
c_extra = NUL;
+ c_final = NUL;
c = *p_extra++;
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
@@ -3232,6 +3249,7 @@ win_line (
if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
n_extra = 1;
c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
c = ' ';
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
@@ -3391,6 +3409,7 @@ win_line (
- vcol % (int)wp->w_buffer->b_p_ts - 1;
}
c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
+ c_final = NUL;
if (ascii_iswhite(c)) {
if (c == TAB)
/* See "Tab alignment" below. */
@@ -3401,13 +3420,14 @@ win_line (
}
}
- // 'list': change char 160 to lcs_nbsp and space to lcs_space.
+ // 'list': change char 160 to 'nbsp' and space to 'space'.
if (wp->w_p_list
&& (((c == 160
|| (mb_utf8 && (mb_c == 160 || mb_c == 0x202f)))
- && lcs_nbsp)
- || (c == ' ' && lcs_space && ptr - line <= trailcol))) {
- c = (c == ' ') ? lcs_space : lcs_nbsp;
+ && curwin->w_p_lcs_chars.nbsp)
+ || (c == ' ' && curwin->w_p_lcs_chars.space
+ && ptr - line <= trailcol))) {
+ c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3422,7 +3442,7 @@ win_line (
}
if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') {
- c = lcs_trail;
+ c = wp->w_p_lcs_chars.trail;
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3443,7 +3463,7 @@ win_line (
if (!vim_isprintc(c)) {
// when getting a character from the file, we may have to
// turn it into something else on the way to putting it on the screen.
- if (c == TAB && (!wp->w_p_list || lcs_tab1)) {
+ if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
int tab_len = 0;
long vcol_adjusted = vcol; // removed showbreak length
// Only adjust the tab_len, when at the first column after the
@@ -3467,26 +3487,28 @@ win_line (
tab_len += vcol_off;
}
// boguscols before FIX_FOR_BOGUSCOLS macro from above.
- if (lcs_tab1 && old_boguscols > 0 && n_extra > tab_len) {
+ if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
+ && n_extra > tab_len) {
tab_len += n_extra - tab_len;
}
/* if n_extra > 0, it gives the number of chars to use for
* a tab, else we need to calculate the width for a tab */
- int len = (tab_len * mb_char2len(lcs_tab2));
+ int len = (tab_len * mb_char2len(wp->w_p_lcs_chars.tab2));
if (n_extra > 0) {
len += n_extra - tab_len;
}
- c = lcs_tab1;
+ c = wp->w_p_lcs_chars.tab1;
p = xmalloc(len + 1);
memset(p, ' ', len);
p[len] = NUL;
xfree(p_extra_free);
p_extra_free = p;
for (i = 0; i < tab_len; i++) {
- utf_char2bytes(lcs_tab2, p);
- p += mb_char2len(lcs_tab2);
- n_extra += mb_char2len(lcs_tab2) - (saved_nextra > 0 ? 1: 0);
+ utf_char2bytes(wp->w_p_lcs_chars.tab2, p);
+ p += mb_char2len(wp->w_p_lcs_chars.tab2);
+ n_extra += mb_char2len(wp->w_p_lcs_chars.tab2)
+ - (saved_nextra > 0 ? 1: 0);
}
p_extra = p_extra_free;
@@ -3511,19 +3533,23 @@ win_line (
// Make sure, the highlighting for the tab char will be
// correctly set further below (effectively reverts the
// FIX_FOR_BOGSUCOLS macro.
- if (n_extra == tab_len + vc_saved && wp->w_p_list && lcs_tab1) {
+ if (n_extra == tab_len + vc_saved && wp->w_p_list
+ && wp->w_p_lcs_chars.tab1) {
tab_len += vc_saved;
}
}
mb_utf8 = false; // don't draw as UTF-8
if (wp->w_p_list) {
- c = lcs_tab1;
+ c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
+ ? wp->w_p_lcs_chars.tab3
+ : wp->w_p_lcs_chars.tab1;
if (wp->w_p_lbr) {
c_extra = NUL; /* using p_extra from above */
} else {
- c_extra = lcs_tab2;
+ c_extra = wp->w_p_lcs_chars.tab2;
}
+ c_final = wp->w_p_lcs_chars.tab3;
n_attr = tab_len + 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3534,6 +3560,7 @@ win_line (
c = 0xc0;
}
} else {
+ c_final = NUL;
c_extra = ' ';
c = ' ';
}
@@ -3561,10 +3588,11 @@ win_line (
p_extra = at_end_str;
n_extra = 1;
c_extra = NUL;
+ c_final = NUL;
}
}
- if (wp->w_p_list && lcs_eol > 0) {
- c = lcs_eol;
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
+ c = wp->w_p_lcs_chars.eol;
} else {
c = ' ';
}
@@ -3588,6 +3616,7 @@ win_line (
if ((dy_flags & DY_UHEX) && wp->w_p_rl)
rl_mirror(p_extra); /* reverse "<12>" */
c_extra = NUL;
+ c_final = NUL;
if (wp->w_p_lbr) {
char_u *p;
@@ -3634,8 +3663,8 @@ win_line (
c = match_conc;
} else if (syn_get_sub_char() != NUL) {
c = syn_get_sub_char();
- } else if (lcs_conceal != NUL) {
- c = lcs_conceal;
+ } else if (wp->w_p_lcs_chars.conceal != NUL) {
+ c = wp->w_p_lcs_chars.conceal;
} else {
c = ' ';
}
@@ -3705,12 +3734,13 @@ win_line (
&& filler_todo <= 0
&& draw_state > WL_NR
&& c != NUL) {
- c = lcs_prec;
+ c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
if (has_mbyte && (*mb_char2cells)(mb_c) > 1) {
/* Double-width character being overwritten by the "precedes"
* character, need to fill up half the character. */
c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
n_extra = 1;
n_attr = 2;
extra_attr = win_hl_attr(wp, HLF_AT);
@@ -3755,7 +3785,7 @@ win_line (
cur = cur->next;
}
}
- if (lcs_eol == lcs_eol_one
+ if (wp->w_p_lcs_chars.eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V
|| lnum == VIsual.lnum
@@ -3858,7 +3888,8 @@ win_line (
// Make sure alignment is the same regardless
// if listchars=eol:X is used or not.
- bool delay_virttext = lcs_eol == lcs_eol_one && eol_hl_off == 0;
+ bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one
+ && eol_hl_off == 0;
if (wp->w_p_cuc) {
rightmost_vcol = wp->w_virtcol;
@@ -3978,15 +4009,15 @@ win_line (
break;
}
- /* line continues beyond line end */
- if (lcs_ext
+ // line continues beyond line end
+ if (wp->w_p_lcs_chars.ext
&& !wp->w_p_wrap
&& filler_todo <= 0
&& (wp->w_p_rl ? col == 0 : col == grid->Columns - 1)
&& (*ptr != NUL
|| (wp->w_p_list && lcs_eol_one > 0)
|| (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
- c = lcs_ext;
+ c = wp->w_p_lcs_chars.ext;
char_attr = win_hl_attr(wp, HLF_AT);
mb_c = c;
if (enc_utf8 && utf_char2len(c) > 1) {
@@ -4164,7 +4195,8 @@ win_line (
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
&& (*ptr != NUL
|| filler_todo > 0
- || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
+ || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
+ && p_extra != at_end_str)
|| (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
) {
bool wrap = wp->w_p_wrap // Wrapping enabled.
@@ -4172,7 +4204,7 @@ win_line (
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character.
&& row != endrow - 1 // Not the last line being displayed.
&& (grid->Columns == Columns // Window spans the width of the screen,
- || ui_is_external(kUIMultigrid)) // or has dedicated grid.
+ || ui_has(kUIMultigrid)) // or has dedicated grid.
&& !wp->w_p_rl; // Not right-to-left.
grid_put_linebuf(grid, row, 0, col - boguscols, grid->Columns, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
@@ -4224,16 +4256,19 @@ win_line (
saved_n_extra = n_extra;
saved_p_extra = p_extra;
saved_c_extra = c_extra;
+ saved_c_final = c_final;
saved_char_attr = char_attr;
n_extra = 0;
- lcs_prec_todo = lcs_prec;
- if (filler_todo <= 0)
- need_showbreak = TRUE;
- --filler_todo;
- /* When the filler lines are actually below the last line of the
- * file, don't draw the line itself, break here. */
- if (filler_todo == 0 && wp->w_botfill)
+ lcs_prec_todo = wp->w_p_lcs_chars.prec;
+ if (filler_todo <= 0) {
+ need_showbreak = true;
+ }
+ filler_todo--;
+ // When the filler lines are actually below the last line of the
+ // file, don't draw the line itself, break here.
+ if (filler_todo == 0 && wp->w_botfill) {
break;
+ }
}
} /* for every character in the line */
@@ -4257,7 +4292,7 @@ win_line (
/// screen positions.
static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
{
- if (!ui_is_external(kUIMultigrid) && *grid != &default_grid) {
+ if (!(*grid)->chars && *grid != &default_grid) {
*row_off += (*grid)->row_offset;
*col_off += (*grid)->col_offset;
*grid = &default_grid;
@@ -4507,7 +4542,7 @@ void redraw_statuslines(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_status) {
- win_redr_status(wp, false);
+ win_redr_status(wp);
}
}
if (redraw_tabline)
@@ -4629,8 +4664,7 @@ win_redr_status_matches (
if (matches == NULL) /* interrupted completion? */
return;
- buf = xmalloc(has_mbyte ? default_grid.Columns * MB_MAXBYTES + 1
- : default_grid.Columns + 1);
+ buf = xmalloc(Columns * MB_MAXBYTES + 1);
if (match == -1) { /* don't show match but original text */
match = 0;
@@ -4651,13 +4685,13 @@ win_redr_status_matches (
if (first_match > 0)
clen += 2;
// jumping right, put match at the left
- if ((long)clen > default_grid.Columns) {
+ if ((long)clen > Columns) {
first_match = match;
/* if showing the last match, we can add some on the left */
clen = 2;
for (i = match; i < num_matches; ++i) {
clen += status_match_len(xp, L_MATCH(i)) + 2;
- if ((long)clen >= default_grid.Columns) {
+ if ((long)clen >= Columns) {
break;
}
}
@@ -4668,7 +4702,7 @@ win_redr_status_matches (
if (add_left)
while (first_match > 0) {
clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2;
- if ((long)clen >= default_grid.Columns) {
+ if ((long)clen >= Columns) {
break;
}
first_match--;
@@ -4686,8 +4720,7 @@ win_redr_status_matches (
clen = len;
i = first_match;
- while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2)
- < default_grid.Columns) {
+ while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -4738,7 +4771,7 @@ win_redr_status_matches (
if (msg_scrolled > 0) {
/* Put the wildmenu just above the command line. If there is
* no room, scroll the screen one line up. */
- if (cmdline_row == default_grid.Rows - 1) {
+ if (cmdline_row == Rows - 1) {
grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns);
msg_scrolled++;
} else {
@@ -4767,7 +4800,7 @@ win_redr_status_matches (
grid_puts(&default_grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
}
- grid_fill(&default_grid, row, row + 1, clen, (int)default_grid.Columns,
+ grid_fill(&default_grid, row, row + 1, clen, (int)Columns,
fillchar, fillchar, attr);
}
@@ -4778,9 +4811,7 @@ win_redr_status_matches (
/// Redraw the status line of window `wp`.
///
/// If inversion is possible we use it. Else '=' characters are used.
-/// If "ignore_pum" is true, also redraw statusline when the popup menu is
-/// displayed.
-static void win_redr_status(win_T *wp, int ignore_pum)
+static void win_redr_status(win_T *wp)
{
int row;
char_u *p;
@@ -4794,7 +4825,7 @@ static void win_redr_status(win_T *wp, int ignore_pum)
// invokes ":redrawstatus". Simply ignore the call then.
if (busy
// Also ignore if wildmenu is showing.
- || (wild_menu_showing != 0 && !ui_is_external(kUIWildmenu))) {
+ || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
return;
}
busy = true;
@@ -4803,7 +4834,7 @@ static void win_redr_status(win_T *wp, int ignore_pum)
if (wp->w_status_height == 0) {
// no status line, can only be last window
redraw_cmdline = true;
- } else if (!redrawing() || (!ignore_pum && pum_drawn())) {
+ } else if (!redrawing()) {
// Don't redraw right now, do it later. Don't update status line when
// popup menu is visible and may be drawn over it
wp->w_redr_status = true;
@@ -5033,7 +5064,7 @@ win_redr_custom (
row = 0;
fillchar = ' ';
attr = HL_ATTR(HLF_TPF);
- maxwidth = default_grid.Columns;
+ maxwidth = Columns;
use_sandbox = was_set_insecurely((char_u *)"tabline", 0);
} else {
row = W_ENDROW(wp);
@@ -5052,13 +5083,13 @@ win_redr_custom (
if (*stl++ != '(')
stl = p_ruf;
}
- col = ru_col - (default_grid.Columns - wp->w_width);
+ col = ru_col - (Columns - wp->w_width);
if (col < (wp->w_width + 1) / 2) {
col = (wp->w_width + 1) / 2;
}
maxwidth = wp->w_width - col;
if (!wp->w_status_height) {
- row = default_grid.Rows - 1;
+ row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling
fillchar = ' ';
attr = 0;
@@ -5152,7 +5183,7 @@ win_redr_custom (
p = (char_u *) tabtab[n].start;
cur_click_def = tabtab[n].def;
}
- while (col < default_grid.Columns) {
+ while (col < Columns) {
tab_page_click_defs[col++] = cur_click_def;
}
}
@@ -5282,7 +5313,7 @@ void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
}
static int put_dirty_row = -1;
-static int put_dirty_first = -1;
+static int put_dirty_first = INT_MAX;
static int put_dirty_last = 0;
/// Start a group of screen_puts_len calls that builds a single screen line.
@@ -5313,8 +5344,6 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
int prev_c = 0; /* previous Arabic character */
int pc, nc, nc1;
int pcc[MAX_MCO];
- int force_redraw_this;
- int force_redraw_next = FALSE;
int need_redraw;
bool do_flush = false;
@@ -5337,16 +5366,10 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
/* When drawing over the right halve of a double-wide char clear out the
* left halve. Only needed in a terminal. */
- if (col > 0 && col < grid->Columns && grid_fix_col(grid, col, row) != col) {
- schar_from_ascii(grid->chars[off - 1], ' ');
- grid->attrs[off - 1] = 0;
+ if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
// redraw the previous cell, make it empty
- if (put_dirty_first == -1) {
- put_dirty_first = col-1;
- }
- put_dirty_last = col+1;
- // force the cell at "col" to be redrawn
- force_redraw_next = true;
+ put_dirty_first = -1;
+ put_dirty_last = MAX(put_dirty_last, 1);
}
max_off = grid->line_offset[row] + grid->Columns;
@@ -5394,15 +5417,12 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
schar_from_cc(buf, u8c, u8cc);
- force_redraw_this = force_redraw_next;
- force_redraw_next = FALSE;
-
need_redraw = schar_cmp(grid->chars[off], buf)
|| (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
|| grid->attrs[off] != attr
|| exmode_active;
- if (need_redraw || force_redraw_this) {
+ if (need_redraw) {
// When at the end of the text and overwriting a two-cell
// character with a one-cell character, need to clear the next
// cell. Also when overwriting the left halve of a two-cell char
@@ -5426,10 +5446,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
grid->chars[off + 1][0] = 0;
grid->attrs[off + 1] = attr;
}
- if (put_dirty_first == -1) {
- put_dirty_first = col;
- }
- put_dirty_last = col+mbyte_cells;
+ put_dirty_first = MIN(put_dirty_first, col);
+ put_dirty_last = MAX(put_dirty_last, col+mbyte_cells);
}
off += mbyte_cells;
@@ -5457,14 +5475,14 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
void grid_puts_line_flush(ScreenGrid *grid, bool set_cursor)
{
assert(put_dirty_row != -1);
- if (put_dirty_first != -1) {
+ if (put_dirty_first < put_dirty_last) {
if (set_cursor) {
ui_grid_cursor_goto(grid->handle, put_dirty_row,
MIN(put_dirty_last, grid->Columns-1));
}
ui_line(grid, put_dirty_row, put_dirty_first, put_dirty_last,
put_dirty_last, 0, false);
- put_dirty_first = -1;
+ put_dirty_first = INT_MAX;
put_dirty_last = 0;
}
put_dirty_row = -1;
@@ -5790,7 +5808,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
}
// nothing to do
- if (grid->chars == NULL || start_row >= end_row || start_col >= end_col) {
+ if (start_row >= end_row || start_col >= end_col) {
return;
}
@@ -5836,9 +5854,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
if (dirty_last > dirty_first) {
// TODO(bfredl): support a cleared suffix even with a batched line?
if (put_dirty_row == row) {
- if (put_dirty_first == -1) {
- put_dirty_first = dirty_first;
- }
+ put_dirty_first = MIN(put_dirty_first, dirty_first);
put_dirty_last = MAX(put_dirty_last, dirty_last);
} else {
int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
@@ -5851,7 +5867,8 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
}
// TODO(bfredl): The relevant caller should do this
- if (row == default_grid.Rows - 1) { // overwritten the command line
+ if (row == Rows - 1 && !ui_has(kUIMessages)) {
+ // overwritten the command line
redraw_cmdline = true;
if (start_col == 0 && end_col == Columns
&& c1 == ' ' && c2 == ' ' && attr == 0) {
@@ -5881,18 +5898,6 @@ void check_for_delay(int check_msg_scroll)
}
}
-/*
- * screen_valid - allocate screen buffers if size changed
- * If "doclear" is TRUE: clear screen if it has been resized.
- * Returns TRUE if there is a valid screen to write to.
- * Returns FALSE when starting up and screen not initialized yet.
- */
-int screen_valid(int doclear)
-{
- screenalloc(doclear); // allocate screen buffers if size changed
- return default_grid.chars != NULL;
-}
-
/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
/// Updates size, offsets and handle for the grid regardless.
///
@@ -5902,38 +5907,35 @@ void win_grid_alloc(win_T *wp)
{
ScreenGrid *grid = &wp->w_grid;
- int rows = grid->requested_rows;
- if (rows == 0) {
- rows = wp->w_height;
- }
-
- int columns = grid->requested_cols;
- if (columns == 0) {
- columns = wp->w_width;
- }
+ int rows = wp->w_height_inner;
+ int cols = wp->w_width_inner;
// TODO(bfredl): floating windows should force this to true
- bool want_allocation = ui_is_external(kUIMultigrid);
+ bool want_allocation = ui_has(kUIMultigrid);
bool has_allocation = (grid->chars != NULL);
if (want_allocation && has_allocation && highlights_invalid) {
grid_invalidate(grid);
}
+ if (grid->Rows != rows) {
+ wp->w_lines_valid = 0;
+ xfree(wp->w_lines);
+ wp->w_lines = xcalloc(rows+1, sizeof(wline_T));
+ }
+
int was_resized = false;
if ((has_allocation != want_allocation)
|| grid->Rows != rows
- || grid->Columns != columns) {
+ || grid->Columns != cols) {
if (want_allocation) {
- grid_alloc(grid, rows, columns, true);
- win_free_lsize(wp);
- win_alloc_lines(wp);
+ grid_alloc(grid, rows, cols, true, true);
} else {
// Single grid mode, all rendering will be redirected to default_grid.
// Only keep track of the size and offset of the window.
grid_free(grid);
grid->Rows = rows;
- grid->Columns = columns;
+ grid->Columns = cols;
}
was_resized = true;
}
@@ -5945,7 +5947,7 @@ void win_grid_alloc(win_T *wp)
// - a grid was just resized
// - screen_resize was called and all grid sizes must be sent
// - the UI wants multigrid event (necessary)
- if ((send_grid_resize || was_resized) && ui_is_external(kUIMultigrid)) {
+ if ((send_grid_resize || was_resized) && ui_has(kUIMultigrid)) {
ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows);
}
}
@@ -5971,7 +5973,7 @@ void grid_assign_handle(ScreenGrid *grid)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the shell is
/// needed.
-void screenalloc(bool doclear)
+void screenalloc(void)
{
static bool entered = false; // avoid recursiveness
int retry_count = 0;
@@ -6005,6 +6007,10 @@ retry:
*/
++RedrawingDisabled;
+ // win_new_shellsize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_invalidate_screen();
+
win_new_shellsize(); /* fit the windows in the new sized shell */
comp_col(); /* recompute columns for shown command and ruler */
@@ -6018,37 +6024,22 @@ retry:
// If anything fails, make grid arrays NULL, so we don't do anything!
// Continuing with the old arrays may result in a crash, because the
// size is wrong.
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- win_free_lsize(wp);
- }
- if (aucmd_win != NULL)
- win_free_lsize(aucmd_win);
- grid_alloc(&default_grid, Rows, Columns, !doclear);
+ grid_alloc(&default_grid, Rows, Columns, true, true);
StlClickDefinition *new_tab_page_click_defs = xcalloc(
(size_t)Columns, sizeof(*new_tab_page_click_defs));
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- win_alloc_lines(wp);
- }
- if (aucmd_win != NULL && aucmd_win->w_lines == NULL) {
- win_alloc_lines(aucmd_win);
- }
-
clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
xfree(tab_page_click_defs);
tab_page_click_defs = new_tab_page_click_defs;
- tab_page_click_defs_size = default_grid.Columns;
+ tab_page_click_defs_size = Columns;
default_grid.row_offset = 0;
default_grid.col_offset = 0;
default_grid.handle = DEFAULT_GRID_HANDLE;
- must_redraw = CLEAR; /* need to clear the screen later */
- if (doclear)
- screenclear2();
-
+ must_redraw = CLEAR; // need to clear the screen later
entered = FALSE;
--RedrawingDisabled;
@@ -6065,7 +6056,7 @@ retry:
}
}
-void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
+void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
{
int new_row;
ScreenGrid new = *grid;
@@ -6083,7 +6074,7 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
new.line_offset[new_row] = new_row * new.Columns;
new.line_wraps[new_row] = false;
- grid_clear_line(&new, new.line_offset[new_row], columns, true);
+ grid_clear_line(&new, new.line_offset[new_row], columns, valid);
if (copy) {
// If the screen is not going to be cleared, copy as much as
@@ -6155,13 +6146,9 @@ void clear_tab_page_click_defs(StlClickDefinition *const tpcd,
void screenclear(void)
{
- check_for_delay(FALSE);
- screenalloc(false); /* allocate screen buffers if size changed */
- screenclear2(); /* clear the screen */
-}
+ check_for_delay(false);
+ screenalloc(); // allocate screen buffers if size changed
-static void screenclear2(void)
-{
int i;
if (starting == NO_SCREEN || default_grid.chars == NULL) {
@@ -6182,6 +6169,8 @@ static void screenclear2(void)
redraw_all_later(NOT_VALID);
redraw_cmdline = true;
redraw_tabline = true;
+ redraw_popupmenu = true;
+ pum_invalidate();
if (must_redraw == CLEAR) {
must_redraw = NOT_VALID; // no need to clear again
}
@@ -6205,11 +6194,17 @@ static void grid_clear_line(ScreenGrid *grid, unsigned off, int width,
(void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
}
-static void grid_invalidate(ScreenGrid *grid)
+void grid_invalidate(ScreenGrid *grid)
{
(void)memset(grid->attrs, -1, grid->Rows * grid->Columns * sizeof(sattr_T));
}
+bool grid_invalid_row(ScreenGrid *grid, int row)
+{
+ return grid->attrs[grid->line_offset[row]] < 0;
+}
+
+
/// Copy part of a grid line for vertically split window.
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
@@ -6237,7 +6232,7 @@ void setcursor(void)
if (curwin->w_p_rl) {
// With 'rightleft' set and the cursor on a double-wide character,
// position it on the leftmost column.
- col = curwin->w_grid.Columns - curwin->w_wcol
+ col = curwin->w_width_inner - curwin->w_wcol
- ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
&& vim_isprintc(gchar_cursor())) ? 2 : 1);
}
@@ -6247,42 +6242,28 @@ void setcursor(void)
}
}
-/// Insert 'line_count' lines at 'row' in window 'wp'.
-/// Returns FAIL if the lines are not inserted, OK for success.
-int win_ins_lines(win_T *wp, int row, int line_count)
-{
- return win_do_lines(wp, row, line_count, false);
-}
-
-/// Delete "line_count" window lines at "row" in window "wp".
-/// Return OK for success, FAIL if the lines are not deleted.
-int win_del_lines(win_T *wp, int row, int line_count)
-{
- return win_do_lines(wp, row, line_count, true);
-}
-
-// Common code for win_ins_lines() and win_del_lines().
-// Returns OK or FAIL when the work has been done.
-static int win_do_lines(win_T *wp, int row, int line_count, int del)
+/// Scroll 'line_count' lines at 'row' in window 'wp'.
+///
+/// Positive `line_count' means scrolling down, so that more space is available
+/// at 'row'. Negative `line_count` implies deleting lines at `row`.
+void win_scroll_lines(win_T *wp, int row, int line_count)
{
- if (!redrawing() || line_count <= 0) {
- return FAIL;
+ if (!redrawing() || line_count == 0) {
+ return;
}
// No lines are being moved, just draw over the entire area
- if (row + line_count >= wp->w_grid.Rows) {
- return OK;
+ if (row + abs(line_count) >= wp->w_grid.Rows) {
+ return;
}
- int retval;
- if (del) {
- retval = grid_del_lines(&wp->w_grid, row, line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ if (line_count < 0) {
+ grid_del_lines(&wp->w_grid, row, -line_count,
+ wp->w_grid.Rows, 0, wp->w_grid.Columns);
} else {
- retval = grid_ins_lines(&wp->w_grid, row, line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ grid_ins_lines(&wp->w_grid, row, line_count,
+ wp->w_grid.Rows, 0, wp->w_grid.Columns);
}
- return retval;
}
/*
@@ -6302,10 +6283,8 @@ static int win_do_lines(win_T *wp, int row, int line_count, int del)
/// 'col' is the column from with we start inserting.
//
/// 'row', 'col' and 'end' are relative to the start of the region.
-///
-/// @return FAIL for failure, OK for success.
-int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
- int width)
+void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
+ int width)
{
int i;
int j;
@@ -6316,8 +6295,8 @@ int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
row += row_off;
end += row_off;
- if (!screen_valid(TRUE) || line_count <= 0) {
- return FAIL;
+ if (line_count <= 0) {
+ return;
}
// Shift line_offset[] line_count down to reflect the inserted lines.
@@ -6347,17 +6326,15 @@ int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0);
- return OK;
+ return;
}
/// delete lines on the screen and move lines up.
/// 'end' is the line after the scrolled part. Normally it is Rows.
/// When scrolling region used 'off' is the offset from the top for the region.
/// 'row' and 'end' are relative to the start of the region.
-///
-/// Return OK for success, FAIL if the lines are not deleted.
-int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
- int width)
+void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
+ int width)
{
int j;
int i;
@@ -6368,8 +6345,8 @@ int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
row += row_off;
end += row_off;
- if (!screen_valid(TRUE) || line_count <= 0) {
- return FAIL;
+ if (line_count <= 0) {
+ return;
}
// Now shift line_offset[] line_count up to reflect the deleted lines.
@@ -6400,7 +6377,7 @@ int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0);
- return OK;
+ return;
}
@@ -6421,6 +6398,13 @@ int showmode(void)
int nwr_save;
int sub_attr;
+ if (ui_has(kUIMessages) && clear_cmdline) {
+ msg_ext_clear(true);
+ }
+
+ // don't make non-flushed message part of the showmode
+ msg_ext_ui_flush();
+
do_mode = ((p_smd && msg_silent == 0)
&& ((State & TERM_FOCUS)
|| (State & INSERT)
@@ -6445,7 +6429,7 @@ int showmode(void)
/* if the cmdline is more than one line high, erase top lines */
need_clear = clear_cmdline;
- if (clear_cmdline && cmdline_row < default_grid.Rows - 1) {
+ if (clear_cmdline && cmdline_row < Rows - 1) {
msg_clr_cmdline(); // will reset clear_cmdline
}
@@ -6463,9 +6447,14 @@ int showmode(void)
MSG_PUTS_ATTR("--", attr);
// CTRL-X in Insert mode
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
- /* These messages can get long, avoid a wrap in a narrow
- * window. Prefer showing edit_submode_extra. */
- length = (default_grid.Rows - msg_row) * default_grid.Columns - 3;
+ // These messages can get long, avoid a wrap in a narrow window.
+ // Prefer showing edit_submode_extra. With external messages there
+ // is no imposed limit.
+ if (ui_has(kUIMessages)) {
+ length = INT_MAX;
+ } else {
+ length = (Rows - msg_row) * Columns - 3;
+ }
if (edit_submode_extra != NULL) {
length -= vim_strsize(edit_submode_extra);
}
@@ -6567,6 +6556,9 @@ int showmode(void)
msg_clr_cmdline();
}
+ // NB: also handles clearing the showmode if it was emtpy or disabled
+ msg_ext_flush_showmode();
+
/* In Visual mode the size of the selected area must be redrawn. */
if (VIsual_active)
clear_showcmd();
@@ -6589,7 +6581,7 @@ int showmode(void)
static void msg_pos_mode(void)
{
msg_col = 0;
- msg_row = default_grid.Rows - 1;
+ msg_row = Rows - 1;
}
/// Delete mode message. Used when ESC is typed which is expected to end
@@ -6608,11 +6600,13 @@ void unshowmode(bool force)
// Clear the mode message.
void clearmode(void)
{
- msg_pos_mode();
- if (Recording) {
- recording_mode(HL_ATTR(HLF_CM));
- }
- msg_clr_eos();
+ msg_ext_ui_flush();
+ msg_pos_mode();
+ if (Recording) {
+ recording_mode(HL_ATTR(HLF_CM));
+ }
+ msg_clr_eos();
+ msg_ext_flush_showmode();
}
static void recording_mode(int attr)
@@ -6653,7 +6647,7 @@ static void draw_tabline(void)
}
redraw_tabline = false;
- if (ui_is_external(kUITabline)) {
+ if (ui_has(kUITabline)) {
ui_ext_tabline_update();
return;
}
@@ -6663,7 +6657,7 @@ static void draw_tabline(void)
// Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
- assert(default_grid.Columns == tab_page_click_defs_size);
+ assert(Columns == tab_page_click_defs_size);
clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
/* Use the 'tabline' option if it's set. */
@@ -6685,7 +6679,7 @@ static void draw_tabline(void)
}
if (tabcount > 0) {
- tabwidth = (default_grid.Columns - 1 + tabcount / 2) / tabcount;
+ tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
}
if (tabwidth < 6) {
@@ -6696,7 +6690,7 @@ static void draw_tabline(void)
tabcount = 0;
FOR_ALL_TABS(tp) {
- if (col >= default_grid.Columns - 4) {
+ if (col >= Columns - 4) {
break;
}
@@ -6737,7 +6731,7 @@ static void draw_tabline(void)
if (wincount > 1) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
len = (int)STRLEN(NameBuff);
- if (col + len >= default_grid.Columns - 3) {
+ if (col + len >= Columns - 3) {
break;
}
grid_puts_len(&default_grid, NameBuff, len, 0, col,
@@ -6766,8 +6760,8 @@ static void draw_tabline(void)
p += len - room;
len = room;
}
- if (len > default_grid.Columns - col - 1) {
- len = default_grid.Columns - col - 1;
+ if (len > Columns - col - 1) {
+ len = Columns - col - 1;
}
grid_puts_len(&default_grid, p, (int)STRLEN(p), 0, col, attr);
@@ -6791,14 +6785,14 @@ static void draw_tabline(void)
c = '_';
else
c = ' ';
- grid_fill(&default_grid, 0, 1, col, (int)default_grid.Columns, c, c,
+ grid_fill(&default_grid, 0, 1, col, (int)Columns, c, c,
attr_fill);
/* Put an "X" for closing the current tab if there are several. */
if (first_tabpage->tp_next != NULL) {
- grid_putchar(&default_grid, 'X', 0, (int)default_grid.Columns - 1,
+ grid_putchar(&default_grid, 'X', 0, (int)Columns - 1,
attr_nosel);
- tab_page_click_defs[default_grid.Columns - 1] = (StlClickDefinition) {
+ tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
.type = kStlClickTabClose,
.tabnr = 999,
.func = NULL,
@@ -6849,17 +6843,17 @@ static int fillchar_status(int *attr, win_T *wp)
bool is_curwin = (wp == curwin);
if (is_curwin) {
*attr = win_hl_attr(wp, HLF_S);
- fill = fill_stl;
+ fill = wp->w_p_fcs_chars.stl;
} else {
*attr = win_hl_attr(wp, HLF_SNC);
- fill = fill_stlnc;
+ fill = wp->w_p_fcs_chars.stlnc;
}
/* Use fill when there is highlighting, and highlighting of current
* window differs, or the fillchars differ, or this is not the
* current window */
if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
|| !is_curwin || ONE_WINDOW)
- || (fill_stl != fill_stlnc))) {
+ || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
return fill;
}
if (is_curwin) {
@@ -6875,7 +6869,7 @@ static int fillchar_status(int *attr, win_T *wp)
static int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
- return fill_vert;
+ return wp->w_p_fcs_chars.vert;
}
/*
@@ -6903,11 +6897,6 @@ void showruler(int always)
{
if (!always && !redrawing())
return;
- if (pum_drawn()) {
- // Don't redraw right now, do it later.
- curwin->w_redr_status = true;
- return;
- }
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
redraw_custom_statusline(curwin);
} else {
@@ -6926,9 +6915,12 @@ void showruler(int always)
static void win_redr_ruler(win_T *wp, int always)
{
- /* If 'ruler' off or redrawing disabled, don't do anything */
- if (!p_ru)
+ static bool did_show_ext_ruler = false;
+
+ // If 'ruler' off or redrawing disabled, don't do anything
+ if (!p_ru) {
return;
+ }
/*
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
@@ -6942,10 +6934,6 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp == lastwin && lastwin->w_status_height == 0)
if (edit_submode != NULL)
return;
- // Don't draw the ruler when the popup menu is visible, it may overlap.
- if (pum_drawn()) {
- return;
- }
if (*p_ruf) {
int save_called_emsg = called_emsg;
@@ -6987,26 +6975,28 @@ static void win_redr_ruler(win_T *wp, int always)
int fillchar;
int attr;
int off;
+ bool part_of_status = false;
if (wp->w_status_height) {
row = W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
off = wp->w_wincol;
width = wp->w_width;
+ part_of_status = true;
} else {
- row = default_grid.Rows - 1;
+ row = Rows - 1;
fillchar = ' ';
attr = 0;
- width = default_grid.Columns;
+ width = Columns;
off = 0;
}
- /* In list mode virtcol needs to be recomputed */
+ // In list mode virtcol needs to be recomputed
colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL) {
- wp->w_p_list = FALSE;
+ if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
+ wp->w_p_list = false;
getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = TRUE;
+ wp->w_p_list = true;
}
#define RULER_BUF_LEN 70
@@ -7035,7 +7025,7 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp->w_status_height == 0) { // can't use last char of screen
o++;
}
- int this_ru_col = ru_col - (default_grid.Columns - width);
+ int this_ru_col = ru_col - (Columns - width);
if (this_ru_col < 0) {
this_ru_col = 0;
}
@@ -7052,23 +7042,39 @@ static void win_redr_ruler(win_T *wp, int always)
}
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
}
- // Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
- break;
+
+ if (ui_has(kUIMessages) && !part_of_status) {
+ Array content = ARRAY_DICT_INIT;
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, INTEGER_OBJ(attr));
+ ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer)));
+ ADD(content, ARRAY_OBJ(chunk));
+ ui_call_msg_ruler(content);
+ did_show_ext_ruler = true;
+ } else {
+ if (did_show_ext_ruler) {
+ ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
+ did_show_ext_ruler = false;
+ }
+ // Truncate at window boundary.
+ o = 0;
+ for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
+ o += utf_ptr2cells(buffer + i);
+ if (this_ru_col + o > width) {
+ buffer[i] = NUL;
+ break;
+ }
}
+
+ grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
+ i = redraw_cmdline;
+ grid_fill(&default_grid, row, row + 1,
+ this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
+ fillchar, attr);
+ // don't redraw the cmdline because of showing the ruler
+ redraw_cmdline = i;
}
- grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
- i = redraw_cmdline;
- grid_fill(&default_grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
- fillchar, attr);
- // don't redraw the cmdline because of showing the ruler
- redraw_cmdline = i;
wp->w_ru_cursor = wp->w_cursor;
wp->w_ru_virtcol = wp->w_virtcol;
wp->w_ru_empty = empty_line;
@@ -7090,7 +7096,7 @@ int number_width(win_T *wp)
if (wp->w_p_rnu && !wp->w_p_nu) {
// cursor line shows "0"
- lnum = wp->w_grid.Rows;
+ lnum = wp->w_height_inner;
} else {
// cursor line shows absolute line number
lnum = wp->w_buffer->b_ml.ml_line_count;
@@ -7177,7 +7183,7 @@ void screen_resize(int width, int height)
*/
if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM
|| exmode_active) {
- screenalloc(false);
+ screenalloc();
repeat_message();
} else {
if (curwin->w_p_scb)
@@ -7188,7 +7194,10 @@ void screen_resize(int width, int height)
} else {
update_topline();
if (pum_drawn()) {
- redraw_later(NOT_VALID);
+ // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
+ // For now make sure the nested update_screen(0) won't redraw the
+ // pum at the old position. Try to untangle this later.
+ redraw_popupmenu = false;
ins_compl_show_pum();
}
update_screen(NOT_VALID);
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 109541ef07..61ed98247d 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -28,8 +28,7 @@
///
/// Note: before the screen is initialized and when out of memory these can be
/// NULL.
-EXTERN ScreenGrid default_grid INIT(= { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
- 0, 0, 0 });
+EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
diff --git a/src/nvim/search.c b/src/nvim/search.c
index cf0f1ea287..5f0ff96f95 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -2168,7 +2168,7 @@ showmatch(
}
if (curwin->w_p_wrap
|| (vcol >= curwin->w_leftcol
- && vcol < curwin->w_leftcol + curwin->w_grid.Columns)) {
+ && vcol < curwin->w_leftcol + curwin->w_width_inner)) {
mpos = *lpos; // save the pos, update_screen() may change it
save_cursor = curwin->w_cursor;
save_so = p_so;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 4921824316..96a8dfd295 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -27,6 +27,7 @@
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/mark.h"
+#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#include "nvim/memline.h"
@@ -50,16 +51,7 @@
#include "nvim/os/shell.h"
#include "nvim/eval/encode.h"
-#ifdef __MINGW32__
-# undef fpclassify
-# define fpclassify __fpclassify
-# undef isnan
-# define isnan _isnan
-#endif
-
-/*
- * Copy "string" into newly allocated memory.
- */
+/// Copy "string" into newly allocated memory.
char_u *vim_strsave(const char_u *string)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
@@ -1214,14 +1206,14 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
remove_trailing_zeroes = true;
}
- if (isinf(f)
+ if (xisinf(f)
|| (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) {
xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec,
force_sign, space_for_positive),
sizeof(tmp));
str_arg_l = strlen(tmp);
zero_padding = 0;
- } else if (isnan(f)) {
+ } else if (xisnan(f)) {
// Not a number: nan or NAN
memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4);
str_arg_l = 3;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 81c78ca6a9..b6b7dfff11 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6893,7 +6893,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// "fg", which have been changed now.
highlight_attr_set_all();
- if (!ui_is_external(kUILinegrid) && starting == 0) {
+ if (!ui_has(kUILinegrid) && starting == 0) {
// Older UIs assume that we clear the screen after normal group is
// changed
ui_refresh();
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 51bf22b31c..8b4ad4d3af 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -236,7 +236,8 @@ Terminal *terminal_open(TerminalOptions opts)
// Default settings for terminal buffers
curbuf->b_p_ma = false; // 'nomodifiable'
curbuf->b_p_ul = -1; // 'undolevels'
- curbuf->b_p_scbk = p_scbk; // 'scrollback'
+ curbuf->b_p_scbk = // 'scrollback' (initialize local from global)
+ (p_scbk < 0) ? 10000 : MAX(1, p_scbk);
curbuf->b_p_tw = 0; // 'textwidth'
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
@@ -244,13 +245,13 @@ Terminal *terminal_open(TerminalOptions opts)
RESET_BINDING(curwin);
// Reset cursor in current window.
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
-
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
+ // Local 'scrollback' _after_ autocmds.
+ curbuf->b_p_scbk = (curbuf->b_p_scbk < 1) ? SB_MAX : curbuf->b_p_scbk;
// Configure the scrollback buffer.
- rv->sb_size = curbuf->b_p_scbk < 0
- ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk);
+ rv->sb_size = (size_t)curbuf->b_p_scbk;
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
if (!true_color) {
@@ -334,41 +335,32 @@ void terminal_close(Terminal *term, char *msg)
}
}
-void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
+void terminal_check_size(Terminal *term)
{
if (term->closed) {
- // If two windows display the same terminal and one is closed by keypress.
return;
}
- bool force = width == UINT16_MAX || height == UINT16_MAX;
+
int curwidth, curheight;
vterm_get_size(term->vt, &curheight, &curwidth);
+ uint16_t width = 0, height = 0;
- if (force || !width) {
- width = (uint16_t)curwidth;
- }
-
- if (force || !height) {
- height = (uint16_t)curheight;
- }
-
- if (!force && curheight == height && curwidth == width) {
- return;
- }
-
- if (height == 0 || width == 0) {
- return;
- }
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer && wp->w_buffer->terminal == term) {
const uint16_t win_width =
- (uint16_t)(MAX(0, wp->w_width - win_col_off(wp)));
+ (uint16_t)(MAX(0, wp->w_width_inner - win_col_off(wp)));
width = MAX(width, win_width);
- height = (uint16_t)MAX(height, wp->w_height);
+ height = (uint16_t)MAX(height, wp->w_height_inner);
}
}
+ // if no window displays the terminal, or such all windows are zero-height,
+ // don't resize the terminal.
+ if ((curheight == height && curwidth == width) || height == 0 || width == 0) {
+ return;
+ }
+
vterm_set_size(term->vt, height, width);
vterm_screen_flush_damage(term->vts);
term->pending_resize = true;
@@ -383,8 +375,10 @@ void terminal_enter(void)
memset(s, 0, sizeof(TerminalState));
s->term = buf->terminal;
- // Ensure the terminal is properly sized.
- terminal_resize(s->term, 0, 0);
+ // Ensure the terminal is properly sized. Ideally window size management
+ // code should always have resized the terminal already, but check here to
+ // be sure.
+ terminal_check_size(s->term);
int save_state = State;
s->save_rd = RedrawingDisabled;
@@ -981,8 +975,8 @@ static void mouse_action(Terminal *term, int button, int row, int col,
// terminal should lose focus
static bool send_mouse_event(Terminal *term, int c)
{
- int row = mouse_row, col = mouse_col;
- win_T *mouse_win = mouse_find_win(&row, &col);
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ win_T *mouse_win = mouse_find_win(&grid, &row, &col);
if (term->forward_mouse && mouse_win->w_buffer->terminal == term) {
// event in the terminal window and mouse events was enabled by the
@@ -1169,8 +1163,10 @@ static void refresh_size(Terminal *term, buf_T *buf)
/// Adjusts scrollback storage after 'scrollback' option changed.
static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
{
- const size_t scbk = curbuf->b_p_scbk < 0
- ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk);
+ if (buf->b_p_scbk < 1) { // Local 'scrollback' was set to -1.
+ buf->b_p_scbk = SB_MAX;
+ }
+ const size_t scbk = (size_t)buf->b_p_scbk;
assert(term->sb_current < SIZE_MAX);
if (term->sb_pending > 0) { // Pending rows must be processed first.
abort();
@@ -1318,8 +1314,6 @@ static void redraw(bool restore_cursor)
static void adjust_topline(Terminal *term, buf_T *buf, long added)
{
- int height, width;
- vterm_get_size(term->vt, &height, &width);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf) {
linenr_T ml_end = buf->b_ml.ml_line_count;
@@ -1328,7 +1322,7 @@ static void adjust_topline(Terminal *term, buf_T *buf, long added)
if (following || (wp == curwin && is_focused(term))) {
// "Follow" the terminal output
wp->w_cursor.lnum = ml_end;
- set_topline(wp, MAX(wp->w_cursor.lnum - height + 1, 1));
+ set_topline(wp, MAX(wp->w_cursor.lnum - wp->w_height_inner + 1, 1));
} else {
// Ensure valid cursor for each window displaying this terminal.
wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end);
diff --git a/src/nvim/testdir/load.vim b/src/nvim/testdir/load.vim
new file mode 100644
index 0000000000..2e01338dd0
--- /dev/null
+++ b/src/nvim/testdir/load.vim
@@ -0,0 +1,30 @@
+function! s:load_factor() abort
+ let timeout = 200
+ let times = []
+
+ for _ in range(5)
+ let g:val = 0
+ call timer_start(timeout, {-> nvim_set_var('val', 1)})
+ let start = reltime()
+ while 1
+ sleep 10m
+ if g:val == 1
+ let g:waited_in_ms = float2nr(reltimefloat(reltime(start)) * 1000)
+ break
+ endif
+ endwhile
+ call insert(times, g:waited_in_ms, 0)
+ endfor
+
+ let longest = max(times)
+ let factor = (longest + 50.0) / timeout
+
+ return factor
+endfunction
+
+" Compute load factor only once.
+let s:load_factor = s:load_factor()
+
+function! LoadAdjust(num) abort
+ return float2nr(ceil(a:num * s:load_factor))
+endfunction
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index c7c3b378cc..011433f19e 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -24,6 +24,9 @@ let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
let &packpath = &rtp
+" Avoid storing shell history.
+let $HISTFILE = ""
+
" Make sure $HOME does not get read or written.
let $HOME = expand(getcwd() . '/XfakeHOME')
if !isdirectory($HOME)
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 253d6750ed..f1fb8e67b9 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -18,6 +18,8 @@ func Test_vim_did_enter()
endfunc
if has('timers')
+ source load.vim
+
func ExitInsertMode(id)
call feedkeys("\<Esc>")
endfunc
@@ -29,7 +31,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
- call timer_start(100, 'ExitInsertMode')
+ call timer_start(LoadAdjust(100), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
au! CursorHoldI
@@ -40,7 +42,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
- call timer_start(100, 'ExitInsertMode')
+ call timer_start(LoadAdjust(100), 'ExitInsertMode')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index d9a89801ea..78e51ed836 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -1,25 +1,169 @@
-" Test for findfile()
-"
+" Test findfile() and finddir()
+
+let s:files = [ 'Xdir1/foo',
+ \ 'Xdir1/bar',
+ \ 'Xdir1/Xdir2/foo',
+ \ 'Xdir1/Xdir2/foobar',
+ \ 'Xdir1/Xdir2/Xdir3/bar',
+ \ 'Xdir1/Xdir2/Xdir3/barfoo' ]
+
+func CreateFiles()
+ call mkdir('Xdir1/Xdir2/Xdir3/Xdir2', 'p')
+ for f in s:files
+ call writefile([], f)
+ endfor
+endfunc
+
+func CleanFiles()
+ " Safer to delete each file even if it's more verbose
+ " than doing a recursive delete('Xdir1', 'rf').
+ for f in s:files
+ call delete(f)
+ endfor
+
+ call delete('Xdir1/Xdir2/Xdir3/Xdir2', 'd')
+ call delete('Xdir1/Xdir2/Xdir3', 'd')
+ call delete('Xdir1/Xdir2', 'd')
+ call delete('Xdir1', 'd')
+endfunc
+
+" Test findfile({name} [, {path} [, {count}]])
func Test_findfile()
- new
- let cwd=getcwd()
- cd ..
+ let save_path = &path
+ let save_shellslash = &shellslash
+ let save_dir = getcwd()
+ set shellslash
+ call CreateFiles()
+ cd Xdir1
+ e Xdir2/foo
+
+ " With ,, in path, findfile() searches in current directory.
+ set path=,,
+ call assert_equal('foo', findfile('foo'))
+ call assert_equal('bar', findfile('bar'))
+ call assert_equal('', findfile('foobar'))
+
+ " Directories should not be found (finddir() finds them).
+ call assert_equal('', findfile('Xdir2'))
+
+ " With . in 'path', findfile() searches relatively to current file.
+ set path=.
+ call assert_equal('Xdir2/foo', findfile('foo'))
+ call assert_equal('', findfile('bar'))
+ call assert_equal('Xdir2/foobar', findfile('foobar'))
+
+ " Empty {path} 2nd argument is the same as no 2nd argument.
+ call assert_equal('Xdir2/foo', findfile('foo', ''))
+ call assert_equal('', findfile('bar', ''))
+
+ " Test with *
+ call assert_equal('Xdir2/foo', findfile('foo', '*'))
+ call assert_equal('', findfile('bar', '*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '*/*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir2/*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir*/Xdir3'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '*2/*3'))
+
+ " Test with **
+ call assert_equal('bar', findfile('bar', '**'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '**/Xdir3'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir2/**'))
+
+ call assert_equal('Xdir2/Xdir3/barfoo', findfile('barfoo', '**2'))
+ call assert_equal('', findfile('barfoo', '**1'))
+ call assert_equal('Xdir2/foobar', findfile('foobar', '**1'))
+
+ " Test with {count} 3rd argument.
+ call assert_equal('bar', findfile('bar', '**', 0))
+ call assert_equal('bar', findfile('bar', '**', 1))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '**', 2))
+ call assert_equal('', findfile('bar', '**', 3))
+ call assert_equal(['bar', 'Xdir2/Xdir3/bar'], findfile('bar', '**', -1))
+
+ " Test upwards search.
+ cd Xdir2/Xdir3
+ call assert_equal('bar', findfile('bar', ';'))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', ';'))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', ';', 1))
+ call assert_match('.*/Xdir1/foo', findfile('foo', ';', 2))
+ call assert_match('.*/Xdir1/foo', findfile('foo', ';', 2))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', 'Xdir2;', 1))
+ call assert_equal('', findfile('foo', 'Xdir2;', 2))
+
+ " List l should have at least 2 values (possibly more if foo file
+ " happens to be found upwards above Xdir1).
+ let l = findfile('foo', ';', -1)
+ call assert_match('.*/Xdir1/Xdir2/foo', l[0])
+ call assert_match('.*/Xdir1/foo', l[1])
+
+ " Test upwards search with stop-directory.
+ cd Xdir2
+ let l = findfile('bar', ';' . save_dir . '/Xdir1/Xdir2/', -1)
+ call assert_equal(1, len(l))
+ call assert_match('.*/Xdir1/Xdir2/Xdir3/bar', l[0])
+
+ let l = findfile('bar', ';' . save_dir . '/Xdir1/', -1)
+ call assert_equal(2, len(l))
+ call assert_match('.*/Xdir1/Xdir2/Xdir3/bar', l[0])
+ call assert_match('.*/Xdir1/bar', l[1])
+
+ " Test combined downwards and upwards search from Xdir2/.
+ cd ../..
+ call assert_equal('Xdir3/bar', findfile('bar', '**;', 1))
+ call assert_match('.*/Xdir1/bar', findfile('bar', '**;', 2))
+
+ bwipe!
+ exe 'cd ' . save_dir
+ call CleanFiles()
+ let &path = save_path
+ let &shellslash = save_shellslash
+endfunc
+
+" Test finddir({name} [, {path} [, {count}]])
+func Test_finddir()
+ let save_path = &path
+ let save_shellslash = &shellslash
+ let save_dir = getcwd()
+ set path=,,
+ call CreateFiles()
+ cd Xdir1
+
+ call assert_equal('Xdir2', finddir('Xdir2'))
+ call assert_equal('', finddir('Xdir3'))
+
+ " Files should not be found (findfile() finds them).
+ call assert_equal('', finddir('foo'))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**'))
+ call assert_equal('Xdir2/Xdir3', finddir('Xdir3', '**'))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**', 1))
+ call assert_equal('Xdir2/Xdir3/Xdir2', finddir('Xdir2', '**', 2))
+ call assert_equal(['Xdir2',
+ \ 'Xdir2/Xdir3/Xdir2'], finddir('Xdir2', '**', -1))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**1'))
+ call assert_equal('Xdir2', finddir('Xdir2', '**0'))
+ call assert_equal('Xdir2/Xdir3', finddir('Xdir3', '**1'))
+ call assert_equal('', finddir('Xdir3', '**0'))
+
+ " Test upwards dir search.
+ cd Xdir2/Xdir3
+ call assert_match('.*/Xdir1', finddir('Xdir1', ';'))
+
+ " Test upwards search with stop-directory.
+ call assert_match('.*/Xdir1', finddir('Xdir1', ';' . save_dir . '/'))
+ call assert_equal('', finddir('Xdir1', ';' . save_dir . '/Xdir1/'))
- " Tests may be run from a shadow directory, so an extra cd needs to be done to
- " get above src/
- if fnamemodify(getcwd(), ':t') != 'src'
- cd ../..
- else
- cd ..
- endif
- set ssl
-
- call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*'))
- exe "cd" cwd
+ " Test combined downwards and upwards dir search from Xdir2/.
cd ..
- call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*'))
- call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir'))
+ call assert_match('.*/Xdir1', finddir('Xdir1', '**;', 1))
+ call assert_equal('Xdir3/Xdir2', finddir('Xdir2', '**;', 1))
+ call assert_match('.*/Xdir1/Xdir2', finddir('Xdir2', '**;', 2))
+ call assert_equal('Xdir3', finddir('Xdir3', '**;', 1))
- exe "cd" cwd
- q!
+ exe 'cd ' . save_dir
+ call CleanFiles()
+ let &path = save_path
+ let &shellslash = save_shellslash
endfunc
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index c873487b92..a6494c531c 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -28,9 +28,9 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*quotestar\*')
helpclose
- help sp?it
+ help ch?ckhealth
call assert_equal("help", &filetype)
- call assert_true(getline('.') =~ '\*'.(has('win32') ? 'split()' : ':split').'\*')
+ call assert_true(getline('.') =~ '\*:checkhealth\*')
helpclose
help :?
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index 6e07c874b4..ada25da4a8 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -23,6 +23,8 @@ function! Test_lambda_with_timer()
return
endif
+ source load.vim
+
let s:n = 0
let s:timer_id = 0
function! s:Foo()
@@ -31,15 +33,19 @@ function! Test_lambda_with_timer()
endfunction
call s:Foo()
- sleep 210ms
+ sleep 210m
" do not collect lambda
call test_garbagecollect_now()
- let m = s:n
- sleep 230ms
+ let m = LoadAdjust(s:n)
+ sleep 230m
call timer_stop(s:timer_id)
+
+ let n = LoadAdjust(s:n)
+ let nine = LoadAdjust(9)
+
call assert_true(m > 1)
- call assert_true(s:n > m + 1)
- call assert_true(s:n < 9)
+ call assert_true(n > m + 1)
+ call assert_true(n < nine)
endfunction
function! Test_lambda_with_partial()
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 57ea7ca5a9..4899f59910 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -42,6 +42,38 @@ func Test_listchars()
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
endfor
+ " tab with 3rd character.
+ set listchars-=tab:>-
+ set listchars+=tab:<=>,trail:-
+ let expected = [
+ \ '<======>aa<====>$',
+ \ '..bb<==>--$',
+ \ '...cccc>-$',
+ \ 'dd........ee--<>$',
+ \ '-$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars-=trail:-
+ let expected = [
+ \ '<======>aa<====>$',
+ \ '..bb<==>..$',
+ \ '...cccc>.$',
+ \ 'dd........ee..<>$',
+ \ '.$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars-=tab:<=>
+ set listchars+=tab:>-
set listchars+=trail:<
set nolist
normal ggdG
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index af18760065..055d944b15 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -1,9 +1,32 @@
" Test that the system menu can be loaded.
+if !has('menu')
+ finish
+endif
+
func Test_load_menu()
try
source $VIMRUNTIME/menu.vim
catch
call assert_report('error while loading menus: ' . v:exception)
endtry
+ call assert_match('browse confirm w', execute(':menu File.Save'))
+ source $VIMRUNTIME/delmenu.vim
+endfunc
+
+func Test_translate_menu()
+ if !has('multi_lang')
+ return
+ endif
+ if !filereadable($VIMRUNTIME . '/lang/menu_de_de.latin1.vim')
+ throw 'Skipped: translated menu not found'
+ endif
+
+ " First delete any English menus.
+ source $VIMRUNTIME/delmenu.vim
+ set langmenu=de_de
+ source $VIMRUNTIME/menu.vim
+ call assert_match('browse confirm w', execute(':menu Datei.Speichern'))
+
+ source $VIMRUNTIME/delmenu.vim
endfunc
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 9ba264deb6..c2e7bb7bf9 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -238,4 +238,18 @@ func Test_mkview_no_file_name()
%bwipe
endfunc
+func Test_mksession_quote_in_filename()
+ let v:errmsg = ''
+ %bwipe!
+ split another
+ split x'y\"z
+ mksession! Xtest_mks_quoted.out
+ %bwipe!
+ source Xtest_mks_quoted.out
+ call assert_true(bufexists("x'y\"z"))
+
+ %bwipe!
+ call delete('Xtest_mks_quoted.out')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index cdfdd473b9..62ddad5dce 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -5,6 +5,7 @@ if !has('timers')
endif
source shared.vim
+source load.vim
func MyHandler(timer)
let g:val += 1
@@ -14,13 +15,17 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
+func s:assert_inrange(lower, upper, actual)
+ return assert_inrange(a:lower, LoadAdjust(a:upper), a:actual)
+endfunc
+
func Test_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(40, 120, slept)
+ call s:assert_inrange(40, 120, slept)
else
call assert_inrange(20, 120, slept)
endif
@@ -32,7 +37,7 @@ func Test_repeat_three()
let slept = WaitFor('g:val == 3')
call assert_equal(3, g:val)
if has('reltime')
- call assert_inrange(120, 250, slept)
+ call s:assert_inrange(120, 250, slept)
else
call assert_inrange(80, 200, slept)
endif
@@ -58,7 +63,7 @@ func Test_with_partial_callback()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(40, 130, slept)
+ call s:assert_inrange(40, 130, slept)
else
call assert_inrange(20, 100, slept)
endif
@@ -121,7 +126,7 @@ func Test_paused()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(0, 140, slept)
+ call s:assert_inrange(0, 140, slept)
else
call assert_inrange(0, 10, slept)
endif
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index dbffabd707..0fc153e8ce 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -240,6 +240,8 @@ static void terminfo_start(UI *ui)
const char *vte_version_env = os_getenv("VTE_VERSION");
long vtev = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0;
bool iterm_env = termprg && strstr(termprg, "iTerm.app");
+ bool nsterm = (termprg && strstr(termprg, "Apple_Terminal"))
+ || terminfo_is_term_family(term, "nsterm");
bool konsole = terminfo_is_term_family(term, "konsole")
|| os_getenv("KONSOLE_PROFILE_NAME")
|| os_getenv("KONSOLE_DBUS_SESSION");
@@ -247,8 +249,8 @@ static void terminfo_start(UI *ui)
long konsolev = konsolev_env ? strtol(konsolev_env, NULL, 10)
: (konsole ? 1 : 0);
- patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env);
- augment_terminfo(data, term, colorterm, vtev, konsolev, iterm_env);
+ patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env, nsterm);
+ augment_terminfo(data, term, colorterm, vtev, konsolev, iterm_env, nsterm);
data->can_change_scroll_region =
!!unibi_get_str(data->ut, unibi_change_scroll_region);
data->can_set_lr_margin =
@@ -1277,7 +1279,7 @@ static void tui_option_set(UI *ui, String name, Object value)
static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
Integer endcol, Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
TUIData *data = ui->data;
@@ -1299,7 +1301,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
(int)clearattr);
}
- if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
+ if (flags & kLineFlagWrap && ui->width == grid->width
+ && linerow + 1 < grid->height) {
// Only do line wrapping if the grid width is equal to the terminal
// width and the line continuation is within the grid.
@@ -1490,7 +1493,7 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name)
/// and several terminal emulators falsely announce incorrect terminal types.
static void patch_terminfo_bugs(TUIData *data, const char *term,
const char *colorterm, long vte_version,
- long konsolev, bool iterm_env)
+ long konsolev, bool iterm_env, bool nsterm)
{
unibi_term *ut = data->ut;
const char *xterm_version = os_getenv("XTERM_VERSION");
@@ -1499,7 +1502,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
#endif
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
- || terminfo_is_term_family(term, "nsterm");
+ || nsterm;
bool kitty = terminfo_is_term_family(term, "xterm-kitty");
bool linuxvt = terminfo_is_term_family(term, "linux");
bool bsdvt = terminfo_is_bsd_console(term);
@@ -1518,7 +1521,6 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
bool alacritty = terminfo_is_term_family(term, "alacritty");
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
- bool konsole_pretending_xterm = xterm && konsolev;
bool gnome_pretending_xterm = xterm && colorterm
&& strstr(colorterm, "gnome-terminal");
bool mate_pretending_xterm = xterm && colorterm
@@ -1572,11 +1574,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// claim to be xterm. Or they would mimic xterm properly enough to be
// treatable as xterm.
- // 2017-04 terminfo.src lacks these. genuine Xterm has them, as have
- // the false claimants.
+ // 2017-04 terminfo.src lacks these. Xterm-likes have them.
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
if (true_xterm) {
// 2017-04 terminfo.src lacks these. genuine Xterm has them.
@@ -1584,15 +1587,6 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds");
unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
}
- if (true_xterm
- || iterm_pretending_xterm
- || gnome_pretending_xterm
- || konsole_pretending_xterm) {
- // Apple's outdated copy of terminfo.src for MacOS lacks these.
- // genuine Xterm and three false claimants have them.
- unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
- unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
- }
} else if (rxvt) {
// 2017-04 terminfo.src lacks these. Unicode rxvt has them.
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
@@ -1609,11 +1603,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
} else if (tmux) {
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
} else if (terminfo_is_term_family(term, "interix")) {
// 2017-04 terminfo.src lacks this.
unibi_set_if_empty(ut, unibi_carriage_return, "\x0d");
} else if (linuxvt) {
- // Apple's outdated copy of terminfo.src for MacOS lacks these.
unibi_set_if_empty(ut, unibi_parm_up_cursor, "\x1b[%p1%dA");
unibi_set_if_empty(ut, unibi_parm_down_cursor, "\x1b[%p1%dB");
unibi_set_if_empty(ut, unibi_parm_right_cursor, "\x1b[%p1%dC");
@@ -1781,12 +1776,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
/// capabilities.
static void augment_terminfo(TUIData *data, const char *term,
const char *colorterm, long vte_version,
- long konsolev, bool iterm_env)
+ long konsolev, bool iterm_env, bool nsterm)
{
unibi_term *ut = data->ut;
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
- || terminfo_is_term_family(term, "nsterm");
+ || nsterm;
bool bsdvt = terminfo_is_bsd_console(term);
bool dtterm = terminfo_is_term_family(term, "dtterm");
bool rxvt = terminfo_is_term_family(term, "rxvt");
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 96232ab223..16370f2d10 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -33,6 +33,7 @@
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/highlight.h"
+#include "nvim/ui_compositor.h"
#include "nvim/window.h"
#include "nvim/cursor_shape.h"
#ifdef FEAT_TUI
@@ -60,11 +61,11 @@ static bool pending_mode_update = false;
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
-# define UI_LOG(funname, ...)
+# define UI_LOG(funname)
#else
static size_t uilog_seen = 0;
static char uilog_last_event[1024] = { 0 };
-# define UI_LOG(funname, ...) \
+# define UI_LOG(funname) \
do { \
if (strequal(uilog_last_event, STR(funname))) { \
uilog_seen++; \
@@ -80,42 +81,34 @@ static char uilog_last_event[1024] = { 0 };
} while (0)
#endif
-// UI_CALL invokes a function on all registered UI instances. The functions can
-// have 0-10 arguments (configurable by SELECT_NTH).
-//
-// See http://stackoverflow.com/a/11172679 for how it works.
-#ifdef _MSC_VER
-# define UI_CALL(funname, ...) \
- do { \
- UI_LOG(funname, 0); \
- for (size_t i = 0; i < ui_count; i++) { \
- UI *ui = uis[i]; \
- UI_CALL_MORE(funname, __VA_ARGS__); \
- } \
- } while (0)
-#else
-# define UI_CALL(...) \
- do { \
- UI_LOG(__VA_ARGS__, 0); \
- for (size_t i = 0; i < ui_count; i++) { \
- UI *ui = uis[i]; \
- UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
+// UI_CALL invokes a function on all registered UI instances.
+// This is called by code generated by generators/gen_api_ui_events.lua
+// C code should use ui_call_{funname} instead.
+# define UI_CALL(cond, funname, ...) \
+ do { \
+ bool any_call = false; \
+ for (size_t i = 0; i < ui_count; i++) { \
+ UI *ui = uis[i]; \
+ if (ui->funname && (cond)) { \
+ ui->funname(__VA_ARGS__); \
+ any_call = true; \
} \
- } while (0)
-#endif
-#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, MORE, \
- MORE, MORE, MORE, MORE, ZERO, ignore)
-#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11
-#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
-// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
-#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
-#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
-#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
+ } \
+ if (any_call) { \
+ UI_LOG(funname); \
+ } \
+ } while (0)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events_call.generated.h"
#endif
+void ui_init(void)
+{
+ default_grid.handle = 1;
+ ui_comp_init();
+}
+
void ui_builtin_start(void)
{
#ifdef FEAT_TUI
@@ -135,17 +128,12 @@ void ui_builtin_start(void)
#endif
}
-void ui_builtin_stop(void)
-{
- UI_CALL(stop);
-}
-
bool ui_rgb_attached(void)
{
if (!headless_mode && p_tgc) {
return true;
}
- for (size_t i = 0; i < ui_count; i++) {
+ for (size_t i = 1; i < ui_count; i++) {
if (uis[i]->rgb) {
return true;
}
@@ -155,13 +143,13 @@ bool ui_rgb_attached(void)
bool ui_active(void)
{
- return ui_count != 0;
+ return ui_count > 1;
}
void ui_event(char *name, Array args)
{
bool args_consumed = false;
- UI_CALL(event, name, args, &args_consumed);
+ ui_call_event(name, args, &args_consumed);
if (!args_consumed) {
api_free_array(args);
}
@@ -212,6 +200,10 @@ void ui_refresh(void)
screen_resize(width, height);
p_lz = save_p_lz;
+ if (ext_widgets[kUIMessages]) {
+ p_ch = 0;
+ command_height();
+ }
ui_mode_info_set();
pending_mode_update = true;
ui_cursor_shape();
@@ -257,6 +249,9 @@ void ui_attach_impl(UI *ui)
if (ui_count == MAX_UI_COUNT) {
abort();
}
+ if (!ui->ui_ext[kUIMultigrid]) {
+ ui_comp_attach(ui);
+ }
uis[ui_count++] = ui;
ui_refresh_options();
@@ -303,6 +298,10 @@ void ui_detach_impl(UI *ui)
&& !exiting) {
ui_schedule_refresh();
}
+
+ if (!ui->ui_ext[kUIMultigrid]) {
+ ui_comp_detach(ui);
+ }
}
void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
@@ -315,16 +314,25 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
BOOLEAN_OBJ(active));
}
+ if (ext == kUITermColors) {
+ ui_default_colors_set();
+ }
}
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
int clearattr, bool wrap)
{
+ LineFlags flags = wrap ? kLineFlagWrap : 0;
+ if (startcol == -1) {
+ startcol = 0;
+ flags |= kLineFlagInvalid;
+ }
+
size_t off = grid->line_offset[row] + (size_t)startcol;
- UI_CALL(raw_line, grid->handle, row, startcol, endcol, clearcol, clearattr,
- wrap, (const schar_T *)grid->chars + off,
- (const sattr_T *)grid->attrs + off);
+ ui_call_raw_line(grid->handle, row, startcol, endcol, clearcol, clearattr,
+ flags, (const schar_T *)grid->chars + off,
+ (const sattr_T *)grid->attrs + off);
if (p_wd) { // 'writedelay': flush & delay each time.
int old_row = cursor_row, old_col = cursor_col;
@@ -376,6 +384,8 @@ void ui_flush(void)
{
cmdline_ui_flush();
win_ui_flush();
+ msg_ext_ui_flush();
+
if (pending_cursor_update) {
ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
pending_cursor_update = false;
@@ -412,16 +422,16 @@ void ui_cursor_shape(void)
conceal_check_cursor_line();
}
-/// Returns true if `widget` is externalized.
-bool ui_is_external(UIExtension widget)
+/// Returns true if the given UI extension is enabled.
+bool ui_has(UIExtension ext)
{
- return ui_ext[widget];
+ return ui_ext[ext];
}
Array ui_array(void)
{
Array all_uis = ARRAY_DICT_INIT;
- for (size_t i = 0; i < ui_count; i++) {
+ for (size_t i = 1; i < ui_count; i++) {
UI *ui = uis[i];
Dictionary info = ARRAY_DICT_INIT;
PUT(info, "width", INTEGER_OBJ(ui->width));
@@ -452,7 +462,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
return;
}
- wp->w_grid.requested_rows = (int)height;
- wp->w_grid.requested_cols = (int)width;
- redraw_win_later(wp, SOME_VALID);
+ // non-positive indicates no request
+ wp->w_height_request = (int)MAX(height, 0);
+ wp->w_width_request = (int)MAX(width, 0);
+ win_set_inner_size(wp);
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 16237214cb..490cc930b1 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -14,10 +14,12 @@ typedef enum {
kUIPopupmenu,
kUITabline,
kUIWildmenu,
-#define kUIGlobalCount (kUIWildmenu+1)
+ kUIMessages,
+#define kUIGlobalCount kUILinegrid
kUILinegrid,
kUIMultigrid,
kUIHlState,
+ kUITermColors,
kUIExtCount,
} UIExtension;
@@ -26,16 +28,26 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_popupmenu",
"ext_tabline",
"ext_wildmenu",
+ "ext_messages",
"ext_linegrid",
"ext_multigrid",
"ext_hlstate",
+ "ext_termcolors",
});
typedef struct ui_t UI;
+enum {
+ kLineFlagWrap = 1,
+ kLineFlagInvalid = 2,
+};
+
+typedef int LineFlags;
+
struct ui_t {
bool rgb;
+ bool composed;
bool ui_ext[kUIExtCount]; ///< Externalized widgets
int width, height;
void *data;
@@ -44,14 +56,6 @@ struct ui_t {
# include "ui_events.generated.h"
#endif
- // For perfomance and simplicity, we use the dense screen representation
- // in the bridge and the TUI. The remote_ui module will translate this
- // in to the public grid_line format.
- void (*raw_line)(UI *ui, Integer grid, Integer row, Integer startcol,
- Integer endcol, Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk, const sattr_T *attrs);
- void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
- void (*stop)(UI *ui);
void (*inspect)(UI *ui, Dictionary *info);
};
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index bd5d37be73..91cd458702 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -153,14 +153,14 @@ static void ui_bridge_raw_line_event(void **argv)
UI *ui = UI(argv[0]);
ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
- PTR2INT(argv[7]), argv[8], argv[9]);
+ (LineFlags)PTR2INT(argv[7]), argv[8], argv[9]);
xfree(argv[8]);
xfree(argv[9]);
}
static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
size_t ncol = (size_t)(endcol-startcol);
@@ -168,7 +168,7 @@ static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
- INT2PTR(clearattr), INT2PTR(wrap), c, hl);
+ INT2PTR(clearattr), INT2PTR(flags), c, hl);
}
static void ui_bridge_suspend(UI *b)
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
new file mode 100644
index 0000000000..c7ba0306e4
--- /dev/null
+++ b/src/nvim/ui_compositor.c
@@ -0,0 +1,442 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// Compositor: merge floating grids with the main grid for display in
+// TUI and non-multigrid UIs.
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/log.h"
+#include "nvim/main.h"
+#include "nvim/ascii.h"
+#include "nvim/vim.h"
+#include "nvim/ui.h"
+#include "nvim/highlight.h"
+#include "nvim/memory.h"
+#include "nvim/ui_compositor.h"
+#include "nvim/ugrid.h"
+#include "nvim/screen.h"
+#include "nvim/syntax.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/os/os.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_compositor.c.generated.h"
+#endif
+
+static UI *compositor = NULL;
+static int composed_uis = 0;
+kvec_t(ScreenGrid *) layers = KV_INITIAL_VALUE;
+
+static size_t bufsize = 0;
+static schar_T *linebuf;
+static sattr_T *attrbuf;
+
+#ifndef NDEBUG
+static int chk_width = 0, chk_height = 0;
+#endif
+
+static ScreenGrid *curgrid;
+
+static bool valid_screen = true;
+static bool msg_scroll_mode = false;
+static int msg_first_invalid = 0;
+
+void ui_comp_init(void)
+{
+ if (compositor != NULL) {
+ return;
+ }
+ compositor = xcalloc(1, sizeof(UI));
+
+ compositor->rgb = true;
+ compositor->grid_resize = ui_comp_grid_resize;
+ compositor->grid_clear = ui_comp_grid_clear;
+ compositor->grid_scroll = ui_comp_grid_scroll;
+ compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
+ compositor->raw_line = ui_comp_raw_line;
+ compositor->win_scroll_over_start = ui_comp_win_scroll_over_start;
+ compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset;
+
+ // Be unopinionated: will be attached together with a "real" ui anyway
+ compositor->width = INT_MAX;
+ compositor->height = INT_MAX;
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ compositor->ui_ext[i] = true;
+ }
+
+ // TODO(bfredl): this will be more complicated if we implement
+ // hlstate per UI (i e reduce hl ids for non-hlstate UIs)
+ compositor->ui_ext[kUIHlState] = false;
+
+ kv_push(layers, &default_grid);
+ curgrid = &default_grid;
+
+ ui_attach_impl(compositor);
+}
+
+
+void ui_comp_attach(UI *ui)
+{
+ composed_uis++;
+ ui->composed = true;
+}
+
+void ui_comp_detach(UI *ui)
+{
+ composed_uis--;
+ if (composed_uis == 0) {
+ xfree(linebuf);
+ xfree(attrbuf);
+ linebuf = NULL;
+ attrbuf = NULL;
+ bufsize = 0;
+ }
+ ui->composed = false;
+}
+
+bool ui_comp_should_draw(void)
+{
+ return composed_uis != 0 && valid_screen;
+}
+
+/// TODO(bfredl): later on the compositor should just use win_float_pos events,
+/// though that will require slight event order adjustment: emit the win_pos
+/// events in the beginning of update_screen(0), rather than in ui_flush()
+bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width)
+{
+ if (grid->comp_index != 0) {
+ bool moved = (row != grid->comp_row) || (col != grid->comp_col);
+ if (ui_comp_should_draw()) {
+ // Redraw the area covered by the old position, and is not covered
+ // by the new position. Disable the grid so that compose_area() will not
+ // use it.
+ grid->comp_disabled = true;
+ compose_area(grid->comp_row, row,
+ grid->comp_col, grid->comp_col + grid->Columns);
+ if (grid->comp_col < col) {
+ compose_area(MAX(row, grid->comp_row),
+ MIN(row+height, grid->comp_row+grid->Rows),
+ grid->comp_col, col);
+ }
+ if (col+width < grid->comp_col+grid->Columns) {
+ compose_area(MAX(row, grid->comp_row),
+ MIN(row+height, grid->comp_row+grid->Rows),
+ col+width, grid->comp_col+grid->Columns);
+ }
+ compose_area(row+height, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col + grid->Columns);
+ grid->comp_disabled = false;
+ }
+ grid->comp_row = row;
+ grid->comp_col = col;
+ return moved;
+ }
+#ifndef NDEBUG
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ if (kv_A(layers, i) == grid) {
+ assert(false);
+ }
+ }
+#endif
+ // not found: new grid
+ kv_push(layers, grid);
+ grid->comp_row = row;
+ grid->comp_col = col;
+ grid->comp_index = kv_size(layers)-1;
+ return true;
+}
+
+void ui_comp_remove_grid(ScreenGrid *grid)
+{
+ assert(grid != &default_grid);
+ if (grid->comp_index == 0) {
+ // grid wasn't present
+ return;
+ }
+
+ if (curgrid == grid) {
+ curgrid = &default_grid;
+ }
+
+ for (size_t i = grid->comp_index; i < kv_size(layers)-1; i++) {
+ kv_A(layers, i) = kv_A(layers, i+1);
+ kv_A(layers, i)->comp_index = i;
+ }
+ (void)kv_pop(layers);
+ grid->comp_index = 0;
+
+ // recompose the area under the grid
+ // inefficent when being overlapped: only draw up to grid->comp_index
+ ui_comp_compose_grid(grid);
+}
+
+bool ui_comp_set_grid(handle_T handle)
+{
+ if (curgrid->handle == handle) {
+ return true;
+ }
+ ScreenGrid *grid = NULL;
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ if (kv_A(layers, i)->handle == handle) {
+ grid = kv_A(layers, i);
+ break;
+ }
+ }
+ if (grid != NULL) {
+ curgrid = grid;
+ return true;
+ }
+ return false;
+}
+
+static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
+ Integer r, Integer c)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) {
+ return;
+ }
+ int cursor_row = curgrid->comp_row+(int)r;
+ int cursor_col = curgrid->comp_col+(int)c;
+
+ if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) {
+ // TODO(bfredl): this happens with 'writedelay', refactor?
+ // abort();
+ return;
+ }
+ ui_composed_call_grid_cursor_goto(1, cursor_row, cursor_col);
+}
+
+
+/// Baseline implementation. This is always correct, but we can sometimes
+/// do something more efficient (where efficiency means smaller deltas to
+/// the downstream UI.)
+static void compose_line(Integer row, Integer startcol, Integer endcol,
+ LineFlags flags)
+{
+ // in case we start on the right half of a double-width char, we need to
+ // check the left half. But skip it in output if it wasn't doublewidth.
+ int skip = 0;
+ if (startcol > 0 && (flags & kLineFlagInvalid)) {
+ startcol--;
+ skip = 1;
+ }
+
+ int col = (int)startcol;
+ ScreenGrid *grid = NULL;
+ schar_T *bg_line = &default_grid.chars[default_grid.line_offset[row]
+ +(size_t)startcol];
+ sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row]
+ +(size_t)startcol];
+
+ while (col < endcol) {
+ int until = 0;
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ ScreenGrid *g = kv_A(layers, i);
+ if (g->comp_row > row || row >= g->comp_row + g->Rows
+ || g->comp_disabled) {
+ continue;
+ }
+ if (g->comp_col <= col && col < g->comp_col+g->Columns) {
+ grid = g;
+ until = g->comp_col+g->Columns;
+ } else if (g->comp_col > col) {
+ until = MIN(until, g->comp_col);
+ }
+ }
+ until = MIN(until, (int)endcol);
+
+ assert(grid != NULL);
+ assert(until > col);
+ assert(until <= default_grid.Columns);
+ size_t n = (size_t)(until-col);
+ size_t off = grid->line_offset[row-grid->comp_row]
+ + (size_t)(col-grid->comp_col);
+ memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
+ memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
+
+ // 'pumblend'
+ if (grid != &default_grid && p_pb) {
+ for (int i = col-(int)startcol; i < until-startcol; i++) {
+ bool thru = strequal((char *)linebuf[i], " "); // negative space
+ attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru);
+ if (thru) {
+ memcpy(linebuf[i], bg_line[i], sizeof(linebuf[i]));
+ }
+ }
+ }
+
+ // Tricky: if overlap caused a doublewidth char to get cut-off, must
+ // replace the visible half with a space.
+ if (linebuf[col-startcol][0] == NUL) {
+ linebuf[col-startcol][0] = ' ';
+ linebuf[col-startcol][1] = NUL;
+ } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
+ skip = 0;
+ }
+ if (grid->comp_col+grid->Columns > until
+ && grid->chars[off+n][0] == NUL) {
+ linebuf[until-1-startcol][0] = ' ';
+ linebuf[until-1-startcol][1] = '\0';
+ if (col == startcol && n == 1) {
+ skip = 0;
+ }
+ }
+
+ col = until;
+ }
+ assert(endcol <= chk_width);
+ assert(row < chk_height);
+
+ if (!(grid && grid == &default_grid)) {
+ // TODO(bfredl): too conservative, need check
+ // grid->line_wraps if grid->Width == Width
+ flags = flags & ~kLineFlagWrap;
+ }
+
+ ui_composed_call_raw_line(1, row, startcol+skip, endcol, endcol, 0, flags,
+ (const schar_T *)linebuf+skip,
+ (const sattr_T *)attrbuf+skip);
+}
+
+static void compose_area(Integer startrow, Integer endrow,
+ Integer startcol, Integer endcol)
+{
+ endrow = MIN(endrow, default_grid.Rows);
+ endcol = MIN(endcol, default_grid.Columns);
+ if (endcol <= startcol) {
+ return;
+ }
+ for (int r = (int)startrow; r < endrow; r++) {
+ compose_line(r, startcol, endcol, kLineFlagInvalid);
+ }
+}
+
+/// compose the area under the grid.
+///
+/// This is needed when some option affecting composition is changed,
+/// such as 'pumblend' for popupmenu grid.
+void ui_comp_compose_grid(ScreenGrid *grid)
+{
+ if (ui_comp_should_draw()) {
+ compose_area(grid->comp_row, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col+grid->Columns);
+ }
+}
+
+static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
+ Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr,
+ LineFlags flags, const schar_T *chunk,
+ const sattr_T *attrs)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
+ return;
+ }
+
+ row += curgrid->comp_row;
+ startcol += curgrid->comp_col;
+ endcol += curgrid->comp_col;
+ clearcol += curgrid->comp_col;
+ if (curgrid != &default_grid) {
+ flags = flags & ~kLineFlagWrap;
+ }
+ assert(row < default_grid.Rows);
+ assert(clearcol <= default_grid.Columns);
+ if (flags & kLineFlagInvalid
+ || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) {
+ compose_line(row, startcol, clearcol, flags);
+ } else {
+ ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr,
+ flags, chunk, attrs);
+ }
+}
+
+/// The screen is invalid and will soon be cleared
+///
+/// Don't redraw floats until screen is cleared
+void ui_comp_invalidate_screen(void)
+{
+ valid_screen = false;
+}
+
+static void ui_comp_grid_clear(UI *ui, Integer grid)
+{
+ // By design, only first grid uses clearing.
+ assert(grid == 1);
+ ui_composed_call_grid_clear(1);
+ valid_screen = true;
+}
+
+// TODO(bfredl): These events are somewhat of a hack. multiline messages
+// should later on be a separate grid, then this would just be ordinary
+// ui_comp_put_grid and ui_comp_remove_grid calls.
+static void ui_comp_win_scroll_over_start(UI *ui)
+{
+ msg_scroll_mode = true;
+ msg_first_invalid = ui->height;
+}
+
+static void ui_comp_win_scroll_over_reset(UI *ui)
+{
+ msg_scroll_mode = false;
+ for (size_t i = 1; i < kv_size(layers); i++) {
+ ScreenGrid *grid = kv_A(layers, i);
+ if (grid->comp_row+grid->Rows > msg_first_invalid) {
+ compose_area(msg_first_invalid, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col+grid->Columns);
+ }
+ }
+}
+
+static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
+ Integer bot, Integer left, Integer right,
+ Integer rows, Integer cols)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
+ return;
+ }
+ top += curgrid->comp_row;
+ bot += curgrid->comp_row;
+ left += curgrid->comp_col;
+ right += curgrid->comp_col;
+ if (!msg_scroll_mode && kv_size(layers) > curgrid->comp_index+1) {
+ // TODO(bfredl):
+ // 1. check if rectangles actually overlap
+ // 2. calulate subareas that can scroll.
+ if (rows > 0) {
+ bot -= rows;
+ } else {
+ top += (-rows);
+ }
+ compose_area(top, bot, left, right);
+ } else {
+ msg_first_invalid = MIN(msg_first_invalid, (int)top);
+ ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
+ }
+}
+
+static void ui_comp_grid_resize(UI *ui, Integer grid,
+ Integer width, Integer height)
+{
+ if (grid == 1) {
+ ui_composed_call_grid_resize(1, width, height);
+#ifndef NDEBUG
+ chk_width = (int)width;
+ chk_height = (int)height;
+#endif
+ size_t new_bufsize = (size_t)width;
+ if (bufsize != new_bufsize) {
+ xfree(linebuf);
+ xfree(attrbuf);
+ linebuf = xmalloc(new_bufsize * sizeof(*linebuf));
+ attrbuf = xmalloc(new_bufsize * sizeof(*attrbuf));
+ bufsize = new_bufsize;
+ }
+ }
+}
+
diff --git a/src/nvim/ui_compositor.h b/src/nvim/ui_compositor.h
new file mode 100644
index 0000000000..b3780db532
--- /dev/null
+++ b/src/nvim/ui_compositor.h
@@ -0,0 +1,12 @@
+// Bridge for communication between a UI thread and nvim core.
+// Used by the built-in TUI and libnvim-based UIs.
+#ifndef NVIM_UI_COMPOSITOR_H
+#define NVIM_UI_COMPOSITOR_H
+
+#include "nvim/ui.h"
+#include "nvim/event/defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_compositor.h.generated.h"
+#endif
+#endif // NVIM_UI_COMPOSITOR_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 20b71ab724..b7c9140b7f 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -62,12 +62,6 @@ static char *features[] = {
"-iconv",
#endif
-#ifdef HAVE_JEMALLOC
-"+jemalloc",
-#else
-"-jemalloc",
-#endif
-
#ifdef FEAT_TUI
"+tui",
#else
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 3cadfe612a..24fe529fd6 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1300,6 +1300,7 @@ static void win_rotate(int upwards, int count)
if (upwards) { /* first window becomes last window */
/* remove first window/frame from the list */
frp = curwin->w_frame->fr_parent->fr_child;
+ assert(frp != NULL);
wp1 = frp->fr_win;
win_remove(wp1, NULL);
frame_remove(frp);
@@ -3025,8 +3026,10 @@ static void new_frame(win_T *wp)
void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
+ firstwin->w_height_inner = firstwin->w_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
+ firstwin->w_width_inner = firstwin->w_width;
topframe->fr_width = Columns;
}
@@ -3118,7 +3121,7 @@ int win_new_tabpage(int after, char_u *filename)
redraw_all_later(NOT_VALID);
- if (ui_is_external(kUIMultigrid)) {
+ if (ui_has(kUIMultigrid)) {
tabpage_check_windows(tp);
}
@@ -3319,7 +3322,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
lastwin = tp->tp_lastwin;
topframe = tp->tp_topframe;
- if (old_curtab != curtab && ui_is_external(kUIMultigrid)) {
+ if (old_curtab != curtab && ui_has(kUIMultigrid)) {
tabpage_check_windows(old_curtab);
}
@@ -3889,7 +3892,6 @@ static win_T *win_alloc(win_T *after, int hidden)
// allocate window structure and linesizes arrays
win_T *new_wp = xcalloc(1, sizeof(win_T));
- win_alloc_lines(new_wp);
new_wp->handle = ++last_win_id;
handle_register_window(new_wp);
@@ -3970,7 +3972,7 @@ win_free (
}
}
- win_free_lsize(wp);
+ xfree(wp->w_lines);
for (i = 0; i < wp->w_tagstacklen; ++i)
xfree(wp->w_tagstack[i].tagname);
@@ -4010,7 +4012,7 @@ win_free (
void win_free_grid(win_T *wp, bool reinit)
{
- if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) {
+ if (wp->w_grid.handle != 0 && ui_has(kUIMultigrid)) {
ui_call_grid_destroy(wp->w_grid.handle);
wp->w_grid.handle = 0;
}
@@ -4118,28 +4120,6 @@ static void frame_remove(frame_T *frp)
/*
- * Allocate w_lines[] for window "wp".
- */
-void win_alloc_lines(win_T *wp)
-{
- wp->w_lines_valid = 0;
- assert(wp->w_grid.Rows >= 0);
- wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T));
-}
-
-/*
- * free lsize arrays for a window
- */
-void win_free_lsize(win_T *wp)
-{
- // TODO: why would wp be NULL here?
- if (wp != NULL) {
- xfree(wp->w_lines);
- wp->w_lines = NULL;
- }
-}
-
-/*
* Called from win_new_shellsize() after Rows changed.
* This only does the current tab page, others must be done when made active.
*/
@@ -4876,9 +4856,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
// Has no effect when the window is less than two lines.
void set_fraction(win_T *wp)
{
- if (wp->w_height > 1) {
- wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2)
- / (long)wp->w_height;
+ if (wp->w_height_inner > 1) {
+ wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height_inner / 2)
+ / (long)wp->w_height_inner;
}
}
@@ -4889,46 +4869,25 @@ void set_fraction(win_T *wp)
*/
void win_new_height(win_T *wp, int height)
{
- int prev_height = wp->w_height;
-
- /* Don't want a negative height. Happens when splitting a tiny window.
- * Will equalize heights soon to fix it. */
- if (height < 0)
+ // Don't want a negative height. Happens when splitting a tiny window.
+ // Will equalize heights soon to fix it.
+ if (height < 0) {
height = 0;
- if (wp->w_height == height)
- return; /* nothing to do */
-
- if (wp->w_height > 0) {
- if (wp == curwin) {
- // w_wrow needs to be valid. When setting 'laststatus' this may
- // call win_new_height() recursively.
- validate_cursor();
- }
- if (wp->w_height != prev_height) { // -V547
- return; // Recursive call already changed the size, bail out.
- }
- if (wp->w_wrow != wp->w_prev_fraction_row) {
- set_fraction(wp);
- }
}
-
- wp->w_height = height;
- wp->w_skipcol = 0;
-
- // There is no point in adjusting the scroll position when exiting. Some
- // values might be invalid.
- if (!exiting) {
- scroll_to_fraction(wp, prev_height);
+ if (wp->w_height == height) {
+ return; // nothing to do
}
+ wp->w_height = height;
wp->w_pos_changed = true;
+ win_set_inner_size(wp);
}
void scroll_to_fraction(win_T *wp, int prev_height)
{
linenr_T lnum;
int sline, line_size;
- int height = wp->w_height;
+ int height = wp->w_height_inner;
/* Don't change w_topline when height is zero. Don't set w_topline when
* 'scrollbind' is set and this isn't the current window. */
@@ -4951,8 +4910,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
// Make sure the whole cursor line is visible, if possible.
const int rows = plines_win(wp, lnum, false);
- if (sline > wp->w_height - rows) {
- sline = wp->w_height - rows;
+ if (sline > wp->w_height_inner - rows) {
+ sline = wp->w_height_inner - rows;
wp->w_wrow -= rows - line_size;
}
}
@@ -4964,14 +4923,14 @@ void scroll_to_fraction(win_T *wp, int prev_height)
* room use w_skipcol;
*/
wp->w_wrow = line_size;
- if (wp->w_wrow >= wp->w_height
- && (wp->w_width - win_col_off(wp)) > 0) {
- wp->w_skipcol += wp->w_width - win_col_off(wp);
- --wp->w_wrow;
- while (wp->w_wrow >= wp->w_height) {
- wp->w_skipcol += wp->w_width - win_col_off(wp)
+ if (wp->w_wrow >= wp->w_height_inner
+ && (wp->w_width_inner - win_col_off(wp)) > 0) {
+ wp->w_skipcol += wp->w_width_inner - win_col_off(wp);
+ wp->w_wrow--;
+ while (wp->w_wrow >= wp->w_height_inner) {
+ wp->w_skipcol += wp->w_width_inner - win_col_off(wp)
+ win_col_off2(wp);
- --wp->w_wrow;
+ wp->w_wrow--;
}
}
set_topline(wp, lnum);
@@ -5024,10 +4983,58 @@ void scroll_to_fraction(win_T *wp, int prev_height)
redraw_win_later(wp, SOME_VALID);
wp->w_redr_status = TRUE;
invalidate_botline_win(wp);
+}
+
+void win_set_inner_size(win_T *wp)
+{
+ int width = wp->w_width_request;
+ if (width == 0) {
+ width = wp->w_width;
+ }
+
+ int prev_height = wp->w_height_inner;
+ int height = wp->w_height_request;
+ if (height == 0) {
+ height = wp->w_height;
+ }
+
+ if (height != prev_height) {
+ if (height > 0) {
+ if (wp == curwin) {
+ // w_wrow needs to be valid. When setting 'laststatus' this may
+ // call win_new_height() recursively.
+ validate_cursor();
+ }
+ if (wp->w_height_inner != prev_height) { // -V547
+ return; // Recursive call already changed the size, bail out.
+ }
+ if (wp->w_wrow != wp->w_prev_fraction_row) {
+ set_fraction(wp);
+ }
+ }
+ wp->w_height_inner = height;
+ wp->w_skipcol = 0;
+
+ // There is no point in adjusting the scroll position when exiting. Some
+ // values might be invalid.
+ if (!exiting) {
+ scroll_to_fraction(wp, prev_height);
+ }
+ }
+
+ if (width != wp->w_width_inner) {
+ wp->w_width_inner = width;
+ wp->w_lines_valid = 0;
+ changed_line_abv_curs_win(wp);
+ invalidate_botline_win(wp);
+ if (wp == curwin) {
+ update_topline();
+ curs_columns(true); // validate w_wrow
+ }
+ }
if (wp->w_buffer->terminal) {
- terminal_resize(wp->w_buffer->terminal, 0, wp->w_height);
- redraw_win_later(wp, NOT_VALID);
+ terminal_check_size(wp->w_buffer->terminal);
}
}
@@ -5035,23 +5042,11 @@ void scroll_to_fraction(win_T *wp, int prev_height)
void win_new_width(win_T *wp, int width)
{
wp->w_width = width;
- wp->w_lines_valid = 0;
- changed_line_abv_curs_win(wp);
- invalidate_botline_win(wp);
- if (wp == curwin) {
- update_topline();
- curs_columns(TRUE); /* validate w_wrow */
- }
+ win_set_inner_size(wp);
+
redraw_win_later(wp, NOT_VALID);
wp->w_redr_status = TRUE;
- if (wp->w_buffer->terminal) {
- if (wp->w_height != 0) {
- terminal_resize(wp->w_buffer->terminal,
- (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))),
- 0);
- }
- }
wp->w_pos_changed = true;
}
@@ -5355,7 +5350,7 @@ static void last_status_rec(frame_T *fr, int statusline)
*/
int tabline_height(void)
{
- if (ui_is_external(kUITabline)) {
+ if (ui_has(kUITabline)) {
return 0;
}
assert(first_tabpage);
@@ -6092,7 +6087,7 @@ void win_findbuf(typval_T *argvars, list_T *list)
void win_ui_flush(void)
{
- if (!ui_is_external(kUIMultigrid)) {
+ if (!ui_has(kUIMultigrid)) {
return;
}