aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt13
-rw-r--r--src/nvim/api/deprecated.c7
-rw-r--r--src/nvim/api/extmark.c60
-rw-r--r--src/nvim/api/keysets.h1
-rw-r--r--src/nvim/api/private/helpers.c21
-rw-r--r--src/nvim/api/ui.c15
-rw-r--r--src/nvim/api/ui_events.in.h3
-rw-r--r--src/nvim/api/vim.c13
-rw-r--r--src/nvim/api/win_config.c35
-rw-r--r--src/nvim/arglist.c20
-rw-r--r--src/nvim/autocmd.c3
-rw-r--r--src/nvim/buffer.c53
-rw-r--r--src/nvim/buffer_defs.h11
-rw-r--r--src/nvim/bufwrite.c4
-rw-r--r--src/nvim/cmdexpand.c4
-rw-r--r--src/nvim/decoration.c27
-rw-r--r--src/nvim/decoration.h1
-rw-r--r--src/nvim/drawline.c193
-rw-r--r--src/nvim/drawscreen.c131
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/eval.c4
-rw-r--r--src/nvim/eval.lua4
-rw-r--r--src/nvim/eval/vars.c145
-rw-r--r--src/nvim/event/process.c1
-rw-r--r--src/nvim/ex_cmds.c2
-rw-r--r--src/nvim/ex_cmds2.c18
-rw-r--r--src/nvim/ex_docmd.c6
-rw-r--r--src/nvim/ex_eval.c3
-rw-r--r--src/nvim/ex_eval_defs.h5
-rw-r--r--src/nvim/fileio.c25
-rw-r--r--src/nvim/fold.c9
-rw-r--r--src/nvim/grid.c381
-rw-r--r--src/nvim/grid.h37
-rw-r--r--src/nvim/grid_defs.h20
-rw-r--r--src/nvim/highlight.c2
-rw-r--r--src/nvim/lua/stdlib.c13
-rw-r--r--src/nvim/main.c9
-rw-r--r--src/nvim/map.c19
-rw-r--r--src/nvim/map.h38
-rw-r--r--src/nvim/map_glyph_cache.c102
-rw-r--r--src/nvim/map_key_impl.c.h2
-rw-r--r--src/nvim/map_value_impl.c.h2
-rw-r--r--src/nvim/message.c27
-rw-r--r--src/nvim/message.h2
-rw-r--r--src/nvim/mouse.c110
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c5
-rw-r--r--src/nvim/normal.c12
-rw-r--r--src/nvim/option.c7
-rw-r--r--src/nvim/option_defs.h7
-rw-r--r--src/nvim/options.lua32
-rw-r--r--src/nvim/optionstr.c39
-rw-r--r--src/nvim/os/pty_process_win.c2
-rw-r--r--src/nvim/popupmenu.c41
-rw-r--r--src/nvim/regexp.c51
-rw-r--r--src/nvim/statusline.c50
-rw-r--r--src/nvim/tui/tui.c55
-rw-r--r--src/nvim/types.h6
-rw-r--r--src/nvim/ugrid.c4
-rw-r--r--src/nvim/ugrid.h4
-rw-r--r--src/nvim/ui_client.h3
-rw-r--r--src/nvim/ui_compositor.c30
-rw-r--r--src/nvim/version.c10
-rw-r--r--src/nvim/window.c4
63 files changed, 1172 insertions, 797 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 82a88c6774..db79de63cc 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -230,6 +230,11 @@ if(WIN32)
endif()
elseif(APPLE)
target_link_libraries(nvim PRIVATE "-framework CoreServices")
+
+ # Actually export symbols - symbols may not be visible even though
+ # ENABLE_EXPORTS is set to true. See
+ # https://github.com/neovim/neovim/issues/25295
+ set_target_properties(nvim PROPERTIES LINK_FLAGS "-Wl,-export_dynamic")
endif()
if(UNIX)
@@ -879,7 +884,13 @@ set(VIMDOC_FILES
)
glob_wrapper(API_SOURCES ${PROJECT_SOURCE_DIR}/src/nvim/api/*.c)
-glob_wrapper(LUA_SOURCES ${NVIM_RUNTIME_DIR}/lua/vim/*.lua)
+
+glob_wrapper(LUA_SOURCES
+ ${NVIM_RUNTIME_DIR}/lua/vim/*.lua
+ ${NVIM_RUNTIME_DIR}/lua/vim/filetype/*.lua
+ ${NVIM_RUNTIME_DIR}/lua/vim/lsp/*.lua
+ ${NVIM_RUNTIME_DIR}/lua/vim/treesitter/*.lua
+)
add_custom_command(
OUTPUT ${VIMDOC_FILES}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index f5a77ece7e..f345edc52c 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -518,6 +518,7 @@ static int64_t convert_index(int64_t index)
/// @return Option Information
Dictionary nvim_get_option_info(String name, Error *err)
FUNC_API_SINCE(7)
+ FUNC_API_DEPRECATED_SINCE(11)
{
return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err);
}
@@ -531,6 +532,7 @@ Dictionary nvim_get_option_info(String name, Error *err)
/// @param[out] err Error details, if any
void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
}
@@ -543,6 +545,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
/// @return Option value (global)
Object nvim_get_option(String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
@@ -556,6 +559,7 @@ Object nvim_get_option(String name, Arena *arena, Error *err)
/// @return Option value
Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -577,6 +581,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err)
/// @param[out] err Error details, if any
void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -596,6 +601,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
/// @return Option value
Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
win_T *win = find_window_by_handle(window, err);
@@ -617,6 +623,7 @@ Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err)
/// @param[out] err Error details, if any
void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(11)
{
win_T *win = find_window_by_handle(window, err);
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index faab6e593c..91e197bea7 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -113,6 +113,36 @@ static Object hl_group_name(int hl_id, bool hl_name)
}
}
+Array virt_text_to_array(VirtText vt, bool hl_name)
+{
+ Array chunks = ARRAY_DICT_INIT;
+ Array hl_array = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < kv_size(vt); i++) {
+ char *text = kv_A(vt, i).text;
+ int hl_id = kv_A(vt, i).hl_id;
+ if (text == NULL) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ continue;
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, CSTR_TO_OBJ(text));
+ if (hl_array.size > 0) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunk, ARRAY_OBJ(hl_array));
+ hl_array = (Array)ARRAY_DICT_INIT;
+ } else if (hl_id > 0) {
+ ADD(chunk, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
+ }
+ assert(hl_array.size == 0);
+ return chunks;
+}
+
static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name)
{
Array rv = ARRAY_DICT_INIT;
@@ -145,16 +175,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
}
if (kv_size(decor->virt_text)) {
- Array chunks = ARRAY_DICT_INIT;
- for (size_t i = 0; i < decor->virt_text.size; i++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &decor->virt_text.items[i];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ Array chunks = virt_text_to_array(decor->virt_text, hl_name);
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
if (decor->virt_text_pos == kVTWinCol) {
@@ -171,19 +192,9 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
if (kv_size(decor->virt_lines)) {
Array all_chunks = ARRAY_DICT_INIT;
bool virt_lines_leftcol = false;
- for (size_t i = 0; i < decor->virt_lines.size; i++) {
- Array chunks = ARRAY_DICT_INIT;
- VirtText *vt = &decor->virt_lines.items[i].line;
- virt_lines_leftcol = decor->virt_lines.items[i].left_col;
- for (size_t j = 0; j < vt->size; j++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &vt->items[j];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ for (size_t i = 0; i < kv_size(decor->virt_lines); i++) {
+ virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col;
+ Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name);
ADD(all_chunks, ARRAY_OBJ(chunks));
}
PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
@@ -1188,8 +1199,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
goto free_exit;
}
if (j < arr.size - 1) {
- kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
- .hl_id = hl_id }));
+ kv_push(virt_text, ((VirtTextChunk){ .text = NULL, .hl_id = hl_id }));
}
}
} else {
diff --git a/src/nvim/api/keysets.h b/src/nvim/api/keysets.h
index a98cbe276f..236e75983e 100644
--- a/src/nvim/api/keysets.h
+++ b/src/nvim/api/keysets.h
@@ -112,6 +112,7 @@ typedef struct {
String footer_pos;
String style;
Boolean noautocmd;
+ Boolean fixed;
} Dict(float_config);
typedef struct {
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 8fcabb3605..5463578b56 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -21,8 +21,10 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/vars.h"
#include "nvim/ex_eval.h"
#include "nvim/garray.h"
+#include "nvim/globals.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
@@ -235,8 +237,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
// Delete the key
if (di == NULL) {
// Doesn't exist, fail
- api_set_error(err, kErrorTypeValidation, "Key not found: %s",
- key.data);
+ api_set_error(err, kErrorTypeValidation, "Key not found: %s", key.data);
} else {
// Notify watchers
if (watched) {
@@ -265,13 +266,23 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
di = tv_dict_item_alloc_len(key.data, key.size);
tv_dict_add(dict, di);
} else {
- if (watched) {
- tv_copy(&di->di_tv, &oldtv);
- }
// Return the old value
if (retval) {
rv = vim_to_object(&di->di_tv);
}
+ bool type_error = false;
+ if (dict == &vimvardict
+ && !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
+ tv_clear(&tv);
+ if (type_error) {
+ api_set_error(err, kErrorTypeValidation,
+ "Setting v:%s to value with wrong type", key.data);
+ }
+ return rv;
+ }
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
tv_clear(&di->di_tv);
}
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 70c97be984..0ea2310042 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -833,8 +833,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
bool was_space = false;
for (size_t i = 0; i < ncells; i++) {
repeat++;
- if (i == ncells - 1 || attrs[i] != attrs[i + 1]
- || strcmp(chunk[i], chunk[i + 1]) != 0) {
+ if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) {
if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) {
// close to overflowing the redraw buffer. finish this event,
// flush, and start a new "grid_line" event at the current position.
@@ -859,7 +858,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1);
nelem++;
mpack_array(buf, csize);
- mpack_str(buf, chunk[i]);
+ char sc_buf[MAX_SCHAR_SIZE];
+ schar_get(sc_buf, chunk[i]);
+ mpack_str(buf, sc_buf);
if (csize >= 2) {
mpack_uint(buf, (uint32_t)attrs[i]);
if (csize >= 3) {
@@ -869,7 +870,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
data->ncells_pending += MIN(repeat, 2);
last_hl = attrs[i];
repeat = 0;
- was_space = strequal(chunk[i], " ");
+ was_space = chunk[i] == schar_from_ascii(' ');
}
}
// If the last chunk was all spaces, add a clearing chunk even if there are
@@ -893,8 +894,10 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
for (int i = 0; i < endcol - startcol; i++) {
remote_ui_cursor_goto(ui, row, startcol + i);
remote_ui_highlight_set(ui, attrs[i]);
- remote_ui_put(ui, chunk[i]);
- if (utf_ambiguous_width(utf_ptr2char((char *)chunk[i]))) {
+ char sc_buf[MAX_SCHAR_SIZE];
+ schar_get(sc_buf, chunk[i]);
+ remote_ui_put(ui, sc_buf);
+ if (utf_ambiguous_width(utf_ptr2char(sc_buf))) {
data->client_col = -1; // force cursor update
}
}
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index fc70215352..6ca5024a04 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -167,4 +167,7 @@ void msg_history_show(Array entries)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_history_clear(void)
FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
+
+void error_exit(Integer status)
+ FUNC_API_SINCE(12);
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 9d08e9b6c7..00641c633d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1442,7 +1442,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// @param rhs Right-hand-side |{rhs}| of the mapping.
/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|,
/// values are booleans (default false). Also:
-/// - "noremap" non-recursive mapping |:noremap|
+/// - "noremap" disables |recursive_mapping|, like |:noremap|
/// - "desc" human-readable description.
/// - "callback" Lua function called in place of {rhs}.
/// - "replace_keycodes" (boolean) When "expr" is true, replace keycodes in the
@@ -1710,6 +1710,8 @@ static void write_msg(String message, bool to_err, bool writeln)
msg_didout = true; \
kv_drop(line_buf, kv_size(line_buf)); \
kv_resize(line_buf, LINE_BUFFER_MIN_SIZE); \
+ } else if (c == NUL) { \
+ kv_push(line_buf, NL); \
} else { \
kv_push(line_buf, c); \
}
@@ -1948,7 +1950,9 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E
}
ret = arena_array(arena, 3);
size_t off = g->line_offset[(size_t)row] + (size_t)col;
- ADD_C(ret, CSTR_AS_OBJ((char *)g->chars[off]));
+ char *sc_buf = arena_alloc(arena, MAX_SCHAR_SIZE, false);
+ schar_get(sc_buf, g->chars[off]);
+ ADD_C(ret, CSTR_AS_OBJ(sc_buf));
int attr = g->attrs[off];
ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err)));
// will not work first time
@@ -1964,6 +1968,11 @@ void nvim__screenshot(String path)
ui_call_screenshot(path);
}
+void nvim__invalidate_glyph_cache(void)
+{
+ schar_cache_clear_force();
+}
+
Object nvim__unpack(String str, Error *err)
FUNC_API_FAST
{
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 63cf3bb701..ea0b7ce512 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -16,7 +16,7 @@
#include "nvim/drawscreen.h"
#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/highlight_group.h"
#include "nvim/macros.h"
#include "nvim/mbyte.h"
@@ -163,6 +163,8 @@
/// - noautocmd: If true then no buffer-related autocommand events such as
/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from
/// calling this function.
+/// - fixed: If true when anchor is NW or SW, the float window
+/// would be kept fixed even if the window would be truncated.
///
/// @param[out] err Error details, if any
///
@@ -195,6 +197,9 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
if (win_valid(wp) && buffer > 0) {
Boolean noautocmd = !enter || fconfig.noautocmd;
win_set_buf(wp, buf, noautocmd, err);
+ if (!fconfig.noautocmd) {
+ apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf);
+ }
}
if (!win_valid(wp)) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
@@ -254,34 +259,28 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
BorderTextType bordertext_type)
{
- VirtText chunks;
+ VirtText vt;
AlignTextPos align;
char *field_name;
char *field_pos_name;
switch (bordertext_type) {
case kBorderTextTitle:
- chunks = fconfig->title_chunks;
+ vt = fconfig->title_chunks;
align = fconfig->title_pos;
field_name = "title";
field_pos_name = "title_pos";
break;
case kBorderTextFooter:
- chunks = fconfig->footer_chunks;
+ vt = fconfig->footer_chunks;
align = fconfig->footer_pos;
field_name = "footer";
field_pos_name = "footer_pos";
break;
+ default:
+ abort();
}
- Array bordertext = ARRAY_DICT_INIT;
- for (size_t i = 0; i < chunks.size; i++) {
- Array tuple = ARRAY_DICT_INIT;
- ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text));
- if (chunks.items[i].hl_id > 0) {
- ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id)));
- }
- ADD(bordertext, ARRAY_OBJ(tuple));
- }
+ Array bordertext = virt_text_to_array(vt, true);
PUT(config, field_name, ARRAY_OBJ(bordertext));
char *pos;
@@ -348,7 +347,7 @@ Dictionary nvim_win_get_config(Window window, Error *err)
for (size_t i = 0; i < 8; i++) {
Array tuple = ARRAY_DICT_INIT;
- String s = cstrn_to_string(config->border_chars[i], sizeof(schar_T));
+ String s = cstrn_to_string(config->border_chars[i], MAX_SCHAR_SIZE);
int hi_id = config->border_hl_ids[i];
char *hi_name = syn_id2name(hi_id);
@@ -520,7 +519,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
{
struct {
const char *name;
- schar_T chars[8];
+ char chars[8][MAX_SCHAR_SIZE];
bool shadow_color;
} defaults[] = {
{ "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" }, false },
@@ -531,7 +530,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
{ NULL, { { NUL } }, false },
};
- schar_T *chars = fconfig->border_chars;
+ char (*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars;
int *hl_ids = fconfig->border_hl_ids;
fconfig->border = true;
@@ -845,6 +844,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
fconfig->noautocmd = config->noautocmd;
}
+ if (HAS_KEY_X(config, fixed)) {
+ fconfig->fixed = config->fixed;
+ }
+
return true;
#undef HAS_KEY_X
}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index 74348052b0..9c2b3ba6d8 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -857,12 +857,17 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall)
while (true) {
win_T *wpnext = NULL;
tabpage_T *tpnext = curtab->tp_next;
- for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
+ // Try to close floating windows first
+ for (win_T *wp = lastwin->w_floating ? lastwin : firstwin; wp != NULL; wp = wpnext) {
int i;
- wpnext = wp->w_next;
+ wpnext = wp->w_floating
+ ? wp->w_prev->w_floating ? wp->w_prev : firstwin
+ : (wp->w_next == NULL || wp->w_next->w_floating) ? NULL : wp->w_next;
buf_T *buf = wp->w_buffer;
if (buf->b_ffname == NULL
- || (!aall->keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
+ || (!aall->keep_tabs
+ && (buf->b_nwindows > 1 || wp->w_width != Columns
+ || (wp->w_floating && !is_aucmd_win(wp))))) {
i = aall->opened_len;
} else {
// check if the buffer in this window is in the arglist
@@ -917,7 +922,7 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall)
(void)autowrite(buf, false);
// Check if autocommands removed the window.
if (!win_valid(wp) || !bufref_valid(&bufref)) {
- wpnext = firstwin; // Start all over...
+ wpnext = lastwin->w_floating ? lastwin : firstwin; // Start all over...
continue;
}
}
@@ -930,7 +935,7 @@ static void arg_all_close_unused_windows(arg_all_state_T *aall)
// check if autocommands removed the next window
if (!win_valid(wpnext)) {
// start all over...
- wpnext = firstwin;
+ wpnext = lastwin->w_floating ? lastwin : firstwin;
}
}
}
@@ -977,6 +982,8 @@ static void arg_all_open_windows(arg_all_state_T *aall, int count)
if (aall->keep_tabs) {
aall->new_curwin = wp;
aall->new_curtab = curtab;
+ } else if (wp->w_floating) {
+ break;
} else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
emsg(_(e_window_layout_changed_unexpectedly));
i = count;
@@ -1098,7 +1105,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
autocmd_no_leave++;
last_curwin = curwin;
last_curtab = curtab;
- win_enter(lastwin, false);
+ // lastwin may be aucmd_win
+ win_enter(lastwin_nofloating(), false);
// Open up to "count" windows.
arg_all_open_windows(&aall, count);
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 3fa20f4e48..ff0c2f063f 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1341,7 +1341,6 @@ void aucmd_restbuf(aco_save_T *aco)
if (aco->use_aucmd_win_idx >= 0) {
win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win;
- curbuf->b_nwindows--;
// Find "awp", it can't be closed, but it may be in another tab page.
// Do not trigger autocommands here.
block_autocmds();
@@ -1357,7 +1356,7 @@ void aucmd_restbuf(aco_save_T *aco)
}
}
win_found:
- ;
+ curbuf->b_nwindows--;
const bool save_stop_insert_mode = stop_insert_mode;
// May need to stop Insert mode if we were in a prompt buffer.
leaving_window(curwin);
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index d2a5eab0a5..7a3e65e10e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -3182,7 +3182,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
(curbuf->b_flags & BF_NOTEDITED) && !dontwrite
? _("[Not edited]") : "",
(curbuf->b_flags & BF_NEW) && !dontwrite
- ? new_file_message() : "",
+ ? _("[New]") : "",
(curbuf->b_flags & BF_READERR)
? _("[Read errors]") : "",
curbuf->b_p_ro
@@ -3221,7 +3221,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
(int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
- (void)append_arg_number(curwin, buffer, IOSIZE, !shortmess(SHM_FILE));
+ (void)append_arg_number(curwin, buffer, IOSIZE);
if (dont_truncate) {
// Temporarily set msg_scroll to avoid the message being truncated.
@@ -3372,7 +3372,7 @@ void maketitle(void)
*buf_p = NUL;
}
- append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)), false);
+ append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)));
xstrlcat(buf_p, " - NVIM", (sizeof(buf) - (size_t)(buf_p - buf)));
@@ -3509,15 +3509,14 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
}
}
-/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
+/// Append (2 of 8) to "buf[buflen]", if editing more than one file.
///
/// @param wp window whose buffers to check
/// @param[in,out] buf string buffer to add the text to
/// @param buflen length of the string buffer
-/// @param add_file if true, add "file" before the arg number
///
/// @return true if it was appended.
-bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
+bool append_arg_number(win_T *wp, char *buf, int buflen)
FUNC_ATTR_NONNULL_ALL
{
// Nothing to do
@@ -3525,17 +3524,7 @@ bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
return false;
}
- const char *msg;
- switch ((wp->w_arg_idx_invalid ? 1 : 0) + (add_file ? 2 : 0)) {
- case 0:
- msg = _(" (%d of %d)"); break;
- case 1:
- msg = _(" ((%d) of %d)"); break;
- case 2:
- msg = _(" (file %d of %d)"); break;
- case 3:
- msg = _(" (file (%d) of %d)"); break;
- }
+ const char *msg = wp->w_arg_idx_invalid ? _(" ((%d) of %d)") : _(" (%d of %d)");
char *p = buf + strlen(buf); // go to the end of the buffer
vim_snprintf(p, (size_t)(buflen - (p - buf)), msg, wp->w_arg_idx + 1, ARGCOUNT);
@@ -3616,21 +3605,28 @@ void ex_buffer_all(exarg_T *eap)
}
while (true) {
tpnext = curtab->tp_next;
- for (wp = firstwin; wp != NULL; wp = wpnext) {
- wpnext = wp->w_next;
+ // Try to close floating windows first
+ for (wp = lastwin->w_floating ? lastwin : firstwin; wp != NULL; wp = wpnext) {
+ wpnext = wp->w_floating
+ ? wp->w_prev->w_floating ? wp->w_prev : firstwin
+ : (wp->w_next == NULL || wp->w_next->w_floating) ? NULL : wp->w_next;
if ((wp->w_buffer->b_nwindows > 1
+ || wp->w_floating
|| ((cmdmod.cmod_split & WSP_VERT)
? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch
- tabline_height() - global_stl_height()
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin))
&& !ONE_WINDOW
- && !(wp->w_closing
- || wp->w_buffer->b_locked > 0)) {
- win_close(wp, false, false);
- wpnext = firstwin; // just in case an autocommand does
- // something strange with windows
- tpnext = first_tabpage; // start all over...
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
+ && !is_aucmd_win(wp)) {
+ if (win_close(wp, false, false) == FAIL) {
+ break;
+ }
+ // Just in case an autocommand does something strange with
+ // windows: start all over...
+ wpnext = lastwin->w_floating ? lastwin : firstwin;
+ tpnext = first_tabpage;
open_wins = 0;
} else {
open_wins++;
@@ -3650,7 +3646,8 @@ void ex_buffer_all(exarg_T *eap)
//
// Don't execute Win/Buf Enter/Leave autocommands here.
autocmd_no_enter++;
- win_enter(lastwin, false);
+ // lastwin may be aucmd_win
+ win_enter(lastwin_nofloating(), false);
autocmd_no_leave++;
for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) {
// Check if this buffer needs a window
@@ -3668,7 +3665,7 @@ void ex_buffer_all(exarg_T *eap)
} else {
// Check if this buffer already has a window
for (wp = firstwin; wp != NULL; wp = wp->w_next) {
- if (wp->w_buffer == buf) {
+ if (!wp->w_floating && wp->w_buffer == buf) {
break;
}
}
@@ -3742,7 +3739,7 @@ void ex_buffer_all(exarg_T *eap)
// Close superfluous windows.
for (wp = lastwin; open_wins > count;) {
r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
- || autowrite(wp->w_buffer, false) == OK);
+ || autowrite(wp->w_buffer, false) == OK) && !is_aucmd_win(wp);
if (!win_valid(wp)) {
// BufWrite Autocommands made the window invalid, start over
wp = lastwin;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 3f55dbbc00..ff0fca1a56 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -954,7 +954,7 @@ typedef struct {
WinStyle style;
bool border;
bool shadow;
- schar_T border_chars[8];
+ char border_chars[8][MAX_SCHAR_SIZE];
int border_hl_ids[8];
int border_attr[8];
bool title;
@@ -966,6 +966,7 @@ typedef struct {
VirtText footer_chunks;
int footer_width;
bool noautocmd;
+ bool fixed;
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
@@ -975,7 +976,8 @@ typedef struct {
.focusable = true, \
.zindex = kZIndexFloatDefault, \
.style = kWinStyleUnused, \
- .noautocmd = false })
+ .noautocmd = false, \
+ .fixed = false })
// Structure to store last cursor position and topline. Used by check_lnums()
// and reset_lnums().
@@ -1241,6 +1243,8 @@ struct window_S {
// this window, w_allbuf_opt is for all buffers in this window.
winopt_T w_onebuf_opt;
winopt_T w_allbuf_opt;
+ // transform a pointer to a "onebuf" option into a "allbuf" option
+#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
// A few options have local flags for P_INSECURE.
uint32_t w_p_stl_flags; // flags for 'statusline'
@@ -1256,9 +1260,6 @@ struct window_S {
int w_briopt_list; // additional indent for lists
int w_briopt_vcol; // indent for specific column
- // transform a pointer to a "onebuf" option into a "allbuf" option
-#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
-
long w_scbind_pos;
ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary.
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 445e946543..0c95314c52 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -1766,11 +1766,11 @@ restore_backup:
xstrlcat(IObuff, _("[Device]"), IOSIZE);
insert_space = true;
} else if (newfile) {
- xstrlcat(IObuff, new_file_message(), IOSIZE);
+ xstrlcat(IObuff, _("[New]"), IOSIZE);
insert_space = true;
}
if (no_eol) {
- msg_add_eol();
+ xstrlcat(IObuff, _("[noeol]"), IOSIZE);
insert_space = true;
}
// may add [unix/dos/mac]
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 9470f77ab5..1e088ec7fc 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -601,10 +601,10 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m
ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
? &msg_grid_adj : &default_grid;
- grid_puts(grid, buf, row, 0, attr);
+ grid_puts(grid, buf, -1, row, 0, attr);
if (selstart != NULL && highlight) {
*selend = NUL;
- grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
+ grid_puts(grid, selstart, -1, row, selstart_col, HL_ATTR(HLF_WM));
}
grid_fill(grid, row, row + 1, clen, Columns,
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index f4ca31040a..9d391cde8c 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -153,6 +153,24 @@ void clear_virttext(VirtText *text)
*text = (VirtText)KV_INITIAL_VALUE;
}
+/// Get the next chunk of a virtual text item.
+///
+/// @param[in] vt The virtual text item
+/// @param[in,out] pos Position in the virtual text item
+/// @param[in,out] attr Highlight attribute
+///
+/// @return The text of the chunk, or NULL if there are no more chunks
+char *next_virt_text_chunk(VirtText vt, size_t *pos, int *attr)
+{
+ char *text = NULL;
+ for (; text == NULL && *pos < kv_size(vt); (*pos)++) {
+ text = kv_A(vt, *pos).text;
+ int hl_id = kv_A(vt, *pos).hl_id;
+ *attr = hl_combine_attr(*attr, hl_id > 0 ? syn_id2attr(hl_id) : 0);
+ }
+ return text;
+}
+
Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
{
MarkTreeIter itr[1] = { 0 };
@@ -254,7 +272,7 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
DecorRange range = { start_row, start_col, end_row, end_col,
*decor, attr_id,
- kv_size(decor->virt_text) && owned, -1, ns_id, mark_id };
+ kv_size(decor->virt_text) && owned, -10, ns_id, mark_id };
kv_pushp(state->active);
size_t index;
@@ -268,10 +286,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
kv_A(state->active, index) = range;
}
-/// Initialize the draw_col of a newly-added non-inline virtual text item.
+/// Initialize the draw_col of a newly-added virtual text item.
static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item)
{
- if (win_col < 0) {
+ if (win_col < 0 && item->decor.virt_text_pos != kVTInline) {
item->draw_col = win_col;
} else if (item->decor.virt_text_pos == kVTOverlay) {
item->draw_col = (item->decor.virt_text_hide && hidden) ? INT_MIN : win_col;
@@ -371,8 +389,7 @@ next_mark:
spell = item.decor.spell;
}
if (item.start_row == state->row && item.start_col <= col
- && decor_virt_pos(&item.decor) && item.draw_col == -1
- && item.decor.virt_text_pos != kVTInline) {
+ && decor_virt_pos(&item.decor) && item.draw_col == -10) {
decor_init_draw_col(win_col, hidden, &item);
}
if (keep) {
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 0f191aa870..fef5ff7c2a 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -85,6 +85,7 @@ typedef struct {
/// Screen column to draw the virtual text.
/// When -1, the virtual text may be drawn after deciding where.
/// When -3, the virtual text should be drawn on the next screen line.
+ /// When -10, the virtual text has just been added.
/// When INT_MIN, the virtual text should no longer be drawn.
int draw_col;
uint64_t ns_id;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 811cfc1eb2..1e5798db32 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -106,6 +106,8 @@ typedef struct {
int c_extra; ///< extra chars, all the same
int c_final; ///< final char, mandatory if set
+ int n_closing; ///< number of chars in fdc which will be closing
+
bool extra_for_extmark; ///< n_extra set for inline virtual text
// saved "extra" items for when draw_state becomes WL_LINE (again)
@@ -221,11 +223,11 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
if (*p == TAB) {
cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
for (int c = 0; c < cells; c++) {
- schar_from_ascii(dest[c], ' ');
+ dest[rl ? -c : c] = schar_from_ascii(' ');
}
goto done;
} else if ((uint8_t)(*p) < 0x80 && u8cc[0] == 0) {
- schar_from_ascii(dest[0], *p);
+ dest[0] = schar_from_ascii(*p);
s->prev_c = u8c;
} else {
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
@@ -252,10 +254,10 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
} else {
s->prev_c = u8c;
}
- schar_from_cc(dest[0], u8c, u8cc);
+ dest[0] = schar_from_cc(u8c, u8cc);
}
if (cells > 1) {
- dest[1][0] = 0;
+ dest[rl ? -1 : 1] = 0;
}
done:
s->p += c_len;
@@ -275,12 +277,20 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
}
if (item->draw_col == -1) {
if (item->decor.virt_text_pos == kVTRightAlign) {
- right_pos -= item->decor.virt_text_width;
+ if (wp->w_p_rl) {
+ right_pos += item->decor.virt_text_width;
+ } else {
+ right_pos -= item->decor.virt_text_width;
+ }
item->draw_col = right_pos;
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
item->draw_col = state->eol_col;
} else if (item->decor.virt_text_pos == kVTWinCol) {
- item->draw_col = MAX(item->decor.col + col_off, 0);
+ if (wp->w_p_rl) {
+ item->draw_col = MIN(col_off - item->decor.col, wp->w_grid.cols - 1);
+ } else {
+ item->draw_col = MAX(col_off + item->decor.col, 0);
+ }
}
}
if (item->draw_col < 0) {
@@ -295,42 +305,44 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
}
if (kv_size(item->decor.virt_text)) {
col = draw_virt_text_item(buf, item->draw_col, item->decor.virt_text,
- item->decor.hl_mode, max_col, item->draw_col - col_off);
+ item->decor.hl_mode, max_col, item->draw_col - col_off, wp->w_p_rl);
}
item->draw_col = INT_MIN; // deactivate
if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- state->eol_col = col + 1;
+ if (wp->w_p_rl) {
+ state->eol_col = col - 1;
+ } else {
+ state->eol_col = col + 1;
+ }
}
- *end_col = MAX(*end_col, col);
+ if (wp->w_p_rl) {
+ *end_col = MIN(*end_col, col);
+ } else {
+ *end_col = MAX(*end_col, col);
+ }
}
}
static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
- int vcol)
+ int vcol, bool rl)
{
LineState s = LINE_STATE("");
int virt_attr = 0;
size_t virt_pos = 0;
- while (col < max_col) {
- if (!*s.p) {
+ while (rl ? col > max_col : col < max_col) {
+ if (*s.p == NUL) {
if (virt_pos >= kv_size(vt)) {
break;
}
virt_attr = 0;
- do {
- s.p = kv_A(vt, virt_pos).text;
- int hl_id = kv_A(vt, virt_pos).hl_id;
- virt_attr = hl_combine_attr(virt_attr,
- hl_id > 0 ? syn_id2attr(hl_id) : 0);
- virt_pos++;
- } while (!s.p && virt_pos < kv_size(vt));
- if (!s.p) {
+ s.p = next_virt_text_chunk(vt, &virt_pos, &virt_attr);
+ if (s.p == NULL) {
break;
}
}
- if (!*s.p) {
+ if (*s.p == NUL) {
continue;
}
int attr;
@@ -344,21 +356,28 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
attr = virt_attr;
}
schar_T dummy[2];
+ bool rl_overwrote_double_width = linebuf_char[col] == 0;
int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
- max_col - col, false, vcol);
+ rl ? col - max_col : max_col - col, rl, vcol);
// If we failed to emit a char, we still need to put a space and advance.
if (cells < 1) {
- schar_from_ascii(linebuf_char[col], ' ');
+ linebuf_char[col] = schar_from_ascii(' ');
cells = 1;
}
for (int c = 0; c < cells; c++) {
- linebuf_attr[col++] = attr;
+ linebuf_attr[col] = attr;
+ if (rl) {
+ col--;
+ } else {
+ col++;
+ }
}
- if (col < max_col && linebuf_char[col][0] == 0) {
- // If the left half of a double-width char is overwritten,
- // change the right half to a space so that grid redraws properly,
- // but don't advance the current column.
- schar_from_ascii(linebuf_char[col], ' ');
+ // If one half of a double-width char is overwritten,
+ // change the other half to a space so that grid redraws properly,
+ // but don't advance the current column.
+ if ((rl && col > max_col && rl_overwrote_double_width)
+ || (!rl && col < max_col && linebuf_char[col] == 0)) {
+ linebuf_char[col] = schar_from_ascii(' ');
}
vcol += cells;
}
@@ -384,7 +403,8 @@ static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv)
// Allocate a buffer, "wlv->extra[]" may already be in use.
xfree(wlv->p_extra_free);
wlv->p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1);
- wlv->n_extra = (int)fill_foldcolumn(wlv->p_extra_free, wp, wlv->foldinfo, wlv->lnum);
+ wlv->n_extra = (int)fill_foldcolumn(wlv->p_extra_free, wp, wlv->foldinfo, wlv->lnum,
+ &wlv->n_closing);
wlv->p_extra_free[wlv->n_extra] = NUL;
wlv->p_extra = wlv->p_extra_free;
wlv->c_extra = NUL;
@@ -405,7 +425,7 @@ static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv)
///
/// Assume monocell characters
/// @return number of chars added to \param p
-size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
+size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int *n_closing)
{
int i = 0;
int level;
@@ -447,16 +467,23 @@ size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
}
}
+ int n_closing_val = i;
+
if (closed) {
if (symbol != 0) {
// rollback previous write
char_counter -= (size_t)len;
memset(&p[char_counter], ' ', (size_t)len);
+ n_closing_val--;
}
len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]);
char_counter += (size_t)len;
}
+ if (n_closing) {
+ *n_closing = n_closing_val;
+ }
+
return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc);
}
@@ -917,17 +944,20 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
}
} else {
// already inside existing inline virtual text with multiple chunks
- VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i);
- wlv->virt_inline_i++;
- wlv->p_extra = vtc.text;
- wlv->n_extra = (int)strlen(vtc.text);
+ int attr = 0;
+ char *text = next_virt_text_chunk(wlv->virt_inline, &wlv->virt_inline_i, &attr);
+ if (text == NULL) {
+ continue;
+ }
+ wlv->p_extra = text;
+ wlv->n_extra = (int)strlen(text);
if (wlv->n_extra == 0) {
continue;
}
wlv->c_extra = NUL;
wlv->c_final = NUL;
- wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
- wlv->n_attr = mb_charlen(vtc.text);
+ wlv->extra_attr = attr;
+ wlv->n_attr = mb_charlen(text);
// If the text didn't reach until the first window
// column we need to skip cells.
if (wlv->skip_cells > 0) {
@@ -1377,8 +1407,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin);
statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
statuscol.sign_cul_id = statuscol.use_cul ? sign_cul.hl_id : 0;
- statuscol.num_attr = sign_num.hl_id ? syn_id2attr(sign_num.hl_id)
- : get_line_number_attr(wp, &wlv);
+ statuscol.num_attr = sign_num.hl_id > 0 ? syn_id2attr(sign_num.hl_id) : 0;
} else {
if (sign_cul.hl_id > 0) {
sign_cul_attr = syn_id2attr(sign_cul.hl_id);
@@ -1510,6 +1539,25 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
cts.cts_vcol += charsize;
prev_ptr = cts.cts_ptr;
MB_PTR_ADV(cts.cts_ptr);
+ if (wp->w_p_list) {
+ in_multispace = *prev_ptr == ' ' && (*cts.cts_ptr == ' '
+ || (prev_ptr > line && prev_ptr[-1] == ' '));
+ if (!in_multispace) {
+ multispace_pos = 0;
+ } else if (cts.cts_ptr >= line + leadcol
+ && wp->w_p_lcs_chars.multispace != NULL) {
+ multispace_pos++;
+ if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else if (cts.cts_ptr < line + leadcol
+ && wp->w_p_lcs_chars.leadmultispace != NULL) {
+ multispace_pos++;
+ if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ }
+ }
}
wlv.vcol = cts.cts_vcol;
ptr = cts.cts_ptr;
@@ -1665,13 +1713,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp);
}
}
- if (!virt_line_offset) {
+ if (virt_line_offset == 0) {
// Skip the column states if there is a "virt_left_col" line.
wlv.draw_state = WL_BRI - 1;
} else if (statuscol.draw) {
// Skip fold, sign and number states if 'statuscolumn' is set.
wlv.draw_state = WL_STC - 1;
}
+ if (virt_line_offset >= 0 && wp->w_p_rl) {
+ virt_line_offset = wp->w_grid.cols - 1 - virt_line_offset;
+ }
}
if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) {
@@ -1704,6 +1755,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.draw_state = WL_STC;
// Draw the 'statuscolumn' if option is set.
if (statuscol.draw) {
+ if (sign_num.hl_id == 0) {
+ statuscol.num_attr = get_line_number_attr(wp, &wlv);
+ }
if (statuscol.textp == NULL) {
v = (ptr - line);
get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
@@ -1759,7 +1813,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wlv.vcol >= wp->w_virtcol)
|| (number_only && wlv.draw_state > WL_STC))
&& wlv.filler_todo <= 0) {
- draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row);
+ draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
grid_put_linebuf(grid, wlv.row, 0, wlv.col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);
// Pretend we have finished updating the window. Except when
// 'cursorcolumn' is set.
@@ -2332,9 +2386,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
}
- in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
- if (!in_multispace) {
- multispace_pos = 0;
+ if (wp->w_p_list) {
+ in_multispace = c == ' ' && (*ptr == ' '
+ || (prev_ptr > line && prev_ptr[-1] == ' '));
+ if (!in_multispace) {
+ multispace_pos = 0;
+ }
}
// 'list': Change char 160 to 'nbsp' and space to 'space'.
@@ -2737,7 +2794,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.col += n;
} else {
// Add a blank character to highlight.
- schar_from_ascii(linebuf_char[wlv.off], ' ');
+ linebuf_char[wlv.off] = schar_from_ascii(' ');
}
if (area_attr == 0 && !has_fold) {
// Use attributes from match with highest priority among
@@ -2795,7 +2852,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
? 1 : 0);
if (has_decor) {
- has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip);
+ has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr,
+ wlv.col + (wp->w_p_rl ? -eol_skip : eol_skip));
}
if (((wp->w_p_cuc
@@ -2839,7 +2897,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int col_stride = wp->w_p_rl ? -1 : 1;
while (wp->w_p_rl ? wlv.col >= 0 : wlv.col < grid->cols) {
- schar_from_ascii(linebuf_char[wlv.off], ' ');
+ linebuf_char[wlv.off] = schar_from_ascii(' ');
linebuf_vcol[wlv.off] = MAXCOL;
wlv.col += col_stride;
if (draw_color_col) {
@@ -2873,7 +2931,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// logical line
int n = wp->w_p_rl ? -1 : 1;
while (wlv.col >= 0 && wlv.col < grid->cols) {
- schar_from_ascii(linebuf_char[wlv.off], ' ');
+ linebuf_char[wlv.off] = schar_from_ascii(' ');
linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol];
linebuf_vcol[wlv.off] = wlv.vcol;
wlv.off += n;
@@ -2883,9 +2941,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
if (kv_size(fold_vt) > 0) {
- draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0);
+ draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine,
+ wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl);
}
- draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row);
+ draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
grid_put_linebuf(grid, wlv.row, 0, wlv.col, grid->cols, wp->w_p_rl, wp, bg_attr, false);
wlv.row++;
@@ -2973,9 +3032,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.col--;
}
if (mb_utf8) {
- schar_from_cc(linebuf_char[wlv.off], mb_c, u8cc);
+ linebuf_char[wlv.off] = schar_from_cc(mb_c, u8cc);
} else {
- schar_from_ascii(linebuf_char[wlv.off], (char)c);
+ linebuf_char[wlv.off] = schar_from_ascii((char)c);
}
if (multi_attr) {
linebuf_attr[wlv.off] = multi_attr;
@@ -2984,26 +3043,39 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_attr[wlv.off] = wlv.char_attr;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
+ if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
+ linebuf_vcol[wlv.off] = wlv.vcol;
+ } else if (wlv.draw_state == WL_FOLD) {
+ if (wlv.n_closing > 0) {
+ linebuf_vcol[wlv.off] = -3;
+ wlv.n_closing--;
+ } else {
+ linebuf_vcol[wlv.off] = -2;
+ }
+ } else {
+ linebuf_vcol[wlv.off] = -1;
+ }
if (utf_char2cells(mb_c) > 1) {
// Need to fill two screen columns.
wlv.off++;
wlv.col++;
// UTF-8: Put a 0 in the second screen char.
- linebuf_char[wlv.off][0] = 0;
+ linebuf_char[wlv.off] = 0;
linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1];
+
if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
- wlv.vcol++;
+ linebuf_vcol[wlv.off] = ++wlv.vcol;
+ } else {
+ linebuf_vcol[wlv.off] = -1;
}
+
// When "wlv.tocol" is halfway through a character, set it to the end
// of the character, otherwise highlighting won't stop.
if (wlv.tocol == wlv.vcol) {
wlv.tocol++;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
-
if (wp->w_p_rl) {
// now it's time to backup one cell
wlv.off--;
@@ -3141,9 +3213,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int draw_col = wlv.col - wlv.boguscols;
if (virt_line_offset >= 0) {
draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line,
- kHlModeReplace, grid->cols, 0);
+ kHlModeReplace, wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl);
} else if (wlv.filler_todo <= 0) {
- draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, wlv.row);
+ draw_virt_text(wp, buf, win_col_offset, &draw_col, wp->w_p_rl ? -1 : grid->cols, wlv.row);
}
grid_put_linebuf(grid, wlv.row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap);
@@ -3154,9 +3226,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// Force a redraw of the first column of the next line.
current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
-
- // Remember that the line wraps, used for modeless copy.
- current_grid->line_wraps[current_row] = true;
}
wlv.boguscols = 0;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index f71a47a596..04918e9979 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -215,7 +215,6 @@ void screenclear(void)
for (int i = 0; i < default_grid.rows; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
default_grid.cols, true);
- default_grid.line_wraps[i] = false;
}
ui_call_grid_clear(1); // clear the display
@@ -444,6 +443,15 @@ int update_screen(void)
display_tick++; // let syntax code know we're in a next round of
// display updating
+ // glyph cache full, very rare
+ if (schar_cache_clear_if_full()) {
+ // must use CLEAR, as the contents of screen buffers cannot be
+ // compared to their previous state here.
+ // TODO(bfredl): if start to cache schar_T values in places (like fcs/lcs)
+ // we need to revalidate these here as well!
+ type = MAX(type, UPD_CLEAR);
+ }
+
// Tricky: vim code can reset msg_scrolled behind our back, so need
// separate bookkeeping for now.
if (msg_did_scroll) {
@@ -463,7 +471,7 @@ int update_screen(void)
// non-displayed part of msg_grid is considered invalid.
for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
+ msg_grid.cols, i < p_ch);
}
}
msg_grid.throttled = false;
@@ -701,15 +709,15 @@ void end_search_hl(void)
screen_search_hl.rm.regprog = NULL;
}
-static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText text_chunks, int row, int col)
+static void win_redr_bordertext(win_T *wp, VirtText vt, int col)
{
- for (size_t i = 0; i < text_chunks.size; i++) {
- char *text = text_chunks.items[i].text;
- int cell = (int)mb_string2cells(text);
- int hl_id = text_chunks.items[i].hl_id;
- int attr = hl_id ? syn_id2attr(hl_id) : 0;
- grid_puts(grid, text, row, col, attr);
- col += cell;
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *text = next_virt_text_chunk(vt, &i, &attr);
+ if (text == NULL) {
+ break;
+ }
+ col += grid_line_puts(col, text, -1, attr);
}
}
@@ -736,67 +744,70 @@ static void win_redr_border(win_T *wp)
ScreenGrid *grid = &wp->w_grid_alloc;
- schar_T *chars = wp->w_float_config.border_chars;
+ schar_T chars[8];
+ for (int i = 0; i < 8; i++) {
+ chars[i] = schar_from_str(wp->w_float_config.border_chars[i]);
+ }
int *attrs = wp->w_float_config.border_attr;
int *adj = wp->w_border_adj;
int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
if (adj[0]) {
- grid_puts_line_start(grid, 0);
+ grid_line_start(grid, 0);
if (adj[3]) {
- grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ grid_line_put_schar(0, chars[0], attrs[0]);
}
for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
}
if (wp->w_float_config.title) {
int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width,
wp->w_float_config.title_pos);
- win_redr_bordertext(wp, grid, wp->w_float_config.title_chunks, 0, title_col);
+ win_redr_bordertext(wp, wp->w_float_config.title_chunks, title_col);
}
if (adj[1]) {
- grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
+ grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
}
- grid_puts_line_flush(false);
+ grid_line_flush(false);
}
for (int i = 0; i < irow; i++) {
if (adj[3]) {
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
- grid_puts_line_flush(false);
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(0, chars[7], attrs[7]);
+ grid_line_flush(false);
}
if (adj[1]) {
- int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
- grid_puts_line_flush(false);
+ int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
+ grid_line_start(grid, i + adj[0]);
+ grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
+ grid_line_flush(false);
}
}
if (adj[2]) {
- grid_puts_line_start(grid, irow + adj[0]);
+ grid_line_start(grid, irow + adj[0]);
if (adj[3]) {
- grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
+ grid_line_put_schar(0, chars[6], attrs[6]);
}
for (int i = 0; i < icol; i++) {
- int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
- grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
+ int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
+ grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
}
if (wp->w_float_config.footer) {
int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width,
wp->w_float_config.footer_pos);
- win_redr_bordertext(wp, grid, wp->w_float_config.footer_chunks, grid->rows - 1, footer_col);
+ win_redr_bordertext(wp, wp->w_float_config.footer_chunks, footer_col);
}
if (adj[1]) {
- grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
+ grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
}
- grid_puts_line_flush(false);
+ grid_line_flush(false);
}
}
@@ -919,7 +930,9 @@ int showmode(void)
|| (State & MODE_INSERT)
|| restart_edit != NUL
|| VIsual_active));
- if (do_mode || reg_recording != 0) {
+
+ bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages));
+ if ((do_mode || reg_recording != 0) && can_show_mode) {
int sub_attr;
if (skip_showmode()) {
return 0; // show mode later
@@ -1294,23 +1307,25 @@ static void draw_hsep_win(win_T *wp)
}
/// Get the separator connector for specified window corner of window "wp"
-static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
+static schar_T get_corner_sep_connector(win_T *wp, WindowCorner corner)
{
// It's impossible for windows to be connected neither vertically nor horizontally
// So if they're not vertically connected, assume they're horizontally connected
+ int c;
if (vsep_connected(wp, corner)) {
if (hsep_connected(wp, corner)) {
- return wp->w_p_fcs_chars.verthoriz;
+ c = wp->w_p_fcs_chars.verthoriz;
} else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
- return wp->w_p_fcs_chars.vertright;
+ c = wp->w_p_fcs_chars.vertright;
} else {
- return wp->w_p_fcs_chars.vertleft;
+ c = wp->w_p_fcs_chars.vertleft;
}
} else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
- return wp->w_p_fcs_chars.horizdown;
+ c = wp->w_p_fcs_chars.horizdown;
} else {
- return wp->w_p_fcs_chars.horizup;
+ c = wp->w_p_fcs_chars.horizup;
}
+ return schar_from_char(c);
}
/// Draw separator connecting characters on the corners of window "wp"
@@ -1346,21 +1361,31 @@ static void draw_sep_connectors_win(win_T *wp)
win_at_left = frp->fr_parent == NULL;
// Draw the appropriate separator connector in every corner where drawing them is necessary
- if (!(win_at_top || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
- wp->w_winrow - 1, wp->w_wincol - 1, hl);
- }
- if (!(win_at_top || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
- wp->w_winrow - 1, W_ENDCOL(wp), hl);
- }
- if (!(win_at_bottom || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
- W_ENDROW(wp), wp->w_wincol - 1, hl);
+ // Make sure not to send cursor position updates to ui.
+ bool top_left = !(win_at_top || win_at_left);
+ bool top_right = !(win_at_top || win_at_right);
+ bool bot_left = !(win_at_bottom || win_at_left);
+ bool bot_right = !(win_at_bottom || win_at_right);
+
+ if (top_left || top_right) {
+ grid_line_start(&default_grid, wp->w_winrow - 1);
+ if (top_left) {
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_TOP_LEFT), hl);
+ }
+ if (top_right) {
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_TOP_RIGHT), hl);
+ }
+ grid_line_flush(false);
}
- if (!(win_at_bottom || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
- W_ENDROW(wp), W_ENDCOL(wp), hl);
+ if (bot_left || bot_right) {
+ grid_line_start(&default_grid, W_ENDROW(wp));
+ if (bot_left) {
+ grid_line_put_schar(wp->w_wincol - 1, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), hl);
+ }
+ if (bot_right) {
+ grid_line_put_schar(W_ENDCOL(wp), get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), hl);
+ }
+ grid_line_flush(false);
}
}
@@ -2357,7 +2382,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
utf_char2bytes(symbol, &fillbuf[charlen]);
// Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr);
+ grid_puts(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr);
grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, symbol, ' ', at_attr);
set_empty_rows(wp, srow);
wp->w_botline = lnum;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 216f8a67db..520d3bc2b3 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1449,6 +1449,7 @@ void edit_putchar(int c, bool highlight)
// save the character to be able to put it back
if (pc_status == PC_STATUS_UNSET) {
+ // TODO(bfredl): save the schar_T instead
grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr);
pc_status = PC_STATUS_SET;
}
@@ -1532,7 +1533,7 @@ void edit_unputchar(void)
if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
redrawWinline(curwin, curwin->w_cursor.lnum);
} else {
- grid_puts(&curwin->w_grid, pc_bytes, pc_row, pc_col, pc_attr);
+ grid_puts(&curwin->w_grid, pc_bytes, -1, pc_row, pc_col, pc_attr);
}
}
}
@@ -3485,7 +3486,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Otherwise remove the mode message.
if (reg_recording != 0 || restart_edit != NUL) {
showmode();
- } else if (p_smd && (got_int || !skip_showmode())) {
+ } else if (p_smd && (got_int || !skip_showmode())
+ && !(p_ch == 0 && !ui_has(kUIMessages))) {
msg("");
}
// Exit Insert mode
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a279b6d051..21c7cdee7d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1271,8 +1271,11 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
/// it in "*cp". Doesn't give error messages.
int eval_foldexpr(win_T *wp, int *cp)
{
+ const sctx_T saved_sctx = current_sctx;
const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL);
+
char *arg = wp->w_p_fde;
+ current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
emsg_off++;
if (use_sandbox) {
@@ -1309,6 +1312,7 @@ int eval_foldexpr(win_T *wp, int *cp)
}
textlock--;
clear_evalarg(&EVALARG_EVALUATE, NULL);
+ current_sctx = saved_sctx;
return (int)retval;
}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 2137dc07e4..858f7c8afd 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -5572,7 +5572,7 @@ M.funcs = {
jobstart = {
args = { 1, 2 },
desc = [=[
- Note: Prefer |vim.system()| in Lua.
+ Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
Spawns {cmd} as a job.
If {cmd} is a List it runs directly (no 'shell').
@@ -10973,7 +10973,7 @@ M.funcs = {
echo strutf16len('😊') " returns 2
echo strutf16len('ą́') " returns 1
echo strutf16len('ą́', v:true) " returns 3
-
+ <
]=],
name = 'strutf16len',
params = { { 'string', 'string' }, { 'countcc', '0|1' } },
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index e5b1b88eef..5d1da956ee 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -52,8 +52,8 @@
static const char *e_letunexp = N_("E18: Unexpected characters in :let");
static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
-static const char e_setting_str_to_value_with_wrong_type[]
- = N_("E963: Setting %s to value with wrong type");
+static const char e_setting_v_str_to_value_with_wrong_type[]
+ = N_("E963: Setting v:%s to value with wrong type");
static const char e_cannot_use_heredoc_here[]
= N_("E991: Cannot use =<< here");
@@ -1389,6 +1389,62 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
}
}
+/// Additional handling for setting a v: variable.
+///
+/// @return true if the variable should be set normally,
+/// false if nothing else needs to be done.
+bool before_set_vvar(const char *const varname, dictitem_T *const di, typval_T *const tv,
+ const bool copy, const bool watched, bool *const type_error)
+{
+ if (di->di_tv.v_type == VAR_STRING) {
+ typval_T oldtv = TV_INITIAL_VALUE;
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
+ XFREE_CLEAR(di->di_tv.vval.v_string);
+ if (copy || tv->v_type != VAR_STRING) {
+ const char *const val = tv_get_string(tv);
+ // Careful: when assigning to v:errmsg and tv_get_string()
+ // causes an error message the variable will already be set.
+ if (di->di_tv.vval.v_string == NULL) {
+ di->di_tv.vval.v_string = xstrdup(val);
+ }
+ } else {
+ // Take over the string to avoid an extra alloc/free.
+ di->di_tv.vval.v_string = tv->vval.v_string;
+ tv->vval.v_string = NULL;
+ }
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+ return false;
+ } else if (di->di_tv.v_type == VAR_NUMBER) {
+ typval_T oldtv = TV_INITIAL_VALUE;
+ if (watched) {
+ tv_copy(&di->di_tv, &oldtv);
+ }
+ di->di_tv.vval.v_number = tv_get_number(tv);
+ if (strcmp(varname, "searchforward") == 0) {
+ set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
+ } else if (strcmp(varname, "hlsearch") == 0) {
+ no_hlsearch = !di->di_tv.vval.v_number;
+ redraw_all_later(UPD_SOME_VALID);
+ }
+ // Notify watchers
+ if (watched) {
+ tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv);
+ tv_clear(&oldtv);
+ }
+ return false;
+ } else if (di->di_tv.v_type != tv->v_type) {
+ *type_error = true;
+ return false;
+ }
+ return true;
+}
+
/// Set variable to the given value
///
/// If the variable already exists, the value is updated. Otherwise the variable
@@ -1418,31 +1474,29 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
const bool is_const)
FUNC_ATTR_NONNULL_ALL
{
- dictitem_T *v;
- hashtab_T *ht;
- dict_T *dict;
-
const char *varname;
- ht = find_var_ht_dict(name, name_len, &varname, &dict);
+ dict_T *dict;
+ hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict);
const bool watched = tv_dict_is_watched(dict);
if (ht == NULL || *varname == NUL) {
semsg(_(e_illvar), name);
return;
}
- v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true);
+ const size_t varname_len = name_len - (size_t)(varname - name);
+ dictitem_T *di = find_var_in_ht(ht, 0, varname, varname_len, true);
// Search in parent scope which is possible to reference from lambda
- if (v == NULL) {
- v = find_var_in_scoped_ht(name, name_len, true);
+ if (di == NULL) {
+ di = find_var_in_scoped_ht(name, name_len, true);
}
- if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) {
+ if (tv_is_func(*tv) && var_wrong_func_name(name, di == NULL)) {
return;
}
typval_T oldtv = TV_INITIAL_VALUE;
- if (v != NULL) {
+ if (di != NULL) {
if (is_const) {
emsg(_(e_cannot_mod));
return;
@@ -1452,9 +1506,9 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// - Whether the variable is read-only
// - Whether the variable value is locked
// - Whether the variable is locked
- if (var_check_ro(v->di_flags, name, name_len)
- || value_check_lock(v->di_tv.v_lock, name, name_len)
- || var_check_lock(v->di_flags, name, name_len)) {
+ if (var_check_ro(di->di_flags, name, name_len)
+ || value_check_lock(di->di_tv.v_lock, name, name_len)
+ || var_check_lock(di->di_flags, name, name_len)) {
return;
}
@@ -1462,42 +1516,19 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Handle setting internal v: variables separately where needed to
// prevent changing the type.
- if (is_vimvarht(ht)) {
- if (v->di_tv.v_type == VAR_STRING) {
- XFREE_CLEAR(v->di_tv.vval.v_string);
- if (copy || tv->v_type != VAR_STRING) {
- const char *const val = tv_get_string(tv);
-
- // Careful: when assigning to v:errmsg and tv_get_string()
- // causes an error message the variable will already be set.
- if (v->di_tv.vval.v_string == NULL) {
- v->di_tv.vval.v_string = xstrdup(val);
- }
- } else {
- // Take over the string to avoid an extra alloc/free.
- v->di_tv.vval.v_string = tv->vval.v_string;
- tv->vval.v_string = NULL;
- }
- return;
- } else if (v->di_tv.v_type == VAR_NUMBER) {
- v->di_tv.vval.v_number = tv_get_number(tv);
- if (strcmp(varname, "searchforward") == 0) {
- set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
- } else if (strcmp(varname, "hlsearch") == 0) {
- no_hlsearch = !v->di_tv.vval.v_number;
- redraw_all_later(UPD_SOME_VALID);
- }
- return;
- } else if (v->di_tv.v_type != tv->v_type) {
- semsg(_(e_setting_str_to_value_with_wrong_type), name);
- return;
+ bool type_error = false;
+ if (is_vimvarht(ht)
+ && !before_set_vvar(varname, di, tv, copy, watched, &type_error)) {
+ if (type_error) {
+ semsg(_(e_setting_v_str_to_value_with_wrong_type), varname);
}
+ return;
}
if (watched) {
- tv_copy(&v->di_tv, &oldtv);
+ tv_copy(&di->di_tv, &oldtv);
}
- tv_clear(&v->di_tv);
+ tv_clear(&di->di_tv);
} else { // Add a new variable.
// Can't add "v:" or "a:" variable.
if (is_vimvarht(ht) || ht == get_funccal_args_ht()) {
@@ -1513,28 +1544,28 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Make sure dict is valid
assert(dict != NULL);
- v = xmalloc(offsetof(dictitem_T, di_key) + strlen(varname) + 1);
- STRCPY(v->di_key, varname);
- if (hash_add(ht, v->di_key) == FAIL) {
- xfree(v);
+ di = xmalloc(offsetof(dictitem_T, di_key) + varname_len + 1);
+ memcpy(di->di_key, varname, varname_len + 1);
+ if (hash_add(ht, di->di_key) == FAIL) {
+ xfree(di);
return;
}
- v->di_flags = DI_FLAGS_ALLOC;
+ di->di_flags = DI_FLAGS_ALLOC;
if (is_const) {
- v->di_flags |= DI_FLAGS_LOCK;
+ di->di_flags |= DI_FLAGS_LOCK;
}
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) {
- tv_copy(tv, &v->di_tv);
+ tv_copy(tv, &di->di_tv);
} else {
- v->di_tv = *tv;
- v->di_tv.v_lock = VAR_UNLOCKED;
+ di->di_tv = *tv;
+ di->di_tv.v_lock = VAR_UNLOCKED;
tv_init(tv);
}
if (watched) {
- tv_dict_watcher_notify(dict, v->di_key, &v->di_tv, &oldtv);
+ tv_dict_watcher_notify(dict, di->di_key, &di->di_tv, &oldtv);
tv_clear(&oldtv);
}
@@ -1542,7 +1573,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
// Like :lockvar! name: lock the value and what it contains, but only
// if the reference count is up to one. That locks only literal
// values.
- tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true);
+ tv_item_lock(&di->di_tv, DICT_MAXNEST, true, true);
}
}
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 95bf4d1c3b..00ba1334b0 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -423,6 +423,7 @@ static void exit_event(void **argv)
if (!exiting) {
if (ui_client_channel_id) {
+ ui_client_exit_status = status;
os_exit(status);
} else {
assert(status == 0); // Called from rpc_close(), which passes 0 as status.
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4f6b8f2c8f..dc7136196e 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2689,7 +2689,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Obey the 'O' flag in 'cpoptions': overwrite any previous file
// message.
- if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
+ if (shortmess(SHM_OVERALL) && !msg_listdo_overwrite && !exiting && p_verbose == 0) {
msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index b37076c62e..86934a645a 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -455,6 +455,10 @@ void ex_listdo(exarg_T *eap)
tabpage_T *tp;
char *save_ei = NULL;
+ // Temporarily override SHM_OVER and SHM_OVERALL to avoid that file
+ // message overwrites output from the command.
+ msg_listdo_overwrite++;
+
if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) {
// Don't do syntax HL autocommands. Skipping the syntax file is a
// great speed improvement.
@@ -518,9 +522,7 @@ void ex_listdo(exarg_T *eap)
if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
buf = NULL;
} else {
- save_clear_shm_value();
ex_cc(eap);
- restore_shm_value();
buf = curbuf;
i = (int)eap->line1 - 1;
@@ -547,9 +549,7 @@ void ex_listdo(exarg_T *eap)
if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) {
// Clear 'shm' to avoid that the file message overwrites
// any output from the command.
- save_clear_shm_value();
do_argfile(eap, i);
- restore_shm_value();
}
if (curwin->w_arg_idx != i) {
break;
@@ -612,11 +612,8 @@ void ex_listdo(exarg_T *eap)
break;
}
- // Go to the next buffer. Clear 'shm' to avoid that the file
- // message overwrites any output from the command.
- save_clear_shm_value();
+ // Go to the next buffer.
goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
- restore_shm_value();
// If autocommands took us elsewhere, quit here.
if (curbuf->b_fnum != next_fnum) {
@@ -633,11 +630,7 @@ void ex_listdo(exarg_T *eap)
size_t qf_idx = qf_get_cur_idx(eap);
- // Clear 'shm' to avoid that the file message overwrites
- // any output from the command.
- save_clear_shm_value();
ex_cnext(eap);
- restore_shm_value();
// If jumping to the next quickfix entry fails, quit here.
if (qf_get_cur_idx(eap) == qf_idx) {
@@ -664,6 +657,7 @@ void ex_listdo(exarg_T *eap)
listcmd_busy = false;
}
+ msg_listdo_overwrite--;
if (save_ei != NULL) {
buf_T *bnext;
aco_save_T aco;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 44610c81d8..ea93d0fe91 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -906,7 +906,7 @@ void handle_did_throw(void)
if (messages != NULL) {
do {
msglist_T *next = messages->next;
- emsg(messages->msg);
+ emsg_multiline(messages->msg, messages->multiline);
xfree(messages->msg);
xfree(messages->sfile);
xfree(messages);
@@ -4591,7 +4591,9 @@ static void ex_cquit(exarg_T *eap)
FUNC_ATTR_NORETURN
{
// this does not always pass on the exit code to the Manx compiler. why?
- getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
+ int status = eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE;
+ ui_call_error_exit(status);
+ getout(status);
}
/// Do preparations for "qall" and "wqall".
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 1b150ef75d..0704b47d40 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -157,7 +157,7 @@ int aborted_in_try(void)
/// When several messages appear in the same command, the first is usually the
/// most specific one and used as the exception value. The "severe" flag can be
/// set to true, if a later but severer message should be used instead.
-bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
+bool cause_errthrow(const char *mesg, bool multiline, bool severe, bool *ignore)
FUNC_ATTR_NONNULL_ALL
{
msglist_T *elem;
@@ -249,6 +249,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
elem = xmalloc(sizeof(msglist_T));
elem->msg = xstrdup(mesg);
+ elem->multiline = multiline;
elem->next = NULL;
elem->throw_msg = NULL;
*plist = elem;
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 6b3c426722..3ad3368900 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -1,6 +1,8 @@
#ifndef NVIM_EX_EVAL_DEFS_H
#define NVIM_EX_EVAL_DEFS_H
+#include <stdbool.h>
+
#include "nvim/pos.h"
/// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
@@ -41,11 +43,12 @@ enum {
/// message in the list. See cause_errthrow().
typedef struct msglist msglist_T;
struct msglist {
+ msglist_T *next; ///< next of several messages in a row
char *msg; ///< original message, allocated
char *throw_msg; ///< msg to throw: usually original one
char *sfile; ///< value from estack_sfile(), allocated
linenr_T slnum; ///< line number for "sfile"
- msglist_T *next; ///< next of several messages in a row
+ bool multiline; ///< whether this is a multiline message
};
/// The exception types.
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 5d7ddb657a..7ff3e0ec6e 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -105,7 +105,7 @@ void filemess(buf_T *buf, char *name, char *s, int attr)
// For further ones overwrite the previous one, reset msg_scroll before
// calling filemess().
msg_scroll_save = msg_scroll;
- if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
+ if (shortmess(SHM_OVERALL) && !msg_listdo_overwrite && !exiting && p_verbose == 0) {
msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
@@ -316,7 +316,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
}
- if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) {
+ if (((shortmess(SHM_OVER) && !msg_listdo_overwrite) || curbuf->b_help) && p_verbose == 0) {
msg_scroll = false; // overwrite previous file message
} else {
msg_scroll = true; // don't overwrite previous file message
@@ -459,7 +459,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
if (!silent) {
if (dir_of_file_exists(fname)) {
- filemess(curbuf, sfname, new_file_message(), 0);
+ filemess(curbuf, sfname, _("[New]"), 0);
} else {
filemess(curbuf, sfname, _("[New DIRECTORY]"), 0);
}
@@ -1718,7 +1718,7 @@ failed:
c = true;
}
if (read_no_eol_lnum) {
- msg_add_eol();
+ xstrlcat(IObuff, _("[noeol]"), IOSIZE);
c = true;
}
if (ff_error == EOL_DOS) {
@@ -2064,11 +2064,6 @@ static void check_marks_read(void)
curbuf->b_marks_read = true;
}
-char *new_file_message(void)
-{
- return shortmess(SHM_NEW) ? _("[New]") : _("[New File]");
-}
-
/// Set the name of the current buffer. Use when the buffer doesn't have a
/// name and a ":r" or ":w" command with a file name is used.
int set_rw_fname(char *fname, char *sfname)
@@ -2142,17 +2137,17 @@ bool msg_add_fileformat(int eol_type)
{
#ifndef USE_CRNL
if (eol_type == EOL_DOS) {
- xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"), IOSIZE);
+ xstrlcat(IObuff, _("[dos]"), IOSIZE);
return true;
}
#endif
if (eol_type == EOL_MAC) {
- xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"), IOSIZE);
+ xstrlcat(IObuff, _("[mac]"), IOSIZE);
return true;
}
#ifdef USE_CRNL
if (eol_type == EOL_UNIX) {
- xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"), IOSIZE);
+ xstrlcat(IObuff, _("[unix]"), IOSIZE);
return true;
}
#endif
@@ -2181,12 +2176,6 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars)
}
}
-/// Append message for missing line separator to IObuff.
-void msg_add_eol(void)
-{
- xstrlcat(IObuff, shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]"), IOSIZE);
-}
-
bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST
{
#if defined(__linux__) || defined(MSWIN)
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 1d5ba49301..d874e904d0 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -3344,8 +3344,13 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (kv_size(vt) > 0) {
assert(*text == NUL);
- for (size_t i = 0; i < kv_size(vt); i++) {
- char *new_text = concat_str(text, kv_A(vt, i).text);
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *new_text = next_virt_text_chunk(vt, &i, &attr);
+ if (new_text == NULL) {
+ break;
+ }
+ new_text = concat_str(text, new_text);
xfree(text);
text = new_text;
}
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index fa7f270172..2eeefab27d 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -17,6 +17,7 @@
#include "nvim/arabic.h"
#include "nvim/buffer_defs.h"
+#include "nvim/drawscreen.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
@@ -36,6 +37,15 @@
// Per-cell attributes
static size_t linebuf_size = 0;
+// Used to cache glyphs which doesn't fit an a sizeof(schar_T) length UTF-8 string.
+// Then it instead stores an index into glyph_cache.keys[] which is a flat char array.
+// The hash part is used by schar_from_buf() to quickly lookup glyphs which already
+// has been interned. schar_get() should used to convert a schar_T value
+// back to a string buffer.
+//
+// The maximum byte size of a glyph is MAX_SCHAR_SIZE (including the final NUL).
+static Set(glyph) glyph_cache = SET_INIT;
+
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the
@@ -56,25 +66,119 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
}
/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
-int schar_from_cc(char *p, int c, int u8cc[MAX_MCO])
+schar_T schar_from_cc(int c, int u8cc[MAX_MCO])
{
- int len = utf_char2bytes(c, p);
+ char buf[MAX_SCHAR_SIZE];
+ int len = utf_char2bytes(c, buf);
for (int i = 0; i < MAX_MCO; i++) {
if (u8cc[i] == 0) {
break;
}
- len += utf_char2bytes(u8cc[i], p + len);
+ len += utf_char2bytes(u8cc[i], buf + len);
+ }
+ buf[len] = 0;
+ return schar_from_buf(buf, (size_t)len);
+}
+
+schar_T schar_from_str(char *str)
+{
+ if (str == NULL) {
+ return 0;
+ }
+ return schar_from_buf(str, strlen(str));
+}
+
+/// @param buf need not be NUL terminated, but may not contain embedded NULs.
+///
+/// caller must ensure len < MAX_SCHAR_SIZE (not =, as NUL needs a byte)
+schar_T schar_from_buf(const char *buf, size_t len)
+{
+ assert(len < MAX_SCHAR_SIZE);
+ if (len <= 4) {
+ schar_T sc = 0;
+ memcpy((char *)&sc, buf, len);
+ return sc;
+ } else {
+ String str = { .data = (char *)buf, .size = len };
+
+ MHPutStatus status;
+ uint32_t idx = set_put_idx(glyph, &glyph_cache, str, &status);
+ assert(idx < 0xFFFFFF);
+#ifdef ORDER_BIG_ENDIAN
+ return idx + ((uint32_t)0xFF << 24);
+#else
+ return 0xFF + (idx << 8);
+#endif
}
- p[len] = 0;
- return len;
}
+/// Check if cache is full, and if it is, clear it.
+///
+/// This should normally only be called in update_screen()
+///
+/// @return true if cache was clered, and all your screen buffers now are hosed
+/// and you need to use UPD_CLEAR
+bool schar_cache_clear_if_full(void)
+{
+ // note: critical max is really (1<<24)-1. This gives us some marginal
+ // until next time update_screen() is called
+ if (glyph_cache.h.n_keys > (1<<21)) {
+ set_clear(glyph, &glyph_cache);
+ return true;
+ }
+ return false;
+}
+
+/// For testing. The condition in schar_cache_clear_force is hard to
+/// reach, so this function can be used to force a cache clear in a test.
+void schar_cache_clear_force(void)
+{
+ set_clear(glyph, &glyph_cache);
+ must_redraw = UPD_CLEAR;
+}
+
+bool schar_high(schar_T sc)
+{
+#ifdef ORDER_BIG_ENDIAN
+ return ((sc & 0xFF000000) == 0xFF000000);
+#else
+ return ((sc & 0xFF) == 0xFF);
+#endif
+}
+
+void schar_get(char *buf_out, schar_T sc)
+{
+ if (schar_high(sc)) {
+#ifdef ORDER_BIG_ENDIAN
+ uint32_t idx = sc & (0x00FFFFFF);
+#else
+ uint32_t idx = sc >> 8;
+#endif
+ if (idx >= glyph_cache.h.n_keys) {
+ abort();
+ }
+ xstrlcpy(buf_out, &glyph_cache.keys[idx], 32);
+ } else {
+ memcpy(buf_out, (char *)&sc, 4);
+ buf_out[4] = NUL;
+ }
+}
+
+/// @return ascii char or NUL if not ascii
+char schar_get_ascii(schar_T sc)
+{
+#ifdef ORDER_BIG_ENDIAN
+ return (!(sc & 0x80FFFFFF)) ? *(char *)&sc : NUL;
+#else
+ return (sc < 0x80) ? (char)sc : NUL;
+#endif
+}
/// clear a line in the grid starting at "off" until "width" characters
/// are cleared.
void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid)
{
for (int col = 0; col < width; col++) {
- schar_from_ascii(grid->chars[off + (size_t)col], ' ');
+ grid->chars[off + (size_t)col] = schar_from_ascii(' ');
}
int fill = valid ? 0 : -1;
(void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
@@ -93,7 +197,7 @@ bool grid_invalid_row(ScreenGrid *grid, int row)
static int line_off2cells(schar_T *line, size_t off, size_t max_off)
{
- return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
+ return (off + 1 < max_off && line[off + 1] == 0) ? 2 : 1;
}
/// Return number of display cells for char at grid->chars[off].
@@ -124,7 +228,7 @@ int grid_fix_col(ScreenGrid *grid, int col, int row)
col += coloff;
if (grid->chars != NULL && col > 0
- && grid->chars[grid->line_offset[row] + (size_t)col][0] == 0) {
+ && grid->chars[grid->line_offset[row] + (size_t)col] == 0) {
return col - 1 - coloff;
}
return col - coloff;
@@ -135,8 +239,7 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
{
char buf[MB_MAXBYTES + 1];
- buf[utf_char2bytes(c, buf)] = NUL;
- grid_puts(grid, buf, row, col, attr);
+ grid_puts(grid, buf, utf_char2bytes(c, buf), row, col, attr);
}
/// Get a single character directly from grid.chars into "bytes", which must
@@ -155,54 +258,84 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp)
if (attrp != NULL) {
*attrp = grid->attrs[off];
}
- schar_copy(bytes, grid->chars[off]);
+ schar_get(bytes, grid->chars[off]);
}
-/// put string '*text' on the window grid at position 'row' and 'col', with
-/// attributes 'attr', and update chars[] and attrs[].
-/// Note: only outputs within one row, message is truncated at grid boundary!
-/// Note: if grid, row and/or col is invalid, nothing is done.
-int grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr)
+static bool check_grid(ScreenGrid *grid, int row, int col)
{
- return grid_puts_len(grid, text, -1, row, col, attr);
+ grid_adjust(&grid, &row, &col);
+ // Safety check. The check for negative row and column is to fix issue
+ // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
+ if (grid->chars == NULL
+ || row >= grid->rows || row < 0
+ || col >= grid->cols || col < 0) {
+ return false;
+ }
+ return true;
}
-static ScreenGrid *put_dirty_grid = NULL;
-static int put_dirty_row = -1;
-static int put_dirty_first = INT_MAX;
-static int put_dirty_last = 0;
+/// put string 'text' on the window grid at position 'row' and 'col', with
+/// attributes 'attr', and update contents of 'grid'
+/// @param textlen length of string or -1 to use strlen(text)
+/// Note: only outputs within one row!
+int grid_puts(ScreenGrid *grid, const char *text, int textlen, int row, int col, int attr)
+{
+ if (!check_grid(grid, row, col)) {
+ if (rdb_flags & RDB_INVALID) {
+ abort();
+ }
+ return 0;
+ }
+
+ grid_line_start(grid, row);
+ int len = grid_line_puts(col, text, textlen, attr);
+ grid_line_flush(true);
+ return len;
+}
-/// Start a group of grid_puts_len calls that builds a single grid line.
+static ScreenGrid *grid_line_grid = NULL;
+static int grid_line_row = -1;
+static int grid_line_coloff = 0;
+static int grid_line_first = INT_MAX;
+static int grid_line_last = 0;
+static bool grid_line_was_invalid = false;
+
+/// Start a group of grid_line_puts calls that builds a single grid line.
///
-/// Must be matched with a grid_puts_line_flush call before moving to
+/// Must be matched with a grid_line_flush call before moving to
/// another line.
-void grid_puts_line_start(ScreenGrid *grid, int row)
+void grid_line_start(ScreenGrid *grid, int row)
{
- int col = 0; // unused
+ int col = 0;
grid_adjust(&grid, &row, &col);
- assert(put_dirty_row == -1);
- put_dirty_row = row;
- put_dirty_grid = grid;
+ assert(grid_line_row == -1);
+ grid_line_row = row;
+ grid_line_grid = grid;
+ grid_line_coloff = col;
+ // TODO(bfredl): ugly hackaround, will be fixed in STAGE 2
+ grid_line_was_invalid = grid != &default_grid && grid_invalid_row(grid, row);
}
-void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr)
+void grid_line_put_schar(int col, schar_T schar, int attr)
{
- assert(put_dirty_row == row);
- size_t off = grid->line_offset[row] + (size_t)col;
- if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar) || rdb_flags & RDB_NODELTA) {
- schar_copy(grid->chars[off], schar);
+ assert(grid_line_row >= 0);
+ ScreenGrid *grid = grid_line_grid;
+
+ size_t off = grid->line_offset[grid_line_row] + (size_t)col;
+ if (grid->attrs[off] != attr || grid->chars[off] != schar || rdb_flags & RDB_NODELTA) {
+ grid->chars[off] = schar;
grid->attrs[off] = attr;
- put_dirty_first = MIN(put_dirty_first, col);
+ grid_line_first = MIN(grid_line_first, col);
// TODO(bfredl): Y U NO DOUBLEWIDTH?
- put_dirty_last = MAX(put_dirty_last, col + 1);
+ grid_line_last = MAX(grid_line_last, col + 1);
}
grid->vcols[off] = -1;
}
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
-int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int col, int attr)
+int grid_line_puts(int col, const char *text, int textlen, int attr)
{
size_t off;
const char *ptr = text;
@@ -214,37 +347,15 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int
int prev_c = 0; // previous Arabic character
int pc, nc, nc1;
int pcc[MAX_MCO];
- bool do_flush = false;
-
- grid_adjust(&grid, &row, &col);
- // Safety check. The check for negative row and column is to fix issue
- // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
- if (grid->chars == NULL
- || row >= grid->rows || row < 0
- || col >= grid->cols || col < 0) {
- return 0;
- }
+ assert(grid_line_row >= 0);
+ ScreenGrid *grid = grid_line_grid;
+ int row = grid_line_row;
+ col += grid_line_coloff;
- if (put_dirty_row == -1) {
- grid_puts_line_start(grid, row);
- do_flush = true;
- } else {
- if (grid != put_dirty_grid || row != put_dirty_row) {
- abort();
- }
- }
off = grid->line_offset[row] + (size_t)col;
int start_col = col;
- // When drawing over the right half of a double-wide char clear out the
- // left half. Only needed in a terminal.
- if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
- // redraw the previous cell, make it empty
- put_dirty_first = -1;
- put_dirty_last = MAX(put_dirty_last, 1);
- }
-
max_off = grid->line_offset[row] + (size_t)grid->cols;
while (col < grid->cols
&& (len < 0 || (int)(ptr - text) < len)
@@ -293,10 +404,12 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int
}
schar_T buf;
- schar_from_cc(buf, u8c, u8cc);
+ // TODO(bfredl): why not just keep the original byte sequence. arabshape is
+ // an edge case, treat it as such..
+ buf = schar_from_cc(u8c, u8cc);
- int need_redraw = schar_cmp(grid->chars[off], buf)
- || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
+ int need_redraw = grid->chars[off] != buf
+ || (mbyte_cells == 2 && grid->chars[off + 1] != 0)
|| grid->attrs[off] != attr
|| exmode_active
|| rdb_flags & RDB_NODELTA;
@@ -320,20 +433,20 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int
// When at the start of the text and overwriting the right half of a
// two-cell character in the same grid, truncate that into a '>'.
- if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
- schar_from_ascii(grid->chars[off - 1], '>');
+ if (ptr == text && col > 0 && grid->chars[off] == 0) {
+ grid->chars[off - 1] = schar_from_ascii('>');
}
- schar_copy(grid->chars[off], buf);
+ grid->chars[off] = buf;
grid->attrs[off] = attr;
grid->vcols[off] = -1;
if (mbyte_cells == 2) {
- grid->chars[off + 1][0] = 0;
+ grid->chars[off + 1] = 0;
grid->attrs[off + 1] = attr;
grid->vcols[off + 1] = -1;
}
- put_dirty_first = MIN(put_dirty_first, col);
- put_dirty_last = MAX(put_dirty_last, col + mbyte_cells);
+ grid_line_first = MIN(grid_line_first, col);
+ grid_line_last = MAX(grid_line_last, col + mbyte_cells);
}
off += (size_t)mbyte_cells;
@@ -346,39 +459,61 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int
}
}
- if (do_flush) {
- grid_puts_line_flush(true);
- }
return col - start_col;
}
-/// End a group of grid_puts_len calls and send the screen buffer to the UI
-/// layer.
+void grid_line_fill(int start_col, int end_col, int c, int attr)
+{
+ ScreenGrid *grid = grid_line_grid;
+ size_t lineoff = grid->line_offset[grid_line_row];
+ start_col += grid_line_coloff;
+ end_col += grid_line_coloff;
+
+ schar_T sc = schar_from_char(c);
+ for (int col = start_col; col < end_col; col++) {
+ size_t off = lineoff + (size_t)col;
+ if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
+ grid->chars[off] = sc;
+ grid->attrs[off] = attr;
+ grid_line_first = MIN(grid_line_first, col);
+ grid_line_last = MAX(grid_line_last, col + 1);
+ }
+ grid->vcols[off] = -1;
+ }
+}
+
+/// End a group of grid_line_puts calls and send the screen buffer to the UI layer.
///
/// @param set_cursor Move the visible cursor to the end of the changed region.
/// This is a workaround for not yet refactored code paths
/// and shouldn't be used in new code.
-void grid_puts_line_flush(bool set_cursor)
+void grid_line_flush(bool set_cursor)
{
- assert(put_dirty_row != -1);
- if (put_dirty_first < put_dirty_last) {
+ assert(grid_line_row != -1);
+ if (grid_line_first < grid_line_last) {
+ // When drawing over the right half of a double-wide char clear out the
+ // left half. Only needed in a terminal.
+ if (grid_line_was_invalid && grid_line_first == 0) {
+ // redraw the previous cell, make it empty
+ grid_line_first = -1;
+ }
if (set_cursor) {
- ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
- MIN(put_dirty_last, put_dirty_grid->cols - 1));
+ ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row,
+ MIN(grid_line_last, grid_line_grid->cols - 1));
}
- if (!put_dirty_grid->throttled) {
- ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
- put_dirty_last, 0, false);
- } else if (put_dirty_grid->dirty_col) {
- if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
- put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
+ if (!grid_line_grid->throttled) {
+ ui_line(grid_line_grid, grid_line_row, grid_line_first, grid_line_last,
+ grid_line_last, 0, false);
+ } else if (grid_line_grid->dirty_col) {
+ if (grid_line_last > grid_line_grid->dirty_col[grid_line_row]) {
+ grid_line_grid->dirty_col[grid_line_row] = grid_line_last;
}
}
- put_dirty_first = INT_MAX;
- put_dirty_last = 0;
+ grid_line_first = INT_MAX;
+ grid_line_last = 0;
}
- put_dirty_row = -1;
- put_dirty_grid = NULL;
+ grid_line_row = -1;
+ grid_line_grid = NULL;
}
/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
@@ -415,11 +550,11 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
// double wide-char clear out the right half. Only needed in a
// terminal.
if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
- grid_puts_len(grid, " ", 1, row, start_col - 1, 0);
+ grid_puts(grid, " ", 1, row, start_col - 1, 0);
}
if (end_col < grid->cols
&& grid_fix_col(grid, end_col, row) != end_col) {
- grid_puts_len(grid, " ", 1, row, end_col, 0);
+ grid_puts(grid, " ", 1, row, end_col, 0);
}
// if grid was resized (in ext_multigrid mode), the UI has no redraw updates
@@ -429,12 +564,12 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
int dirty_last = 0;
int col = start_col;
- schar_from_char(sc, c1);
+ sc = schar_from_char(c1);
size_t lineoff = grid->line_offset[row];
for (col = start_col; col < end_col; col++) {
size_t off = lineoff + (size_t)col;
- if (schar_cmp(grid->chars[off], sc) || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
- schar_copy(grid->chars[off], sc);
+ if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
+ grid->chars[off] = sc;
grid->attrs[off] = attr;
if (dirty_first == INT_MAX) {
dirty_first = col;
@@ -443,15 +578,11 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
}
grid->vcols[off] = -1;
if (col == start_col) {
- schar_from_char(sc, c2);
+ sc = schar_from_char(c2);
}
}
if (dirty_last > dirty_first) {
- // TODO(bfredl): support a cleared suffix even with a batched line?
- if (put_dirty_row == row) {
- put_dirty_first = MIN(put_dirty_first, dirty_first);
- put_dirty_last = MAX(put_dirty_last, dirty_last);
- } else if (grid->throttled) {
+ if (grid->throttled) {
// Note: assumes msg_grid is the only throttled grid
assert(grid == &msg_grid);
int dirty = 0;
@@ -468,10 +599,6 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
}
}
-
- if (end_col == grid->cols) {
- grid->line_wraps[row] = false;
- }
}
}
@@ -483,11 +610,10 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_to, int cols)
{
return (cols > 0
- && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to])
+ && ((linebuf_char[off_from] != grid->chars[off_to]
|| linebuf_attr[off_from] != grid->attrs[off_to]
|| (line_off2cells(linebuf_char, off_from, off_from + (size_t)cols) > 1
- && schar_cmp(linebuf_char[off_from + 1],
- grid->chars[off_to + 1])))
+ && linebuf_char[off_from + 1] != grid->chars[off_to + 1]))
|| rdb_flags & RDB_NODELTA));
}
@@ -544,7 +670,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
if (wp->w_p_nu && wp->w_p_rnu) {
// do not overwrite the line number, change "123 text" to
// "123<<<xt".
- while (skip < max_off_from && ascii_isdigit(*linebuf_char[off])) {
+ while (skip < max_off_from && ascii_isdigit(schar_get_ascii(linebuf_char[off]))) {
off++;
skip++;
}
@@ -554,9 +680,9 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
if (line_off2cells(linebuf_char, off, max_off_from) > 1) {
// When the first half of a double-width character is
// overwritten, change the second half to a space.
- schar_from_ascii(linebuf_char[off + 1], ' ');
+ linebuf_char[off + 1] = schar_from_ascii(' ');
}
- schar_from_ascii(linebuf_char[off], '<');
+ linebuf_char[off] = schar_from_ascii('<');
linebuf_attr[off] = HL_ATTR(HLF_AT);
off++;
}
@@ -565,8 +691,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
if (rlflag) {
// Clear rest first, because it's left of the text.
if (clear_width > 0) {
- while (col <= endcol && grid->chars[off_to][0] == ' '
- && grid->chars[off_to][1] == NUL
+ while (col <= endcol && grid->chars[off_to] == schar_from_ascii(' ')
&& grid->attrs[off_to] == bg_attr) {
off_to++;
col++;
@@ -619,9 +744,9 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
clear_next = true;
}
- schar_copy(grid->chars[off_to], linebuf_char[off_from]);
+ grid->chars[off_to] = linebuf_char[off_from];
if (char_cells == 2) {
- schar_copy(grid->chars[off_to + 1], linebuf_char[off_from + 1]);
+ grid->chars[off_to + 1] = linebuf_char[off_from + 1];
}
grid->attrs[off_to] = linebuf_attr[off_from];
@@ -645,7 +770,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
if (clear_next) {
// Clear the second half of a double-wide character of which the left
// half was overwritten with a single-wide character.
- schar_from_ascii(grid->chars[off_to], ' ');
+ grid->chars[off_to] = schar_from_ascii(' ');
end_dirty++;
}
@@ -654,12 +779,10 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
// blank out the rest of the line
// TODO(bfredl): we could cache winline widths
while (col < clear_width) {
- if (grid->chars[off_to][0] != ' '
- || grid->chars[off_to][1] != NUL
+ if (grid->chars[off_to] != schar_from_ascii(' ')
|| grid->attrs[off_to] != bg_attr
|| rdb_flags & RDB_NODELTA) {
- grid->chars[off_to][0] = ' ';
- grid->chars[off_to][1] = NUL;
+ grid->chars[off_to] = schar_from_ascii(' ');
grid->attrs[off_to] = bg_attr;
if (start_dirty == -1) {
start_dirty = col;
@@ -675,12 +798,6 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle
}
}
- if (clear_width > 0 || wp->w_width != grid->cols) {
- // If we cleared after the end of the line, it did not wrap.
- // For vsplit, line wrapping is not possible.
- grid->line_wraps[row] = false;
- }
-
if (clear_end < end_dirty) {
clear_end = end_dirty;
}
@@ -704,14 +821,12 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
ngrid.vcols = xmalloc(ncells * sizeof(colnr_T));
memset(ngrid.vcols, -1, ncells * sizeof(colnr_T));
ngrid.line_offset = xmalloc((size_t)rows * sizeof(*ngrid.line_offset));
- ngrid.line_wraps = xmalloc((size_t)rows * sizeof(*ngrid.line_wraps));
ngrid.rows = rows;
ngrid.cols = columns;
for (new_row = 0; new_row < ngrid.rows; new_row++) {
ngrid.line_offset[new_row] = (size_t)new_row * (size_t)ngrid.cols;
- ngrid.line_wraps[new_row] = false;
grid_clear_line(&ngrid, ngrid.line_offset[new_row], columns, valid);
@@ -756,13 +871,11 @@ void grid_free(ScreenGrid *grid)
xfree(grid->attrs);
xfree(grid->vcols);
xfree(grid->line_offset);
- xfree(grid->line_wraps);
grid->chars = NULL;
grid->attrs = NULL;
grid->vcols = NULL;
grid->line_offset = NULL;
- grid->line_wraps = NULL;
}
/// Doesn't allow reinit, so must only be called by free_all_mem!
@@ -885,16 +998,13 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
j += line_count;
grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
} else {
j = end - 1 - i;
temp = (unsigned)grid->line_offset[j];
while ((j -= line_count) >= row) {
grid->line_offset[j + line_count] = grid->line_offset[j];
- grid->line_wraps[j + line_count] = grid->line_wraps[j];
}
grid->line_offset[j + line_count] = temp;
- grid->line_wraps[j + line_count] = false;
grid_clear_line(grid, temp, grid->cols, false);
}
}
@@ -933,17 +1043,14 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
j -= line_count;
grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
} else {
// whole width, moving the line pointers is faster
j = row + i;
temp = (unsigned)grid->line_offset[j];
while ((j += line_count) <= end - 1) {
grid->line_offset[j - line_count] = grid->line_offset[j];
- grid->line_wraps[j - line_count] = grid->line_wraps[j];
}
grid->line_offset[j - line_count] = temp;
- grid->line_wraps[j - line_count] = false;
grid_clear_line(grid, temp, grid->cols, false);
}
}
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
index 0db97345d1..9cdc6c6677 100644
--- a/src/nvim/grid.h
+++ b/src/nvim/grid.h
@@ -33,30 +33,25 @@ EXTERN colnr_T *linebuf_vcol INIT(= NULL);
// screen grid.
/// Put a ASCII character in a screen cell.
-static inline void schar_from_ascii(char *p, const char c)
-{
- p[0] = c;
- p[1] = 0;
-}
+///
+/// If `x` is a compile time constant, schar_from_ascii(x) will also be.
+/// But the specific value varies per plattform.
+#ifdef ORDER_BIG_ENDIAN
+# define schar_from_ascii(x) ((schar_T)((x) << 24))
+#else
+# define schar_from_ascii(x) ((schar_T)(x))
+#endif
/// Put a unicode character in a screen cell.
-static inline int schar_from_char(char *p, int c)
-{
- int len = utf_char2bytes(c, p);
- p[len] = NUL;
- return len;
-}
-
-/// compare the contents of two screen cells.
-static inline int schar_cmp(char *sc1, char *sc2)
-{
- return strncmp(sc1, sc2, sizeof(schar_T));
-}
-
-/// copy the contents of screen cell `sc2` into cell `sc1`
-static inline void schar_copy(char *sc1, char *sc2)
+static inline schar_T schar_from_char(int c)
{
- xstrlcpy(sc1, sc2, sizeof(schar_T));
+ schar_T sc = 0;
+ if (c >= 0x200000) {
+ // TODO(bfredl): this must NEVER happen, even if the file contained overlong sequences
+ c = 0xFFFD;
+ }
+ utf_char2bytes(c, (char *)&sc);
+ return sc;
}
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 4ad7d4cdb4..100d648263 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -9,9 +9,13 @@
#include "nvim/types.h"
#define MAX_MCO 6 // fixed value for 'maxcombine'
+// Includes final NUL. at least 4*(MAX_MCO+1)+1
+#define MAX_SCHAR_SIZE 32
-// The characters and attributes drawn on grids.
-typedef char schar_T[(MAX_MCO + 1) * 4 + 1];
+// if data[0] is 0xFF, then data[1..4] is a 24-bit index (in machine endianess)
+// otherwise it must be a UTF-8 string of length maximum 4 (no NUL when n=4)
+
+typedef uint32_t schar_T;
typedef int sattr_T;
enum {
@@ -40,15 +44,14 @@ enum {
/// attrs[] contains the highlighting attribute for each cell.
///
/// vcols[] contains the virtual columns in the line. -1 means not available
-/// (below last line), MAXCOL means after the end of the line.
+/// or before buffer text, MAXCOL means after the end of the line.
+/// -2 or -3 means in fold column and a mouse click should:
+/// -2: open a fold
+/// -3: close a fold
///
/// line_offset[n] is the offset from chars[], attrs[] and vcols[] for the start
/// of line 'n'. These offsets are in general not linear, as full screen scrolling
/// is implemented by rotating the offsets in the line_offset array.
-///
-/// line_wraps[] is an array of boolean flags indicating if the screen line
-/// wraps to the next line. It can only be true if a window occupies the entire
-/// screen width.
typedef struct ScreenGrid ScreenGrid;
struct ScreenGrid {
handle_T handle;
@@ -57,7 +60,6 @@ struct ScreenGrid {
sattr_T *attrs;
colnr_T *vcols;
size_t *line_offset;
- char *line_wraps;
// last column that was drawn (not cleared with the default background).
// only used when "throttled" is set. Not allocated by grid_alloc!
@@ -113,7 +115,7 @@ struct ScreenGrid {
bool comp_disabled;
};
-#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \
+#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \
false, 0, 0, NULL, false, true, 0, \
0, 0, 0, 0, 0, false }
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index df4bffdac3..3728db31d8 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -86,7 +86,7 @@ static int get_attr_entry(HlEntry entry)
}
retry: {}
- MhPutStatus status;
+ MHPutStatus status;
uint32_t k = set_put_idx(HlEntry, &attr_entries, entry, &status);
if (status == kMHExisting) {
return (int)k;
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 4f9677650f..f175a7abb0 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -24,6 +24,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/vars.h"
#include "nvim/ex_eval.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
@@ -359,6 +360,9 @@ int nlua_setvar(lua_State *lstate)
Error err = ERROR_INIT;
dictitem_T *di = dict_check_writable(dict, key, del, &err);
if (ERROR_SET(&err)) {
+ nlua_push_errstr(lstate, "%s", err.msg);
+ api_clear_error(&err);
+ lua_error(lstate);
return 0;
}
@@ -394,6 +398,15 @@ int nlua_setvar(lua_State *lstate)
di = tv_dict_item_alloc_len(key.data, key.size);
tv_dict_add(dict, di);
} else {
+ bool type_error = false;
+ if (dict == &vimvardict
+ && !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
+ tv_clear(&tv);
+ if (type_error) {
+ return luaL_error(lstate, "Setting v:%s to value with wrong type", key.data);
+ }
+ return 0;
+ }
if (watched) {
tv_copy(&di->di_tv, &oldtv);
}
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d9ca82784f..4b3244de12 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -341,7 +341,7 @@ int main(int argc, char **argv)
uint64_t rv = ui_client_start_server(params.argc, params.argv);
if (!rv) {
os_errmsg("Failed to start Nvim server!\n");
- getout(1);
+ os_exit(1);
}
ui_client_channel_id = rv;
}
@@ -659,6 +659,9 @@ void os_exit(int r)
if (ui_client_channel_id) {
ui_client_stop();
+ if (r == 0) {
+ r = ui_client_exit_status;
+ }
} else {
ui_flush();
ui_call_stop();
@@ -1579,6 +1582,7 @@ static void handle_tag(char *tagname)
// If the user doesn't want to edit the file then we quit here.
if (swap_exists_did_quit) {
+ ui_call_error_exit(1);
getout(1);
}
}
@@ -1722,6 +1726,7 @@ static void create_windows(mparm_T *parmp)
if (got_int || only_one_window()) {
// abort selected or quit and only one window
did_emsg = false; // avoid hit-enter prompt
+ ui_call_error_exit(1);
getout(1);
}
// We can't close the window, it would disturb what
@@ -1825,6 +1830,7 @@ static void edit_buffers(mparm_T *parmp, char *cwd)
if (got_int || only_one_window()) {
// abort selected and only one window
did_emsg = false; // avoid hit-enter prompt
+ ui_call_error_exit(1);
getout(1);
}
win_close(curwin, true, false);
@@ -2235,6 +2241,7 @@ static void usage(void)
static void check_swap_exists_action(void)
{
if (swap_exists_action == SEA_QUIT) {
+ ui_call_error_exit(1);
getout(1);
}
handle_swap_exists(NULL);
diff --git a/src/nvim/map.c b/src/nvim/map.c
index e1d0646083..54f1969df8 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -47,25 +47,6 @@ static inline uint32_t hash_cstr_t(const char *s)
#define equal_cstr_t strequal
-// when used as a key, String doesn't need to be NUL terminated,
-// and can also contain embedded NUL:s as part of the data.
-static inline uint32_t hash_String(String s)
-{
- uint32_t h = 0;
- for (size_t i = 0; i < s.size; i++) {
- h = (h << 5) - h + (uint8_t)s.data[i];
- }
- return h;
-}
-
-static inline bool equal_String(String a, String b)
-{
- if (a.size != b.size) {
- return false;
- }
- return memcmp(a.data, b.data, a.size) == 0;
-}
-
static inline uint32_t hash_HlEntry(HlEntry ae)
{
const uint8_t *data = (const uint8_t *)&ae;
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 23a5ea36a3..2d5517c552 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -18,6 +18,25 @@
typedef const char *cstr_t;
typedef void *ptr_t;
+// when used as a key, String doesn't need to be NUL terminated,
+// and can also contain embedded NUL:s as part of the data.
+static inline uint32_t hash_String(String s)
+{
+ uint32_t h = 0;
+ for (size_t i = 0; i < s.size; i++) {
+ h = (h << 5) - h + (uint8_t)s.data[i];
+ }
+ return h;
+}
+
+static inline bool equal_String(String a, String b)
+{
+ if (a.size != b.size) {
+ return false;
+ }
+ return memcmp(a.data, b.data, a.size) == 0;
+}
+
#define Set(type) Set_##type
#define Map(T, U) Map_##T##U
#define PMap(T) Map(T, ptr_t)
@@ -57,7 +76,7 @@ typedef enum {
kMHExisting = 0,
kMHNewKeyDidFit,
kMHNewKeyRealloc,
-} MhPutStatus;
+} MHPutStatus;
void mh_clear(MapHash *h);
void mh_realloc(MapHash *h, uint32_t n_min_buckets);
@@ -65,20 +84,22 @@ void mh_realloc(MapHash *h, uint32_t n_min_buckets);
// layer 1: key type specific defs
// This is all need for sets.
-#define KEY_DECLS(T) \
+#define MH_DECLS(T, K, K_query) \
typedef struct { \
MapHash h; \
- T *keys; \
+ K *keys; \
} Set(T); \
\
- uint32_t mh_find_bucket_##T(Set(T) *set, T key, bool put); \
- uint32_t mh_get_##T(Set(T) *set, T key); \
+ uint32_t mh_find_bucket_##T(Set(T) *set, K_query key, bool put); \
+ uint32_t mh_get_##T(Set(T) *set, K_query key); \
void mh_rehash_##T(Set(T) *set); \
- uint32_t mh_put_##T(Set(T) *set, T key, MhPutStatus *new); \
+ uint32_t mh_put_##T(Set(T) *set, K_query key, MHPutStatus *new); \
+
+#define KEY_DECLS(T) \
+ MH_DECLS(T, T, T) \
uint32_t mh_delete_##T(Set(T) *set, T *key); \
- \
static inline bool set_put_##T(Set(T) *set, T key, T **key_alloc) { \
- MhPutStatus status; \
+ MHPutStatus status; \
uint32_t k = mh_put_##T(set, key, &status); \
if (key_alloc) { \
*key_alloc = &set->keys[k]; \
@@ -120,6 +141,7 @@ void mh_realloc(MapHash *h, uint32_t n_min_buckets);
#define quasiquote(x, y) x##y
+MH_DECLS(glyph, char, String)
KEY_DECLS(int)
KEY_DECLS(cstr_t)
KEY_DECLS(ptr_t)
diff --git a/src/nvim/map_glyph_cache.c b/src/nvim/map_glyph_cache.c
new file mode 100644
index 0000000000..6dcbfe0532
--- /dev/null
+++ b/src/nvim/map_glyph_cache.c
@@ -0,0 +1,102 @@
+// 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
+
+// Specialized version of Set() where interned strings is stored in a compact,
+// NUL-separated char array.
+// `String key` lookup keys don't need to be NULL terminated, but they
+// must not contain embedded NUL:s. When reading a key from set->keys, they
+// are always NUL terminated, though. Thus, it is enough to store an index into
+// this array, and use strlen(), to retrive an interned key.
+
+#include "nvim/api/private/helpers.h"
+#include "nvim/map.h"
+
+uint32_t mh_find_bucket_glyph(Set(glyph) *set, String key, bool put)
+{
+ MapHash *h = &set->h;
+ uint32_t step = 0;
+ uint32_t mask = h->n_buckets - 1;
+ uint32_t k = hash_String(key);
+ uint32_t i = k & mask;
+ uint32_t last = i;
+ uint32_t site = put ? last : MH_TOMBSTONE;
+ while (!mh_is_empty(h, i)) {
+ if (mh_is_del(h, i)) {
+ if (site == last) {
+ site = i;
+ }
+ } else if (equal_String(cstr_as_string(&set->keys[h->hash[i] - 1]), key)) {
+ return i;
+ }
+ i = (i + (++step)) & mask;
+ if (i == last) {
+ abort();
+ }
+ }
+ if (site == last) {
+ site = i;
+ }
+ return site;
+}
+
+/// @return index into set->keys if found, MH_TOMBSTONE otherwise
+uint32_t mh_get_glyph(Set(glyph) *set, String key)
+{
+ if (set->h.n_buckets == 0) {
+ return MH_TOMBSTONE;
+ }
+ uint32_t idx = mh_find_bucket_glyph(set, key, false);
+ return (idx != MH_TOMBSTONE) ? set->h.hash[idx] - 1 : MH_TOMBSTONE;
+}
+
+void mh_rehash_glyph(Set(glyph) *set)
+{
+ // assume the format of set->keys, i e NUL terminated strings
+ for (uint32_t k = 0; k < set->h.n_keys; k += (uint32_t)strlen(&set->keys[k]) + 1) {
+ uint32_t idx = mh_find_bucket_glyph(set, cstr_as_string(&set->keys[k]), true);
+ // there must be tombstones when we do a rehash
+ if (!mh_is_empty((&set->h), idx)) {
+ abort();
+ }
+ set->h.hash[idx] = k + 1;
+ }
+ set->h.n_occupied = set->h.size = set->h.n_keys;
+}
+
+uint32_t mh_put_glyph(Set(glyph) *set, String key, MHPutStatus *new)
+{
+ MapHash *h = &set->h;
+ // Might rehash ahead of time if "key" already existed. But it was
+ // going to happen soon anyway.
+ if (h->n_occupied >= h->upper_bound) {
+ mh_realloc(h, h->n_buckets + 1);
+ mh_rehash_glyph(set);
+ }
+
+ uint32_t idx = mh_find_bucket_glyph(set, key, true);
+
+ if (mh_is_either(h, idx)) {
+ h->size++;
+ h->n_occupied++;
+
+ uint32_t size = (uint32_t)key.size + 1; // NUL takes space
+ uint32_t pos = h->n_keys;
+ h->n_keys += size;
+ if (h->n_keys > h->keys_capacity) {
+ h->keys_capacity = MAX(h->keys_capacity * 2, 64);
+ set->keys = xrealloc(set->keys, h->keys_capacity * sizeof(char));
+ *new = kMHNewKeyRealloc;
+ } else {
+ *new = kMHNewKeyDidFit;
+ }
+ memcpy(&set->keys[pos], key.data, key.size);
+ set->keys[pos + key.size] = NUL;
+ h->hash[idx] = pos + 1;
+ return pos;
+ } else {
+ *new = kMHExisting;
+ uint32_t pos = h->hash[idx] - 1;
+ assert(equal_String(cstr_as_string(&set->keys[pos]), key));
+ return pos;
+ }
+}
diff --git a/src/nvim/map_key_impl.c.h b/src/nvim/map_key_impl.c.h
index 7e7b2f74fe..4d060f5fb8 100644
--- a/src/nvim/map_key_impl.c.h
+++ b/src/nvim/map_key_impl.c.h
@@ -80,7 +80,7 @@ void KEY_NAME(mh_rehash_)(SET_TYPE *set)
/// if new item, indicates if keys[] was resized.
///
/// @return keys index
-uint32_t KEY_NAME(mh_put_)(SET_TYPE *set, KEY_TYPE key, MhPutStatus *new)
+uint32_t KEY_NAME(mh_put_)(SET_TYPE *set, KEY_TYPE key, MHPutStatus *new)
{
MapHash *h = &set->h;
// Might rehash ahead of time if "key" already existed. But it was
diff --git a/src/nvim/map_value_impl.c.h b/src/nvim/map_value_impl.c.h
index fffda280f0..8f07bd8fa7 100644
--- a/src/nvim/map_value_impl.c.h
+++ b/src/nvim/map_value_impl.c.h
@@ -28,7 +28,7 @@ VALUE_TYPE *MAP_NAME(map_ref_)(MAP_TYPE *map, KEY_TYPE key, KEY_TYPE **key_alloc
VALUE_TYPE *MAP_NAME(map_put_ref_)(MAP_TYPE *map, KEY_TYPE key, KEY_TYPE **key_alloc,
bool *new_item)
{
- MhPutStatus status;
+ MHPutStatus status;
uint32_t k = KEY_NAME(mh_put_)(&map->set, key, &status);
if (status != kMHExisting) {
if (status == kMHNewKeyRealloc) {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 81760dd017..dba4dba600 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -640,7 +640,7 @@ int emsg_not_now(void)
return false;
}
-static bool emsg_multiline(const char *s, bool multiline)
+bool emsg_multiline(const char *s, bool multiline)
{
int attr;
bool ignore = false;
@@ -663,7 +663,7 @@ static bool emsg_multiline(const char *s, bool multiline)
// be found, the message will be displayed later on.) "ignore" is set
// when the message should be ignored completely (used for the
// interrupt message).
- if (cause_errthrow(s, severe, &ignore)) {
+ if (cause_errthrow(s, multiline, severe, &ignore)) {
if (!ignore) {
did_emsg++;
}
@@ -1907,10 +1907,13 @@ void msg_prt_line(const char *s, int list)
continue;
} else {
attr = 0;
- c = (unsigned char)(*s++);
- in_multispace = c == ' ' && ((col > 0 && s[-2] == ' ') || *s == ' ');
- if (!in_multispace) {
- multispace_pos = 0;
+ c = (uint8_t)(*s++);
+ if (list) {
+ in_multispace = c == ' ' && (*s == ' '
+ || (col > 0 && s[-2] == ' '));
+ if (!in_multispace) {
+ multispace_pos = 0;
+ }
}
if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
// tab amount depends on current column
@@ -1950,7 +1953,7 @@ void msg_prt_line(const char *s, int list)
// the same in plain text.
attr = HL_ATTR(HLF_0);
} else if (c == ' ') {
- if (list && lead != NULL && s <= lead && in_multispace
+ if (lead != NULL && s <= lead && in_multispace
&& curwin->w_p_lcs_chars.leadmultispace != NULL) {
c = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++];
if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
@@ -1963,7 +1966,7 @@ void msg_prt_line(const char *s, int list)
} else if (trail != NULL && s > trail) {
c = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_0);
- } else if (list && in_multispace
+ } else if (in_multispace
&& curwin->w_p_lcs_chars.multispace != NULL) {
c = curwin->w_p_lcs_chars.multispace[multispace_pos++];
if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
@@ -2004,7 +2007,7 @@ static const char *screen_puts_mbyte(const char *s, int l, int attr)
return s;
}
- grid_puts_len(&msg_grid_adj, s, l, msg_row, msg_col, attr);
+ grid_puts(&msg_grid_adj, s, l, msg_row, msg_col, attr);
if (cmdmsg_rl) {
msg_col -= cw;
if (msg_col == 0) {
@@ -2705,7 +2708,7 @@ static void t_puts(int *t_col, const char *t_s, const char *s, int attr)
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
// Output postponed text.
msg_didout = true; // Remember that line is not empty.
- grid_puts_len(&msg_grid_adj, t_s, (int)(s - t_s), msg_row, msg_col, attr);
+ grid_puts(&msg_grid_adj, t_s, (int)(s - t_s), msg_row, msg_col, attr);
msg_col += *t_col;
*t_col = 0;
// If the string starts with a composing character don't increment the
@@ -3091,9 +3094,9 @@ void msg_moremsg(int full)
char *s = _("-- More --");
attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
- grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
+ grid_puts(&msg_grid_adj, s, -1, Rows - 1, 0, attr);
if (full) {
- grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
+ grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), -1,
Rows - 1, vim_strsize(s), attr);
}
}
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 191d3b8da7..78cd5b7d0e 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -68,6 +68,8 @@ EXTERN int msg_scrolled_at_flush INIT(= 0);
EXTERN int msg_grid_scroll_discount INIT(= 0);
+EXTERN int msg_listdo_overwrite INIT(= 0);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index b8c80cadf5..9b09a3fdf3 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1849,74 +1849,66 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
- int mouse_char = ' ';
- int max_row = Rows;
- int max_col = Columns;
- bool multigrid = ui_has(kUIMultigrid);
- colnr_T col_from_screen = -1;
+ // XXX: this doesn't change click_grid if it is 1, even with multigrid
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
- if (wp && multigrid) {
- max_row = wp->w_grid_alloc.rows;
- max_col = wp->w_grid_alloc.cols;
- }
-
- if (wp && mouse_row >= 0 && mouse_row < max_row
- && mouse_col >= 0 && mouse_col < max_col) {
- ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
- int fdc = win_fdccol_count(wp);
- int use_row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
- int use_col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
-
- if (gp->chars != NULL) {
- const size_t off = gp->line_offset[use_row] + (size_t)use_col;
-
- // Only use vcols[] after the window was redrawn. Mainly matters
- // for tests, a user would not click before redrawing.
- if (wp->w_redr_type == 0) {
- col_from_screen = gp->vcols[off];
- }
-
- if (col_from_screen == MAXCOL) {
- // When clicking after end of line, still need to set correct curswant
- size_t off_l = gp->line_offset[use_row];
- if (gp->vcols[off_l] < MAXCOL) {
- // Binary search to find last char in line
- size_t off_r = off;
- while (off_l < off_r) {
- size_t off_m = (off_l + off_r + 1) / 2;
- if (gp->vcols[off_m] < MAXCOL) {
- off_l = off_m;
- } else {
- off_r = off_m - 1;
- }
- }
- *vcolp = gp->vcols[off_r] + (int)(off - off_r);
+ // Only use vcols[] after the window was redrawn. Mainly matters
+ // for tests, a user would not click before redrawing.
+ if (wp == NULL || wp->w_redr_type != 0) {
+ return;
+ }
+ ScreenGrid *gp = &wp->w_grid;
+ int start_row = 0;
+ int start_col = 0;
+ grid_adjust(&gp, &start_row, &start_col);
+ if (gp->handle != click_grid || gp->chars == NULL) {
+ return;
+ }
+ click_row += start_row;
+ click_col += start_col;
+ if (click_row < 0 || click_row >= gp->rows
+ || click_col < 0 || click_col >= gp->cols) {
+ return;
+ }
+
+ const size_t off = gp->line_offset[click_row] + (size_t)click_col;
+ colnr_T col_from_screen = gp->vcols[off];
+
+ if (col_from_screen == MAXCOL) {
+ // When clicking after end of line, still need to set correct curswant
+ size_t off_l = gp->line_offset[click_row] + (size_t)start_col;
+ if (gp->vcols[off_l] < MAXCOL) {
+ // Binary search to find last char in line
+ size_t off_r = off;
+ while (off_l < off_r) {
+ size_t off_m = (off_l + off_r + 1) / 2;
+ if (gp->vcols[off_m] < MAXCOL) {
+ off_l = off_m;
} else {
- // Shouldn't normally happen
- *vcolp = MAXCOL;
+ off_r = off_m - 1;
}
- } else if (col_from_screen >= 0) {
- // Use the virtual column from vcols[], it is accurate also after
- // concealed characters.
- *vcolp = col_from_screen;
}
-
- // Remember the character under the mouse, might be one of foldclose or
- // foldopen fillchars in the fold column.
- mouse_char = utf_ptr2char((char *)gp->chars[off]);
- }
-
- // Check for position outside of the fold column.
- if (wp->w_p_rl ? click_col < wp->w_width_inner - fdc :
- click_col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
- mouse_char = ' ';
+ colnr_T eol_vcol = gp->vcols[off_r];
+ assert(eol_vcol < MAXCOL);
+ if (eol_vcol < 0) {
+ // Empty line or whole line before w_leftcol,
+ // with columns before buffer text
+ eol_vcol = wp->w_leftcol - 1;
+ }
+ *vcolp = eol_vcol + (int)(off - off_r);
+ } else {
+ // Empty line or whole line before w_leftcol
+ *vcolp = click_col - start_col + wp->w_leftcol;
}
+ } else if (col_from_screen >= 0) {
+ // Use the virtual column from vcols[], it is accurate also after
+ // concealed characters.
+ *vcolp = col_from_screen;
}
- if (wp && mouse_char == wp->w_p_fcs_chars.foldclosed) {
+ if (col_from_screen == -2) {
*flagsp |= MOUSE_FOLD_OPEN;
- } else if (mouse_char != ' ') {
+ } else if (col_from_screen == -3) {
*flagsp |= MOUSE_FOLD_CLOSE;
}
}
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index c3b1022db2..37e32729cc 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -9,6 +9,7 @@
#include "mpack/conv.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/grid.h"
#include "nvim/macros.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel_defs.h"
@@ -497,13 +498,13 @@ redo:
if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) {
g->clear_width = repeat;
} else {
+ schar_T sc = schar_from_buf(cellbuf, cellsize);
for (int r = 0; r < repeat; r++) {
if (g->coloff >= (int)grid_line_buf_size) {
p->state = -1;
return false;
}
- memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize);
- grid_line_buf_char[g->coloff][cellsize] = NUL;
+ grid_line_buf_char[g->coloff] = sc;
grid_line_buf_attr[g->coloff++] = g->cur_attr;
}
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 4280d62a95..309b6e2568 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2078,18 +2078,16 @@ static void display_showcmd(void)
msg_grid_validate();
int showcmd_row = Rows - 1;
- grid_puts_line_start(&msg_grid_adj, showcmd_row);
+ grid_line_start(&msg_grid_adj, showcmd_row);
if (!showcmd_is_clear) {
- grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col,
- HL_ATTR(HLF_MSG));
+ grid_line_puts(sc_col, showcmd_buf, -1, HL_ATTR(HLF_MSG));
}
// clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
- grid_puts(&msg_grid_adj, (char *)" " + len, showcmd_row,
- sc_col + len, HL_ATTR(HLF_MSG));
+ grid_line_puts(sc_col + len, (char *)" " + len, -1, HL_ATTR(HLF_MSG));
- grid_puts_line_flush(false);
+ grid_line_flush(false);
}
/// When "check" is false, prepare for commands that scroll the window.
@@ -4513,7 +4511,7 @@ static void nv_replace(cmdarg_T *cap)
// Visual mode "r"
if (VIsual_active) {
if (got_int) {
- reset_VIsual();
+ got_int = false;
}
if (had_ctrl_v) {
// Use a special (negative) number to make a difference between a
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ae4e7c1fda..c34999ed32 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1915,6 +1915,9 @@ bool parse_winhl_opt(win_T *wp)
char *commap = xstrchrnul(hi, ',');
size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
+ if (hl_id == 0) {
+ return false;
+ }
int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
HlAttrs attrs = HLATTRS_INIT;
@@ -1967,6 +1970,10 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
curbuf->b_p_script_ctx[indir & PV_MASK] = last_set;
} else if (indir & PV_WIN) {
curwin->w_p_script_ctx[indir & PV_MASK] = last_set;
+ if (both) {
+ // also setting the "all buffers" value
+ curwin->w_allbuf_opt.wo_script_ctx[indir & PV_MASK] = last_set;
+ }
}
}
}
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 14f29682e1..317bc989e5 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -240,11 +240,7 @@ typedef enum {
enum {
SHM_RO = 'r', ///< Readonly.
SHM_MOD = 'm', ///< Modified.
- SHM_FILE = 'f', ///< (file 1 of 2)
- SHM_LAST = 'i', ///< Last line incomplete.
- SHM_TEXT = 'x', ///< tx instead of textmode.
SHM_LINES = 'l', ///< "L" instead of "lines".
- SHM_NEW = 'n', ///< "[New]" instead of "[New file]".
SHM_WRI = 'w', ///< "[w]" instead of "written".
SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS.
SHM_WRITE = 'W', ///< Don't use "written" at all.
@@ -260,11 +256,10 @@ enum {
SHM_RECORDING = 'q', ///< Short recording message.
SHM_FILEINFO = 'F', ///< No file info messages.
SHM_SEARCHCOUNT = 'S', ///< No search stats: '[1/10]'
- SHM_LEN = 30, ///< Max length of all flags together plus a NUL character.
};
/// Represented by 'a' flag.
#define SHM_ALL_ABBREVIATIONS ((char[]) { \
- SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
+ SHM_RO, SHM_MOD, SHM_LINES, SHM_WRI, \
0 })
// characters for p_go:
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 3221e5b6e9..429a70eb38 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2790,8 +2790,8 @@ return {
and the value of that item:
item default Used for ~
- stl ' ' or '^' statusline of the current window
- stlnc ' ' or '=' statusline of the non-current windows
+ stl ' ' statusline of the current window
+ stlnc ' ' statusline of the non-current windows
wbr ' ' window bar
horiz '─' or '-' horizontal separators |:split|
horizup '┴' or '-' upwards facing horizontal separator
@@ -2810,9 +2810,7 @@ return {
eob '~' empty lines at the end of a buffer
lastline '@' 'display' contains lastline/truncate
- Any one that is omitted will fall back to the default. For "stl" and
- "stlnc" the space will be used when there is highlighting, '^' or '='
- otherwise.
+ Any one that is omitted will fall back to the default.
Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and
"verthoriz" are only used when 'laststatus' is 3, since only vertical
@@ -3938,9 +3936,8 @@ return {
cb = 'did_set_ignorecase',
defaults = { if_true = false },
desc = [=[
- Ignore case in search patterns. Also used when searching in the tags
- file.
- Also see 'smartcase' and 'tagcase'.
+ Ignore case in search patterns, completion, and when searching the tags file.
+ See also 'smartcase' and 'tagcase'.
Can be overruled by using "\c" or "\C" in the pattern, see
|/ignorecase|.
]=],
@@ -4444,12 +4441,11 @@ return {
deny_duplicates = true,
desc = [=[
List of words that change the behavior of the |jumplist|.
- stack Make the jumplist behave like the tagstack or like a
- web browser. Relative location of entries in the
- jumplist is preserved at the cost of discarding
- subsequent entries when navigating backwards in the
- jumplist and then jumping to a location.
- |jumplist-stack|
+ stack Make the jumplist behave like the tagstack.
+ Relative location of entries in the jumplist is
+ preserved at the cost of discarding subsequent entries
+ when navigating backwards in the jumplist and then
+ jumping to a location. |jumplist-stack|
view When moving through the jumplist, |changelist|,
|alternate-file| or using |mark-motions| try to
@@ -7243,23 +7239,17 @@ return {
{
abbreviation = 'shm',
cb = 'did_set_shortmess',
- defaults = { if_true = 'filnxtToOCF' },
+ defaults = { if_true = 'ltToOCF' },
desc = [=[
This option helps to avoid all the |hit-enter| prompts caused by file
messages, for example with CTRL-G, and to avoid some other messages.
It is a list of flags:
flag meaning when present ~
- f use "(3 of 5)" instead of "(file 3 of 5)" *shm-f*
- i use "[noeol]" instead of "[Incomplete last line]" *shm-i*
l use "999L, 888B" instead of "999 lines, 888 bytes" *shm-l*
m use "[+]" instead of "[Modified]" *shm-m*
- n use "[New]" instead of "[New File]" *shm-n*
r use "[RO]" instead of "[readonly]" *shm-r*
w use "[w]" instead of "written" for file write message *shm-w*
and "[a]" instead of "appended" for ':w >> file' command
- x use "[dos]" instead of "[dos format]", "[unix]" *shm-x*
- instead of "[unix format]" and "[mac]" instead of "[mac
- format]"
a all of the above abbreviations *shm-a*
o overwrite message for writing a file with subsequent *shm-o*
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 17afd10ee3..797c3cb554 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -69,8 +69,6 @@ static const char e_backupext_and_patchmode_are_equal[]
= N_("E589: 'backupext' and 'patchmode' are equal");
static const char e_showbreak_contains_unprintable_or_wide_character[]
= N_("E595: 'showbreak' contains unprintable or wide character");
-static const char e_internal_error_shortmess_too_long[]
- = N_("E1336: Internal error: shortmess too long");
static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
@@ -134,11 +132,12 @@ static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelt
static char *(p_sloc_values[]) = { "last", "statusline", "tabline", NULL };
/// All possible flags for 'shm'.
-static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW,
+/// the literal chars before 0 are removed flags. these are safely ignored
+static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL,
SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO,
SHM_COMPLETIONMENU, SHM_COMPLETIONSCAN, SHM_RECORDING, SHM_FILEINFO,
- SHM_SEARCHCOUNT, 0, };
+ SHM_SEARCHCOUNT, 'n', 'f', 'x', 'i', 0, };
/// After setting various option values: recompute variables that depend on
/// option values.
@@ -151,13 +150,13 @@ void didset_string_options(void)
(void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
(void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
(void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
+ (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
(void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
(void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
(void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
(void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
(void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
(void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
- (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
(void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
}
@@ -2269,36 +2268,6 @@ int check_ff_value(char *p)
return check_opt_strings(p, p_ff_values, false);
}
-static char shm_buf[SHM_LEN];
-static int set_shm_recursive = 0;
-
-/// Save the actual shortmess Flags and clear them temporarily to avoid that
-/// file messages overwrites any output from the following commands.
-///
-/// Caller must make sure to first call save_clear_shm_value() and then
-/// restore_shm_value() exactly the same number of times.
-void save_clear_shm_value(void)
-{
- if (strlen(p_shm) >= SHM_LEN) {
- iemsg(e_internal_error_shortmess_too_long);
- return;
- }
-
- if (++set_shm_recursive == 1) {
- STRCPY(shm_buf, p_shm);
- set_option_value_give_err("shm", STATIC_CSTR_AS_OPTVAL(""), 0);
- }
-}
-
-/// Restore the shortmess Flags set from the save_clear_shm_value() function.
-void restore_shm_value(void)
-{
- if (--set_shm_recursive == 0) {
- set_option_value_give_err("shm", CSTR_AS_OPTVAL(shm_buf), 0);
- memset(shm_buf, 0, SHM_LEN);
- }
-}
-
static const char e_conflicts_with_value_of_listchars[]
= N_("E834: Conflicts with value of 'listchars'");
static const char e_conflicts_with_value_of_fillchars[]
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
index abeb020645..763d30d4a2 100644
--- a/src/nvim/os/pty_process_win.c
+++ b/src/nvim/os/pty_process_win.c
@@ -256,9 +256,9 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
QUEUE_FOREACH(q, &args_q, {
ArgNode *arg_node = QUEUE_DATA(q, ArgNode, node);
xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len);
+ QUEUE_REMOVE(q);
xfree(arg_node->arg);
xfree(arg_node);
- QUEUE_REMOVE(q);
if (!QUEUE_EMPTY(&args_q)) {
xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len);
}
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index fe31c70d5d..488c893bd8 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -507,14 +507,14 @@ void pum_redraw(void)
const int *const attrs = (idx == pum_selected) ? attrsSel : attrsNorm;
int attr = attrs[0]; // start with "word" highlight
- grid_puts_line_start(&pum_grid, row);
+ grid_line_start(&pum_grid, row);
// prepend a space if there is room
if (extra_space) {
if (pum_rl) {
- grid_putchar(&pum_grid, ' ', row, col_off + 1, attr);
+ grid_line_puts(col_off + 1, " ", 1, attr);
} else {
- grid_putchar(&pum_grid, ' ', row, col_off - 1, attr);
+ grid_line_puts(col_off - 1, " ", 1, attr);
}
}
@@ -580,13 +580,13 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&pum_grid, rt, (int)strlen(rt), row, grid_col - size + 1, attr);
+ grid_line_puts(grid_col - size + 1, rt, -1, attr);
xfree(rt_start);
xfree(st);
grid_col -= width;
} else {
- // use grid_puts_len() to truncate the text
- grid_puts(&pum_grid, st, row, grid_col, attr);
+ // use grid_line_puts() to truncate the text
+ grid_line_puts(grid_col, st, -1, attr);
xfree(st);
grid_col += width;
}
@@ -597,11 +597,10 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, " ", 2, row, grid_col - 1,
- attr);
+ grid_line_puts(grid_col - 1, " ", 2, attr);
grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, " ", 2, row, grid_col, attr);
+ grid_line_puts(grid_col, " ", 2, attr);
grid_col += 2;
}
totwidth += 2;
@@ -632,37 +631,31 @@ void pum_redraw(void)
}
if (pum_rl) {
- grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
- grid_col + 1, ' ', ' ', attr);
+ grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, ' ', attr);
grid_col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&pum_grid, row, row + 1, grid_col,
- col_off + pum_base_width + n, ' ', ' ', attr);
+ grid_line_fill(grid_col, col_off + pum_base_width + n, ' ', attr);
grid_col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (pum_rl) {
- grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, grid_col + 1,
- ' ', ' ', attr);
+ grid_line_fill(col_off - pum_width + 1, grid_col + 1, ' ', attr);
} else {
- grid_fill(&pum_grid, row, row + 1, grid_col, col_off + pum_width, ' ', ' ',
- attr);
+ grid_line_fill(grid_col, col_off + pum_width, ' ', attr);
}
if (pum_scrollbar > 0) {
if (pum_rl) {
- grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_height
- ? attr_thumb : attr_scroll);
+ grid_line_puts(col_off - pum_width, " ", 1,
+ i >= thumb_pos && i < thumb_pos + thumb_height ? attr_thumb : attr_scroll);
} else {
- grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_height
- ? attr_thumb : attr_scroll);
+ grid_line_puts(col_off + pum_width, " ", 1,
+ i >= thumb_pos && i < thumb_pos + thumb_height ? attr_thumb : attr_scroll);
}
}
- grid_puts_line_flush(false);
+ grid_line_flush(false);
row++;
}
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 1d0a987780..14c7e56e97 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -59,11 +59,7 @@
#define un_Magic(x) ((x) + 256)
#define is_Magic(x) ((x) < 0)
-// We should define ftpr as a pointer to a function returning a pointer to
-// a function returning a pointer to a function ...
-// This is impossible, so we declare a pointer to a function returning a
-// pointer to a function returning void. This should work for all compilers.
-typedef void (*(*fptr_T)(int *, int))(void);
+typedef void (*fptr_T)(int *, int);
static int no_Magic(int x)
{
@@ -1494,34 +1490,14 @@ static inline char *cstrchr(const char *const s, const int c)
// regsub stuff //
////////////////////////////////////////////////////////////////
-// This stuff below really confuses cc on an SGI -- webb
-
-static fptr_T do_upper(int *d, int c)
+static void do_upper(int *d, int c)
{
*d = mb_toupper(c);
-
- return (fptr_T)NULL;
-}
-
-static fptr_T do_Upper(int *d, int c)
-{
- *d = mb_toupper(c);
-
- return (fptr_T)do_Upper;
}
-static fptr_T do_lower(int *d, int c)
+static void do_lower(int *d, int c)
{
*d = mb_tolower(c);
-
- return (fptr_T)NULL;
-}
-
-static fptr_T do_Lower(int *d, int c)
-{
- *d = mb_tolower(c);
-
- return (fptr_T)do_Lower;
}
/// regtilde(): Replace tildes in the pattern by the old pattern.
@@ -1886,16 +1862,16 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
} else if (vim_strchr("uUlLeE", (uint8_t)(*src))) {
switch (*src++) {
case 'u':
- func_one = (fptr_T)do_upper;
+ func_one = do_upper;
continue;
case 'U':
- func_all = (fptr_T)do_Upper;
+ func_all = do_upper;
continue;
case 'l':
- func_one = (fptr_T)do_lower;
+ func_one = do_lower;
continue;
case 'L':
- func_all = (fptr_T)do_Lower;
+ func_all = do_lower;
continue;
case 'e':
case 'E':
@@ -1954,11 +1930,13 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
} else {
c = utf_ptr2char(src - 1);
}
+
// Write to buffer, if copy is set.
if (func_one != NULL) {
- func_one = (fptr_T)(func_one(&cc, c));
+ func_one(&cc, c);
+ func_one = NULL;
} else if (func_all != NULL) {
- func_all = (fptr_T)(func_all(&cc, c));
+ func_all(&cc, c);
} else {
// just copy
cc = c;
@@ -2061,11 +2039,10 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
c = utf_ptr2char(s);
if (func_one != (fptr_T)NULL) {
- // Turbo C complains without the typecast
- func_one = (fptr_T)(func_one(&cc, c));
+ func_one(&cc, c);
+ func_one = NULL;
} else if (func_all != (fptr_T)NULL) {
- // Turbo C complains without the typecast
- func_all = (fptr_T)(func_all(&cc, c));
+ func_all(&cc, c);
} else { // just copy
cc = c;
}
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 30c98a2f1e..4d318f8c59 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -153,13 +153,13 @@ void win_redr_status(win_T *wp)
row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
col = is_stl_global ? 0 : wp->w_wincol;
- int width = grid_puts(&default_grid, p, row, col, attr);
+ int width = grid_puts(&default_grid, p, -1, row, col, attr);
grid_fill(&default_grid, row, row + 1, width + col,
this_ru_col + col, fillchar, fillchar, attr);
if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
&& this_ru_col - len > (int)(strlen(NameBuff) + 1)) {
- grid_puts(&default_grid, NameBuff, row,
+ grid_puts(&default_grid, NameBuff, -1, row,
(int)((size_t)this_ru_col - strlen(NameBuff) - 1), attr);
}
@@ -170,8 +170,8 @@ void win_redr_status(win_T *wp)
const int sc_width = MIN(10, this_ru_col - len - 2);
if (sc_width > 0) {
- grid_puts_len(&default_grid, showcmd_buf, sc_width, row,
- wp->w_wincol + this_ru_col - sc_width - 1, attr);
+ grid_puts(&default_grid, showcmd_buf, sc_width, row,
+ wp->w_wincol + this_ru_col - sc_width - 1, attr);
}
}
}
@@ -419,7 +419,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
int start_col = col;
// Draw each snippet with the specified highlighting.
- grid_puts_line_start(grid, row);
+ grid_line_start(grid, row);
int curattr = attr;
char *p = buf;
@@ -427,7 +427,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
int textlen = (int)(hltab[n].start - p);
// Make all characters printable.
size_t tsize = transstr_buf(p, textlen, transbuf, sizeof transbuf, true);
- col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr);
+ col += grid_line_puts(col, transbuf, (int)tsize, curattr);
p = hltab[n].start;
if (hltab[n].userhl == 0) {
@@ -442,13 +442,13 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
}
// Make sure to use an empty string instead of p, if p is beyond buf + len.
size_t tsize = transstr_buf(p >= buf + len ? "" : p, -1, transbuf, sizeof transbuf, true);
- col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr);
+ col += grid_line_puts(col, transbuf, (int)tsize, curattr);
int maxcol = start_col + maxwidth;
// fill up with "fillchar"
- grid_fill(grid, row, row + 1, col, maxcol, fillchar, fillchar, curattr);
+ grid_line_fill(col, maxcol, fillchar, curattr);
- grid_puts_line_flush(false);
+ grid_line_flush(false);
// Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
// in the tab page line, status line or window bar
@@ -618,7 +618,7 @@ void win_redr_ruler(win_T *wp)
}
ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
- grid_puts(grid, buffer, row, this_ru_col + off, attr);
+ grid_puts(grid, buffer, -1, row, this_ru_col + off, attr);
grid_fill(grid, row, row + 1,
this_ru_col + off + (int)strlen(buffer), off + width, fillchar,
fillchar, attr);
@@ -637,18 +637,7 @@ int fillchar_status(int *attr, win_T *wp)
*attr = win_hl_attr(wp, HLF_SNC);
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)
- || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
- return fill;
- }
- if (is_curwin) {
- return '^';
- }
- return '=';
+ return fill;
}
/// Redraw the status line according to 'statusline' and take care of any
@@ -810,12 +799,12 @@ void draw_tabline(void)
if (col + len >= Columns - 3) {
break;
}
- grid_puts_len(&default_grid, NameBuff, len, 0, col,
- hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
+ grid_puts(&default_grid, NameBuff, len, 0, col,
+ hl_combine_attr(attr, win_hl_attr(cwp, HLF_T)));
col += len;
}
if (modified) {
- grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
+ grid_puts(&default_grid, "+", 1, 0, col++, attr);
}
grid_putchar(&default_grid, ' ', 0, col++, attr);
}
@@ -835,7 +824,7 @@ void draw_tabline(void)
len = Columns - col - 1;
}
- grid_puts_len(&default_grid, p, (int)strlen(p), 0, col, attr);
+ grid_puts(&default_grid, p, (int)strlen(p), 0, col, attr);
col += len;
}
grid_putchar(&default_grid, ' ', 0, col++, attr);
@@ -864,8 +853,8 @@ void draw_tabline(void)
const int sc_width = MIN(10, (int)Columns - col - (tabcount > 1) * 3);
if (sc_width > 0) {
- grid_puts_len(&default_grid, showcmd_buf, sc_width, 0,
- Columns - sc_width - (tabcount > 1) * 2, attr_nosel);
+ grid_puts(&default_grid, showcmd_buf, sc_width, 0,
+ Columns - sc_width - (tabcount > 1) * 2, attr_nosel);
}
}
@@ -1583,7 +1572,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
// Note: The call will only return true if it actually
// appended data to the `buf_tmp` buffer.
- if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
+ if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp))) {
str = buf_tmp;
}
break;
@@ -1663,7 +1652,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
char *p = NULL;
if (fold) {
- size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM));
+ size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo,
+ (linenr_T)get_vim_var_nr(VV_LNUM), NULL);
stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1);
p = out_p;
p[n] = NUL;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 0983667695..db15cdb053 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -24,6 +24,7 @@
#include "nvim/event/signal.h"
#include "nvim/event/stream.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/log.h"
@@ -144,6 +145,7 @@ struct TUIData {
} unibi_ext;
char *space_buf;
bool stopped;
+ int seen_error_exit;
int width;
int height;
bool rgb;
@@ -161,6 +163,7 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term)
tui->is_starting = true;
tui->screenshot = NULL;
tui->stopped = false;
+ tui->seen_error_exit = 0;
tui->loop = &main_loop;
kv_init(tui->invalid_regions);
signal_watcher_init(tui->loop, &tui->winch_handle, tui);
@@ -383,8 +386,16 @@ static void terminfo_stop(TUIData *tui)
unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys);
// May restore old title before exiting alternate screen.
tui_set_title(tui, (String)STRING_INIT);
- // Exit alternate screen.
- unibi_out(tui, unibi_exit_ca_mode);
+ if (ui_client_exit_status == 0) {
+ ui_client_exit_status = tui->seen_error_exit;
+ }
+ // if nvim exited with nonzero status, without indicated this was an
+ // intentional exit (like `:1cquit`), it likely was an internal failure.
+ // Don't clobber the stderr error message in this case.
+ if (ui_client_exit_status == tui->seen_error_exit) {
+ // Exit alternate screen.
+ unibi_out(tui, unibi_exit_ca_mode);
+ }
if (tui->cursor_color_changed) {
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
@@ -442,6 +453,11 @@ static void tui_terminal_stop(TUIData *tui)
terminfo_stop(tui);
}
+void tui_error_exit(TUIData *tui, Integer status)
+{
+ tui->seen_error_exit = (int)status;
+}
+
void tui_stop(TUIData *tui)
{
tui_terminal_stop(tui);
@@ -675,15 +691,15 @@ static void final_column_wrap(TUIData *tui)
/// It is undocumented, but in the majority of terminals and terminal emulators
/// printing at the right margin does not cause an automatic wrap until the
/// next character is printed, holding the cursor in place until then.
-static void print_cell(TUIData *tui, UCell *ptr)
+static void print_cell(TUIData *tui, char *buf, sattr_T attr)
{
UGrid *grid = &tui->grid;
if (!tui->immediate_wrap_after_last_column) {
// Printing the next character finally advances the cursor.
final_column_wrap(tui);
}
- update_attrs(tui, ptr->attr);
- out(tui, ptr->data, strlen(ptr->data));
+ update_attrs(tui, attr);
+ out(tui, buf, strlen(buf));
grid->col++;
if (tui->immediate_wrap_after_last_column) {
// Printing at the right margin immediately advances the cursor.
@@ -703,8 +719,8 @@ static bool cheap_to_print(TUIData *tui, int row, int col, int next)
return false;
}
}
- if (strlen(cell->data) > 1) {
- return false;
+ if (schar_get_ascii(cell->data) == 0) {
+ return false; // not ascii
}
cell++;
}
@@ -831,14 +847,16 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool
{
UGrid *grid = &tui->grid;
- if (grid->row == -1 && cell->data[0] == NUL) {
+ if (grid->row == -1 && cell->data == NUL) {
// If cursor needs to repositioned and there is nothing to print, don't move cursor.
return;
}
cursor_goto(tui, row, col);
- bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data));
+ char buf[MAX_SCHAR_SIZE];
+ schar_get(buf, cell->data);
+ bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(buf));
if (is_ambiwidth && is_doublewidth) {
// Clear the two screen cells.
// If the character is single-width in the host terminal it won't change the second cell.
@@ -847,7 +865,7 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool
cursor_goto(tui, row, col);
}
- print_cell(tui, cell);
+ print_cell(tui, buf, cell->attr);
if (is_ambiwidth) {
// Force repositioning cursor after printing an ambiguous-width character.
@@ -976,6 +994,8 @@ void tui_grid_clear(TUIData *tui, Integer g)
{
UGrid *grid = &tui->grid;
ugrid_clear(grid);
+ // safe to clear cache at this point
+ schar_cache_clear_if_full();
kv_size(tui->invalid_regions) = 0;
clear_region(tui, 0, tui->height, 0, tui->width, 0);
}
@@ -1273,7 +1293,7 @@ void tui_flush(TUIData *tui)
int clear_col;
for (clear_col = r.right; clear_col > 0; clear_col--) {
UCell *cell = &grid->cells[row][clear_col - 1];
- if (!(cell->data[0] == ' ' && cell->data[1] == NUL
+ if (!(cell->data == schar_from_ascii(' ')
&& cell->attr == clear_attr)) {
break;
}
@@ -1281,7 +1301,7 @@ void tui_flush(TUIData *tui)
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
print_cell_at_pos(tui, row, curcol, cell,
- curcol < clear_col - 1 && (cell + 1)->data[0] == NUL);
+ curcol < clear_col - 1 && (cell + 1)->data == NUL);
});
if (clear_col < r.right) {
clear_region(tui, row, row + 1, clear_col, r.right, clear_attr);
@@ -1399,7 +1419,10 @@ void tui_screenshot(TUIData *tui, String path)
for (int i = 0; i < grid->height; i++) {
cursor_goto(tui, i, 0);
for (int j = 0; j < grid->width; j++) {
- print_cell(tui, &grid->cells[i][j]);
+ UCell cell = grid->cells[i][j];
+ char buf[MAX_SCHAR_SIZE];
+ schar_get(buf, cell.data);
+ print_cell(tui, buf, cell.attr);
}
}
flush_buf(tui);
@@ -1446,13 +1469,13 @@ void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, In
{
UGrid *grid = &tui->grid;
for (Integer c = startcol; c < endcol; c++) {
- memcpy(grid->cells[linerow][c].data, chunk[c - startcol], sizeof(schar_T));
+ grid->cells[linerow][c].data = chunk[c - startcol];
assert((size_t)attrs[c - startcol] < kv_size(tui->attrs));
grid->cells[linerow][c].attr = attrs[c - startcol];
}
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
print_cell_at_pos(tui, (int)linerow, curcol, cell,
- curcol < endcol - 1 && (cell + 1)->data[0] == NUL);
+ curcol < endcol - 1 && (cell + 1)->data == NUL);
});
if (clearcol > endcol) {
@@ -1469,7 +1492,7 @@ void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, In
if (endcol != grid->width) {
// Print the last char of the row, if we haven't already done so.
- int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1;
+ int size = grid->cells[linerow][grid->width - 1].data == NUL ? 2 : 1;
print_cell_at_pos(tui, (int)linerow, grid->width - size,
&grid->cells[linerow][grid->width - size], size == 2);
}
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 3a05223023..afc1b3f5fe 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -46,4 +46,10 @@ typedef enum {
typedef struct Decoration Decoration;
+#ifndef ORDER_BIG_ENDIAN
+# if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define ORDER_BIG_ENDIAN
+# endif
+#endif
+
#endif // NVIM_TYPES_H
diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c
index be1746983c..d3a1097aba 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -4,6 +4,7 @@
#include <assert.h>
#include <string.h>
+#include "nvim/grid.h"
#include "nvim/memory.h"
#include "nvim/ugrid.h"
@@ -79,8 +80,7 @@ static void clear_region(UGrid *grid, int top, int bot, int left, int right, sat
{
for (int row = top; row <= bot; row++) {
UGRID_FOREACH_CELL(grid, row, left, right + 1, {
- cell->data[0] = ' ';
- cell->data[1] = 0;
+ cell->data = schar_from_ascii(' ');
cell->attr = attr;
});
}
diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h
index a85a6fb4a8..1c73c43867 100644
--- a/src/nvim/ugrid.h
+++ b/src/nvim/ugrid.h
@@ -11,10 +11,8 @@ struct ugrid;
typedef struct ucell UCell;
typedef struct ugrid UGrid;
-#define CELLBYTES (sizeof(schar_T))
-
struct ucell {
- char data[CELLBYTES + 1];
+ schar_T data;
sattr_T attr;
};
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index 7e5f847039..05964422f3 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -23,6 +23,9 @@ EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
// ID of the ui client channel. If zero, the client is not running.
EXTERN uint64_t ui_client_channel_id INIT(= 0);
+// exit status from embedded nvim process
+EXTERN int ui_client_exit_status INIT(= 0);
+
// TODO(bfredl): the current structure for how tui and ui_client.c communicate is a bit awkward.
// This will be restructured as part of The UI Devirtualization Project.
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index e9b23d1298..b88c355f42 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -55,7 +55,7 @@ static int msg_current_row = INT_MAX;
static bool msg_was_scrolled = false;
static int msg_sep_row = -1;
-static schar_T msg_sep_char = { ' ', NUL };
+static schar_T msg_sep_char = schar_from_ascii(' ');
static int dbghl_normal, dbghl_clear, dbghl_composed, dbghl_recompose;
@@ -354,7 +354,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
grid = &msg_grid;
sattr_T msg_sep_attr = (sattr_T)HL_ATTR(HLF_MSGSEP);
for (int i = col; i < until; i++) {
- memcpy(linebuf[i - startcol], msg_sep_char, sizeof(*linebuf));
+ linebuf[i - startcol] = msg_sep_char;
attrbuf[i - startcol] = msg_sep_attr;
}
} else {
@@ -363,9 +363,8 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
memcpy(linebuf + (col - startcol), grid->chars + off, n * sizeof(*linebuf));
memcpy(attrbuf + (col - startcol), grid->attrs + off, n * sizeof(*attrbuf));
if (grid->comp_col + grid->cols > until
- && grid->chars[off + n][0] == NUL) {
- linebuf[until - 1 - startcol][0] = ' ';
- linebuf[until - 1 - startcol][1] = '\0';
+ && grid->chars[off + n] == NUL) {
+ linebuf[until - 1 - startcol] = schar_from_ascii(' ');
if (col == startcol && n == 1) {
skipstart = 0;
}
@@ -378,10 +377,10 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
for (int i = col - (int)startcol; i < until - startcol; i += width) {
width = 1;
// negative space
- bool thru = strequal((char *)linebuf[i], " ") && bg_line[i][0] != NUL;
- if (i + 1 < endcol - startcol && bg_line[i + 1][0] == NUL) {
+ bool thru = linebuf[i] == schar_from_ascii(' ') && bg_line[i] != NUL;
+ if (i + 1 < endcol - startcol && bg_line[i + 1] == NUL) {
width = 2;
- thru &= strequal((char *)linebuf[i + 1], " ");
+ thru &= linebuf[i + 1] == schar_from_ascii(' ');
}
attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], &thru);
if (width == 2) {
@@ -396,28 +395,25 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
// 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;
+ if (linebuf[col - startcol] == NUL) {
+ linebuf[col - startcol] = schar_from_ascii(' ');
if (col == endcol - 1) {
skipend = 0;
}
- } else if (col == startcol && n > 1 && linebuf[1][0] == NUL) {
+ } else if (col == startcol && n > 1 && linebuf[1] == NUL) {
skipstart = 0;
}
col = until;
}
- if (linebuf[endcol - startcol - 1][0] == NUL) {
+ if (linebuf[endcol - startcol - 1] == NUL) {
skipend = 0;
}
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
+ if (!(grid && (grid == &default_grid || (grid->comp_col == 0 && grid->cols == Columns)))) {
flags = flags & ~kLineFlagWrap;
}
@@ -568,7 +564,7 @@ void ui_comp_msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep
if (scrolled && row > 0) {
msg_sep_row = (int)row - 1;
if (sep_char.data) {
- xstrlcpy(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
+ msg_sep_char = schar_from_buf(sep_char.data, sep_char.size);
}
} else {
msg_sep_row = -1;
diff --git a/src/nvim/version.c b/src/nvim/version.c
index b6861142cc..0744a601bb 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -2837,12 +2837,6 @@ void intro_message(int colon)
}
}
}
-
- // Make the wait-return message appear just below the text.
- if (colon) {
- assert(row <= INT_MAX);
- msg_row = (int)row;
- }
}
static void do_intro_line(long row, char *mesg, int attr)
@@ -2871,8 +2865,8 @@ static void do_intro_line(long row, char *mesg, int attr)
l += utfc_ptr2len(p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
- grid_puts_len(&default_grid, p, l, (int)row, (int)col,
- *p == '<' ? HL_ATTR(HLF_8) : attr);
+ grid_puts(&default_grid, p, l, (int)row, (int)col,
+ *p == '<' ? HL_ATTR(HLF_8) : attr);
col += clen;
}
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c0d399c4c4..1208494eaf 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1009,7 +1009,9 @@ void ui_ext_win_position(win_T *wp, bool validate)
comp_row += grid->comp_row;
comp_col += grid->comp_col;
comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0);
- comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0);
+ if (!c.fixed || east) {
+ comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0);
+ }
wp->w_winrow = comp_row;
wp->w_wincol = comp_col;
ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,