aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/autocmd.c73
-rw-r--r--src/nvim/api/buffer.c14
-rw-r--r--src/nvim/api/command.c11
-rw-r--r--src/nvim/api/extmark.c42
-rw-r--r--src/nvim/api/keysets.h1
-rw-r--r--src/nvim/api/ui.c15
-rw-r--r--src/nvim/api/vim.c41
-rw-r--r--src/nvim/api/win_config.c36
-rw-r--r--src/nvim/buffer_defs.h11
-rw-r--r--src/nvim/decoration.c39
-rw-r--r--src/nvim/decoration.h2
-rw-r--r--src/nvim/drawline.c124
-rw-r--r--src/nvim/drawscreen.c18
-rw-r--r--src/nvim/edit.c5
-rw-r--r--src/nvim/eval.c101
-rw-r--r--src/nvim/eval.lua6
-rw-r--r--src/nvim/fold.c39
-rw-r--r--src/nvim/grid.c178
-rw-r--r--src/nvim/grid.h37
-rw-r--r--src/nvim/grid_defs.h11
-rw-r--r--src/nvim/highlight.c2
-rw-r--r--src/nvim/highlight_group.c6
-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/mapping.c158
-rw-r--r--src/nvim/marktree.c9
-rw-r--r--src/nvim/mouse.c16
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c5
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/os/pty_process_win.c2
-rw-r--r--src/nvim/regexp.c51
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/statusline.c3
-rw-r--r--src/nvim/tui/tui.c36
-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_compositor.c26
-rw-r--r--src/nvim/window.c4
44 files changed, 822 insertions, 487 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index aa0c2695ad..2e4d2a622d 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -49,19 +49,20 @@ static int64_t next_autocmd_id = 1;
/// Get all autocommands that match the corresponding {opts}.
///
/// These examples will get autocommands matching ALL the given criteria:
-/// <pre>lua
-/// -- Matches all criteria
-/// autocommands = vim.api.nvim_get_autocmds({
-/// group = "MyGroup",
-/// event = {"BufEnter", "BufWinEnter"},
-/// pattern = {"*.c", "*.h"}
-/// })
///
-/// -- All commands from one group
-/// autocommands = vim.api.nvim_get_autocmds({
-/// group = "MyGroup",
-/// })
-/// </pre>
+/// ```lua
+/// -- Matches all criteria
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// event = {"BufEnter", "BufWinEnter"},
+/// pattern = {"*.c", "*.h"}
+/// })
+///
+/// -- All commands from one group
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// })
+/// ```
///
/// NOTE: When multiple patterns or events are provided, it will find all the autocommands that
/// match any combination of them.
@@ -344,28 +345,31 @@ cleanup:
/// function _name_ string) or `command` (Ex command string).
///
/// Example using Lua callback:
-/// <pre>lua
-/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
-/// pattern = {"*.c", "*.h"},
-/// callback = function(ev)
-/// print(string.format('event fired: \%s', vim.inspect(ev)))
-/// end
-/// })
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// callback = function(ev)
+/// print(string.format('event fired: %s', vim.inspect(ev)))
+/// end
+/// })
+/// ```
///
/// Example using an Ex command as the handler:
-/// <pre>lua
-/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
-/// pattern = {"*.c", "*.h"},
-/// command = "echo 'Entering a C or C++ file'",
-/// })
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// command = "echo 'Entering a C or C++ file'",
+/// })
+/// ```
///
/// Note: `pattern` is NOT automatically expanded (unlike with |:autocmd|), thus names like "$HOME"
/// and "~" must be expanded explicitly:
-/// <pre>lua
-/// pattern = vim.fn.expand("~") .. "/some/path/*.py"
-/// </pre>
+///
+/// ```lua
+/// pattern = vim.fn.expand("~") .. "/some/path/*.py"
+/// ```
///
/// @param event (string|array) Event(s) that will trigger the handler (`callback` or `command`).
/// @param opts Options dict:
@@ -619,11 +623,12 @@ cleanup:
/// Create or get an autocommand group |autocmd-groups|.
///
/// To get an existing group id, do:
-/// <pre>lua
-/// local id = vim.api.nvim_create_augroup("MyGroup", {
-/// clear = false
-/// })
-/// </pre>
+///
+/// ```lua
+/// local id = vim.api.nvim_create_augroup("MyGroup", {
+/// clear = false
+/// })
+/// ```
///
/// @param name String: The name of the group
/// @param opts Dictionary Parameters
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index baac694848..e8f9f809f2 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -85,11 +85,15 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
///
/// Example (Lua): capture buffer updates in a global `events` variable
/// (use "vim.print(events)" to see its contents):
-/// <pre>lua
-/// events = {}
-/// vim.api.nvim_buf_attach(0, false, {
-/// on_lines=function(...) table.insert(events, {...}) end})
-/// </pre>
+///
+/// ```lua
+/// events = {}
+/// vim.api.nvim_buf_attach(0, false, {
+/// on_lines = function(...)
+/// table.insert(events, {...})
+/// end,
+/// })
+/// ```
///
/// @see |nvim_buf_detach()|
/// @see |api-buffer-updates-lua|
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 2b09cfc4b2..808d4e0b8d 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -860,11 +860,12 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
/// For Lua usage see |lua-guide-commands-create|.
///
/// Example:
-/// <pre>vim
-/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
-/// :SayHello
-/// Hello world!
-/// </pre>
+///
+/// ```vim
+/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {'bang': v:true})
+/// :SayHello
+/// Hello world!
+/// ```
///
/// @param name Name of the new user command. Must begin with an uppercase letter.
/// @param command Replacement command to execute when this user command is executed. When called
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index b76a275c0d..faab6e593c 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -300,10 +300,11 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
/// respectively, thus the following are equivalent:
-/// <pre>lua
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
-/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
+/// vim.api.nvim_buf_get_extmarks(0, my_ns, {0,0}, {-1,-1}, {})
+/// ```
///
/// If `end` is less than `start`, traversal works backwards. (Useful
/// with `limit`, to get the first marks prior to a given position.)
@@ -313,20 +314,21 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// of an extmark will be considered.
///
/// Example:
-/// <pre>lua
-/// local api = vim.api
-/// local pos = api.nvim_win_get_cursor(0)
-/// local ns = api.nvim_create_namespace('my-plugin')
-/// -- Create new extmark at line 1, column 1.
-/// local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
-/// -- Create new extmark at line 3, column 1.
-/// local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
-/// -- Get extmarks only from line 3.
-/// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
-/// -- Get all marks in this buffer + namespace.
-/// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
-/// vim.print(ms)
-/// </pre>
+///
+/// ```lua
+/// local api = vim.api
+/// local pos = api.nvim_win_get_cursor(0)
+/// local ns = api.nvim_create_namespace('my-plugin')
+/// -- Create new extmark at line 1, column 1.
+/// local m1 = api.nvim_buf_set_extmark(0, ns, 0, 0, {})
+/// -- Create new extmark at line 3, column 1.
+/// local m2 = api.nvim_buf_set_extmark(0, ns, 2, 0, {})
+/// -- Get extmarks only from line 3.
+/// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
+/// -- Get all marks in this buffer + namespace.
+/// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {})
+/// vim.print(ms)
+/// ```
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace id from |nvim_create_namespace()| or -1 for all namespaces
@@ -1204,7 +1206,9 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
}
- *width = w;
+ if (width != NULL) {
+ *width = w;
+ }
return virt_text;
free_exit:
diff --git a/src/nvim/api/keysets.h b/src/nvim/api/keysets.h
index 4e5e7af619..736ca9ce07 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/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/vim.c b/src/nvim/api/vim.c
index da10ab5bd4..0a94b8aafc 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -212,10 +212,11 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
/// nvim_feedkeys().
///
/// Example:
-/// <pre>vim
-/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
-/// :call nvim_feedkeys(key, 'n', v:false)
-/// </pre>
+///
+/// ```vim
+/// :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true)
+/// :call nvim_feedkeys(key, 'n', v:false)
+/// ```
///
/// @param keys to be typed
/// @param mode behavior flags, see |feedkeys()|
@@ -1280,10 +1281,11 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
/// "#rrggbb" hexadecimal string.
///
/// Example:
-/// <pre>vim
-/// :echo nvim_get_color_by_name("Pink")
-/// :echo nvim_get_color_by_name("#cbcbcb")
-/// </pre>
+///
+/// ```vim
+/// :echo nvim_get_color_by_name("Pink")
+/// :echo nvim_get_color_by_name("#cbcbcb")
+/// ```
///
/// @param name Color name or "#rrggbb" string
/// @return 24-bit RGB value, or -1 for invalid argument.
@@ -1420,14 +1422,16 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
///
/// Example:
-/// <pre>vim
-/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
-/// </pre>
+///
+/// ```vim
+/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+/// ```
///
/// is equivalent to:
-/// <pre>vim
-/// nmap <nowait> <Space><NL> <Nop>
-/// </pre>
+///
+/// ```vim
+/// nmap <nowait> <Space><NL> <Nop>
+/// ```
///
/// @param channel_id
/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
@@ -1943,7 +1947,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
@@ -1959,6 +1965,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 ac27554172..3d77d65fbc 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"
@@ -56,16 +56,19 @@
/// this should not be used to specify arbitrary WM screen positions.
///
/// Example (Lua): window-relative float
-/// <pre>lua
-/// vim.api.nvim_open_win(0, false,
-/// {relative='win', row=3, col=3, width=12, height=3})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_open_win(0, false,
+/// {relative='win', row=3, col=3, width=12, height=3})
+/// ```
///
/// Example (Lua): buffer-relative float (travels as buffer is scrolled)
-/// <pre>lua
-/// vim.api.nvim_open_win(0, false,
-/// {relative='win', width=12, height=3, bufpos={100,10}})
-/// </pre>
+///
+/// ```lua
+/// vim.api.nvim_open_win(0, false,
+/// {relative='win', width=12, height=3, bufpos={100,10}})
+/// })
+/// ```
///
/// @param buffer Buffer to display, or 0 for current buffer
/// @param enter Enter the window (make it the current window)
@@ -160,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
///
@@ -190,7 +195,8 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
}
// autocmds in win_enter or win_set_buf below may close the window
if (win_valid(wp) && buffer > 0) {
- win_set_buf(wp, buf, fconfig.noautocmd, err);
+ Boolean noautocmd = !enter || fconfig.noautocmd;
+ win_set_buf(wp, buf, noautocmd, err);
}
if (!win_valid(wp)) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
@@ -344,7 +350,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);
@@ -516,7 +522,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 },
@@ -527,7 +533,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;
@@ -841,6 +847,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/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/decoration.c b/src/nvim/decoration.c
index 265bc11661..f4ca31040a 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -268,6 +268,28 @@ 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.
+static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item)
+{
+ if (win_col < 0) {
+ 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;
+ } else {
+ item->draw_col = -1;
+ }
+}
+
+void decor_recheck_draw_col(int win_col, bool hidden, DecorState *state)
+{
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange *item = &kv_A(state->active, i);
+ if (item->draw_col == -3) {
+ decor_init_draw_col(win_col, hidden, item);
+ }
+ }
+}
+
int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
{
buf_T *buf = wp->w_buffer;
@@ -348,19 +370,10 @@ next_mark:
if (active && item.decor.spell != kNone) {
spell = item.decor.spell;
}
- if (item.start_row == state->row && decor_virt_pos(&item.decor)
- && item.draw_col != INT_MIN) {
- if (item.start_col <= col) {
- if (item.decor.virt_text_pos == kVTOverlay && item.draw_col == -1) {
- item.draw_col = (item.decor.virt_text_hide && hidden) ? INT_MIN : win_col;
- } else if (item.draw_col == -3) {
- item.draw_col = -1;
- }
- } else if (wp->w_p_wrap
- && (item.decor.virt_text_pos == kVTRightAlign
- || item.decor.virt_text_pos == kVTWinCol)) {
- item.draw_col = -3;
- }
+ 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_init_draw_col(win_col, hidden, &item);
}
if (keep) {
kv_A(state->active, j++) = item;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 3d16aa803e..0f191aa870 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -84,7 +84,7 @@ typedef struct {
bool virt_text_owned;
/// 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 a later screen line.
+ /// When -3, the virtual text should be drawn on the next screen line.
/// 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 969021ef2c..8b4786a98e 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)
@@ -136,8 +138,6 @@ typedef struct {
///< or w_skipcol or concealing
int skipped_cells; ///< nr of skipped cells for virtual text
///< to be added to wlv.vcol later
- bool more_virt_inline_chunks; ///< indicates if there is more inline virtual text
- ///< after n_extra
} winlinevars_T;
/// for line_putchar. Contains the state that needs to be remembered from
@@ -223,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[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)) {
@@ -254,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[1] = 0;
}
done:
s->p += c_len;
@@ -350,17 +350,17 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
max_col - col, false, 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;
}
- if (col < max_col && linebuf_char[col][0] == 0) {
+ if (col < max_col && linebuf_char[col] == 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], ' ');
+ linebuf_char[col] = schar_from_ascii(' ');
}
vcol += cells;
}
@@ -386,7 +386,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;
@@ -407,7 +408,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;
@@ -449,16 +450,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);
}
@@ -868,10 +876,12 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv)
}
}
-/// Checks if there is more inline virtual text that need to be drawn
-/// and sets has_more_virt_inline_chunks to reflect that.
+/// Checks if there is more inline virtual text that need to be drawn.
static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v)
{
+ if (wlv->virt_inline_i < kv_size(wlv->virt_inline)) {
+ return true;
+ }
DecorState *state = &decor_state;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange *item = &kv_A(state->active, i);
@@ -911,7 +921,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
break;
}
}
- wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v);
if (!kv_size(wlv->virt_inline)) {
// no more inline virtual text here
break;
@@ -929,11 +938,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
wlv->c_final = NUL;
wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
wlv->n_attr = mb_charlen(vtc.text);
-
- // Checks if there is more inline virtual text chunks that need to be drawn.
- wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v)
- || wlv->virt_inline_i < kv_size(wlv->virt_inline);
-
// If the text didn't reach until the first window
// column we need to skip cells.
if (wlv->skip_cells > 0) {
@@ -1147,8 +1151,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
bool saved_search_attr_from_match = false;
int win_col_offset = 0; // offset for window columns
+ bool area_active = false; // whether in Visual selection, for virtual text
+ bool decor_need_recheck = false; // call decor_recheck_draw_col() at next char
char buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
+ VirtText fold_vt = VIRTTEXT_EMPTY;
// 'cursorlineopt' has "screenline" and cursor is in this line
bool cul_screenline = false;
@@ -1788,9 +1795,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
if (has_decor && wlv.n_extra == 0) {
- bool selected = (area_highlighting
- && ((wlv.vcol >= wlv.fromcol && wlv.vcol < wlv.tocol)
- || (noinvcur && wlv.vcol == wp->w_virtcol)));
+ // Duplicate the Visual area check after this block,
+ // but don't check inside p_extra here.
+ if (wlv.vcol == wlv.fromcol
+ || (wlv.vcol + 1 == wlv.fromcol
+ && (wlv.n_extra == 0 && utf_ptr2cells(ptr) > 1))
+ || (vcol_prev == fromcol_prev
+ && vcol_prev < wlv.vcol
+ && wlv.vcol < wlv.tocol)) {
+ area_active = true;
+ } else if (area_active
+ && (wlv.vcol == wlv.tocol
+ || (noinvcur && wlv.vcol == wp->w_virtcol))) {
+ area_active = false;
+ }
+
+ bool selected = (area_active || (area_highlighting && noinvcur
+ && wlv.vcol == wp->w_virtcol));
+ if (decor_need_recheck) {
+ decor_recheck_draw_col(wlv.off, selected, &decor_state);
+ decor_need_recheck = false;
+ }
extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state);
if (!has_fold && wp->w_buffer->b_virt_text_inline > 0) {
@@ -1824,10 +1849,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& vcol_prev < wlv.vcol // not at margin
&& wlv.vcol < wlv.tocol)) {
*area_attr_p = vi_attr; // start highlighting
+ area_active = true;
} else if (*area_attr_p != 0
&& (wlv.vcol == wlv.tocol
|| (noinvcur && wlv.vcol == wp->w_virtcol))) {
*area_attr_p = 0; // stop highlighting
+ area_active = false;
}
if (!has_fold && wlv.n_extra == 0) {
@@ -1900,7 +1927,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (draw_folded && wlv.n_extra == 0 && wlv.col == win_col_offset) {
linenr_T lnume = lnum + foldinfo.fi_lines - 1;
memset(buf_fold, ' ', FOLD_TEXT_LEN);
- wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold, &fold_vt);
wlv.n_extra = (int)strlen(wlv.p_extra);
if (wlv.p_extra != buf_fold) {
@@ -2720,7 +2747,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
@@ -2822,7 +2849,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) {
@@ -2856,7 +2883,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;
@@ -2865,6 +2892,9 @@ 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(wp, buf, win_col_offset, &wlv.col, 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++;
@@ -2890,15 +2920,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wlv.filler_todo <= 0
&& (wp->w_p_rl ? wlv.col == 0 : wlv.col == grid->cols - 1)
&& !has_fold) {
- if (*ptr == NUL && lcs_eol_one == 0 && has_decor) {
+ if (has_decor && *ptr == NUL && lcs_eol_one == 0) {
// Tricky: there might be a virtual text just _after_ the last char
decor_redraw_col(wp, (colnr_T)v, wlv.off, false, &decor_state);
- handle_inline_virtual_text(wp, &wlv, v);
}
if (*ptr != NUL
|| lcs_eol_one > 0
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
- || wlv.more_virt_inline_chunks) {
+ || has_more_inline_virt(&wlv, v)) {
c = wp->w_p_lcs_chars.ext;
wlv.char_attr = win_hl_attr(wp, HLF_AT);
mb_c = c;
@@ -2954,9 +2983,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;
@@ -2967,12 +2996,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_vcol[wlv.off] = wlv.vcol;
+ if (wlv.draw_state == WL_FOLD) {
+ linebuf_vcol[wlv.off] = -2;
+ if (wlv.n_closing > 0) {
+ linebuf_vcol[wlv.off] = -3;
+ wlv.n_closing--;
+ }
+ }
+
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++;
@@ -3085,13 +3122,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.char_attr = saved_attr2;
}
- if ((wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols)) && has_decor) {
+ if (has_decor && wlv.filler_todo <= 0
+ && (wp->w_p_rl ? (wlv.col < 0) : (wlv.col >= grid->cols))) {
// At the end of screen line: might need to peek for decorations just after
- // this position. Without wrapping, we might need to display win_pos overlays
- // from the entire text line.
- colnr_T nextpos = wp->w_p_wrap ? (colnr_T)(ptr - line) : (colnr_T)strlen(line);
- decor_redraw_col(wp, nextpos, wlv.off, true, &decor_state);
- handle_inline_virtual_text(wp, &wlv, v);
+ // this position.
+ if (!has_fold && wp->w_p_wrap && wlv.n_extra == 0) {
+ decor_redraw_col(wp, (int)(ptr - line), -3, false, &decor_state);
+ // Check position/hiding of virtual text again on next screen line.
+ decor_need_recheck = true;
+ } else if (has_fold || !wp->w_p_wrap) {
+ // Without wrapping, we might need to display right_align and win_col
+ // virt_text for the entire text line.
+ decor_redraw_col(wp, MAXCOL, -1, true, &decor_state);
+ }
}
// At end of screen line and there is more to come: Display the line
@@ -3104,7 +3147,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& wlv.p_extra != at_end_str)
|| (wlv.n_extra != 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
- || wlv.more_virt_inline_chunks)) {
+ || has_more_inline_virt(&wlv, v))) {
bool wrap = wp->w_p_wrap // Wrapping enabled.
&& wlv.filler_todo <= 0 // Not drawing diff filler lines.
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character.
@@ -3117,7 +3160,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
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);
- } else {
+ } else if (wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, wlv.row);
}
@@ -3186,6 +3229,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
} // for every character in the line
+ clear_virttext(&fold_vt);
kv_destroy(virt_lines);
xfree(wlv.p_extra_free);
xfree(wlv.saved_p_extra_free);
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index f71a47a596..e3e9fc8170 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -444,6 +444,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) {
@@ -736,7 +745,10 @@ 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;
@@ -770,7 +782,7 @@ static void win_redr_border(win_T *wp)
grid_puts_line_flush(false);
}
if (adj[1]) {
- int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
+ int ic = (i == 0 && !adj[0] && chars[2]) ? 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);
@@ -784,7 +796,7 @@ static void win_redr_border(win_T *wp)
}
for (int i = 0; i < icol; i++) {
- int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
+ int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 06eb81be92..216f8a67db 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4429,9 +4429,8 @@ static bool ins_tab(void)
}
}
if (!(State & VREPLACE_FLAG)) {
- extmark_splice_cols(curbuf, (int)fpos.lnum - 1, change_col,
- cursor->col - change_col, fpos.col - change_col,
- kExtmarkUndo);
+ inserted_bytes(fpos.lnum, change_col,
+ cursor->col - change_col, fpos.col - change_col);
}
}
cursor->col -= i;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9be4cea059..21c7cdee7d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -12,7 +12,9 @@
#include <string.h>
#include "auto/config.h"
+#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@@ -947,38 +949,42 @@ int skip_expr(char **pp, evalarg_T *const evalarg)
return res;
}
+/// Convert "tv" to a string.
+///
+/// @param convert when true convert a List into a sequence of lines.
+///
+/// @return an allocated string.
+static char *typval2string(typval_T *tv, bool convert)
+{
+ if (convert && tv->v_type == VAR_LIST) {
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char), 80);
+ if (tv->vval.v_list != NULL) {
+ tv_list_join(&ga, tv->vval.v_list, "\n");
+ if (tv_list_len(tv->vval.v_list) > 0) {
+ ga_append(&ga, NL);
+ }
+ }
+ ga_append(&ga, NUL);
+ return (char *)ga.ga_data;
+ }
+ return xstrdup(tv_get_string(tv));
+}
+
/// Top level evaluation function, returning a string.
///
-/// @param convert when true convert a List into a sequence of lines and convert
-/// a Float to a String.
+/// @param convert when true convert a List into a sequence of lines.
///
-/// @return pointer to allocated memory, or NULL for failure.
+/// @return pointer to allocated memory, or NULL for failure.
char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
- garray_T ga;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
- if (convert && tv.v_type == VAR_LIST) {
- ga_init(&ga, (int)sizeof(char), 80);
- if (tv.vval.v_list != NULL) {
- tv_list_join(&ga, tv.vval.v_list, "\n");
- if (tv_list_len(tv.vval.v_list) > 0) {
- ga_append(&ga, NL);
- }
- }
- ga_append(&ga, NUL);
- retval = (char *)ga.ga_data;
- } else if (convert && tv.v_type == VAR_FLOAT) {
- char numbuf[NUMBUFLEN];
- vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float);
- retval = xstrdup(numbuf);
- } else {
- retval = xstrdup(tv_get_string(&tv));
- }
+ retval = typval2string(&tv, convert);
tv_clear(&tv);
}
clear_evalarg(&EVALARG_EVALUATE, NULL);
@@ -990,7 +996,7 @@ char *eval_to_string(char *arg, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, int use_sandbox)
+char *eval_to_string_safe(char *arg, const bool use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -1263,11 +1269,13 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
/// it in "*cp". Doesn't give error messages.
-int eval_foldexpr(char *arg, int *cp)
+int eval_foldexpr(win_T *wp, int *cp)
{
- typval_T tv;
- varnumber_T retval;
- int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL);
+ 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) {
@@ -1275,6 +1283,9 @@ int eval_foldexpr(char *arg, int *cp)
}
textlock++;
*cp = NUL;
+
+ typval_T tv;
+ varnumber_T retval;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
@@ -1294,16 +1305,54 @@ int eval_foldexpr(char *arg, int *cp)
}
tv_clear(&tv);
}
+
emsg_off--;
if (use_sandbox) {
sandbox--;
}
textlock--;
clear_evalarg(&EVALARG_EVALUATE, NULL);
+ current_sctx = saved_sctx;
return (int)retval;
}
+/// Evaluate 'foldtext', returning an Array or a String (NULL_STRING on failure).
+Object eval_foldtext(win_T *wp)
+{
+ const bool use_sandbox = was_set_insecurely(wp, "foldtext", OPT_LOCAL);
+ char *arg = wp->w_p_fdt;
+ funccal_entry_T funccal_entry;
+
+ save_funccal(&funccal_entry);
+ if (use_sandbox) {
+ sandbox++;
+ }
+ textlock++;
+
+ typval_T tv;
+ Object retval;
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
+ retval = STRING_OBJ(NULL_STRING);
+ } else {
+ if (tv.v_type == VAR_LIST) {
+ retval = vim_to_object(&tv);
+ } else {
+ retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv)));
+ }
+ tv_clear(&tv);
+ }
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+
+ if (use_sandbox) {
+ sandbox--;
+ }
+ textlock--;
+ restore_funccal();
+
+ return retval;
+}
+
/// Get an lvalue
///
/// Lvalue may be
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e6efbe85d8..2137dc07e4 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -2111,7 +2111,11 @@ M.funcs = {
]=],
name = 'execute',
- params = { { 'command', 'any' }, { 'silent', 'boolean' } },
+ params = {
+ { 'command', 'string|string[]' },
+ { 'silent', "''|'silent'|'silent!'" }
+ },
+ returns = 'string',
signature = 'execute({command} [, {silent}])',
},
exepath = {
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index a6cb0b568c..1d5ba49301 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -12,12 +12,14 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/api/extmark.h"
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
@@ -1702,8 +1704,9 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker
/// @return the text for a closed fold
///
/// Otherwise the result is in allocated memory.
-char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char *buf)
- FUNC_ATTR_NONNULL_ARG(1)
+char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char *buf,
+ VirtText *vt)
+ FUNC_ATTR_NONNULL_ALL
{
char *text = NULL;
// an error occurred when evaluating 'fdt' setting
@@ -1750,8 +1753,22 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
current_sctx = wp->w_p_script_ctx[WV_FDT].script_ctx;
emsg_off++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt,
- was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+
+ Object obj = eval_foldtext(wp);
+ if (obj.type == kObjectTypeArray) {
+ Error err = ERROR_INIT;
+ *vt = parse_virt_text(obj.data.array, &err, NULL);
+ if (!ERROR_SET(&err)) {
+ *buf = NUL;
+ text = buf;
+ }
+ api_clear_error(&err);
+ } else if (obj.type == kObjectTypeString) {
+ text = obj.data.string.data;
+ obj = NIL;
+ }
+ api_free_object(obj);
+
emsg_off--;
if (text == NULL || did_emsg) {
@@ -2929,7 +2946,7 @@ static void foldlevelExpr(fline_T *flp)
const bool save_keytyped = KeyTyped;
int c;
- const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
+ const int n = eval_foldexpr(flp->wp, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -3320,10 +3337,20 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
foldinfo_T info = fold_info(curwin, lnum);
if (info.fi_lines > 0) {
- char *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
+ VirtText vt = VIRTTEXT_EMPTY;
+ char *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf, &vt);
if (text == buf) {
text = xstrdup(text);
}
+ 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);
+ xfree(text);
+ text = new_text;
+ }
+ }
+ clear_virttext(&vt);
rettv->vval.v_string = text;
}
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index fa7f270172..cf6cd2f04e 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
+ }
+}
+
+/// 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;
}
- p[len] = 0;
- return len;
}
+/// @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;
@@ -155,7 +259,7 @@ 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
@@ -185,12 +289,12 @@ void grid_puts_line_start(ScreenGrid *grid, int row)
put_dirty_grid = grid;
}
-void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr)
+void grid_put_schar(ScreenGrid *grid, int row, 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);
+ 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);
@@ -293,10 +397,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,15 +426,15 @@ 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;
}
@@ -429,12 +535,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,7 +549,7 @@ 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) {
@@ -483,11 +589,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 +649,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 +659,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 +670,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 +723,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 +749,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 +758,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;
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..b0d1dafd29 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 {
@@ -41,6 +45,9 @@ enum {
///
/// vcols[] contains the virtual columns in the line. -1 means not available
/// (below last line), 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
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/highlight_group.c b/src/nvim/highlight_group.c
index b970e752bb..84cf19ba69 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -824,7 +824,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_link = 0;
}
- g->sg_gui = attrs.rgb_ae_attr;
+ g->sg_gui = attrs.rgb_ae_attr &~HL_DEFAULT;
g->sg_rgb_fg = attrs.rgb_fg_color;
g->sg_rgb_bg = attrs.rgb_bg_color;
@@ -851,7 +851,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
}
}
- g->sg_cterm = attrs.cterm_ae_attr;
+ g->sg_cterm = attrs.cterm_ae_attr &~HL_DEFAULT;
g->sg_cterm_bg = attrs.cterm_bg_color;
g->sg_cterm_fg = attrs.cterm_fg_color;
g->sg_cterm_bold = g->sg_cterm & HL_BOLD;
@@ -1441,7 +1441,7 @@ void restore_cterm_colors(void)
/// @param check_link if true also check for an existing link.
///
/// @return true if highlight group "idx" has any settings.
-static int hl_has_settings(int idx, bool check_link)
+static bool hl_has_settings(int idx, bool check_link)
{
return hl_table[idx].sg_cleared == 0
&& (hl_table[idx].sg_attr != 0
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/mapping.c b/src/nvim/mapping.c
index ab528f8865..35a728314c 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -148,9 +148,7 @@ mapblock_T *get_maphash(int index, buf_T *buf)
/// "mpp" is a pointer to the m_next field of the PREVIOUS entry!
static void mapblock_free(mapblock_T **mpp)
{
- mapblock_T *mp;
-
- mp = *mpp;
+ mapblock_T *mp = *mpp;
xfree(mp->m_keys);
if (!mp->m_simplified) {
NLUA_CLEAR_REF(mp->m_luaref);
@@ -212,8 +210,6 @@ static char *map_mode_to_chars(int mode)
/// @param local true for buffer-local map
static void showmap(mapblock_T *mp, bool local)
{
- size_t len = 1;
-
if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
&& (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
return;
@@ -226,12 +222,10 @@ static void showmap(mapblock_T *mp, bool local)
}
}
- {
- char *const mapchars = map_mode_to_chars(mp->m_mode);
- msg_puts(mapchars);
- len = strlen(mapchars);
- xfree(mapchars);
- }
+ char *const mapchars = map_mode_to_chars(mp->m_mode);
+ msg_puts(mapchars);
+ size_t len = strlen(mapchars);
+ xfree(mapchars);
while (++len <= 3) {
msg_putchar(' ');
@@ -572,33 +566,16 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
/// @param buf Target Buffer
static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
{
- mapblock_T *mp, **mpp;
- const char *p;
- int n;
int retval = 0;
- mapblock_T **abbr_table;
- mapblock_T **map_table;
- int noremap;
- map_table = maphash;
- abbr_table = &first_abbr;
+ // If <buffer> was given, we'll be searching through the buffer's
+ // mappings/abbreviations, not the globals.
+ mapblock_T **map_table = args->buffer ? buf->b_maphash : maphash;
+ mapblock_T **abbr_table = args->buffer ? &buf->b_first_abbr : &first_abbr;
// For ":noremap" don't remap, otherwise do remap.
- if (maptype == MAPTYPE_NOREMAP) {
- noremap = REMAP_NONE;
- } else {
- noremap = REMAP_YES;
- }
-
- if (args->buffer) {
- // If <buffer> was given, we'll be searching through the buffer's
- // mappings/abbreviations, not the globals.
- map_table = buf->b_maphash;
- abbr_table = &buf->b_first_abbr;
- }
- if (args->script) {
- noremap = REMAP_SCRIPT;
- }
+ int noremap = args->script ? REMAP_SCRIPT :
+ maptype == MAPTYPE_NOREMAP ? REMAP_NONE : REMAP_YES;
const bool has_lhs = (args->lhs[0] != NUL);
const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
@@ -648,8 +625,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const int first = vim_iswordp(lhs);
int last = first;
- p = lhs + utfc_ptr2len(lhs);
- n = 1;
+ const char *p = lhs + utfc_ptr2len(lhs);
+ int n = 1;
while (p < lhs + len) {
n++; // nr of (multi-byte) chars
last = vim_iswordp(p); // type of last char
@@ -685,6 +662,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
&& maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
+ mapblock_T *mp;
if (is_abbrev) {
if (hash != 0) { // there is only one abbreviation list
break;
@@ -714,6 +692,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
+ mapblock_T *mp;
if (is_abbrev) {
if (hash != 0) { // there is only one abbreviation list
break;
@@ -729,7 +708,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
showmap(mp, true);
did_local = true;
} else {
- n = mp->m_keylen;
+ int n = mp->m_keylen;
if (strncmp(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
showmap(mp, true);
did_local = true;
@@ -759,8 +738,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
hash_end = 256;
}
for (int hash = hash_start; hash < hash_end && !got_int; hash++) {
- mpp = is_abbrev ? abbr_table : &(map_table[hash]);
- for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
+ mapblock_T **mpp = is_abbrev ? abbr_table : &(map_table[hash]);
+ for (mapblock_T *mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
if ((mp->m_mode & mode) == 0) {
// skip entries with wrong mode
mpp = &(mp->m_next);
@@ -772,6 +751,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
did_it = true;
}
} else { // do we have a match?
+ int n;
+ const char *p;
if (round) { // second round: Try unmap "rhs" string
n = (int)strlen(mp->m_str);
p = mp->m_str;
@@ -994,12 +975,10 @@ free_and_return:
/// Get the mapping mode from the command name.
static int get_map_mode(char **cmdp, bool forceit)
{
- char *p;
- int modec;
int mode;
- p = *cmdp;
- modec = (uint8_t)(*p++);
+ char *p = *cmdp;
+ int modec = (uint8_t)(*p++);
if (modec == 'i') {
mode = MODE_INSERT; // :imap
} else if (modec == 'l') {
@@ -1036,16 +1015,13 @@ static int get_map_mode(char **cmdp, bool forceit)
/// This function used to be called map_clear().
static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
{
- int mode;
- int local;
-
- local = (strcmp(arg, "<buffer>") == 0);
+ bool local = strcmp(arg, "<buffer>") == 0;
if (!local && *arg != NUL) {
emsg(_(e_invarg));
return;
}
- mode = get_map_mode(&cmdp, forceit);
+ int mode = get_map_mode(&cmdp, forceit);
map_clear_mode(curbuf, mode, local, abbr);
}
@@ -1057,11 +1033,8 @@ static void do_mapclear(char *cmdp, char *arg, int forceit, int abbr)
/// @param abbr true for abbreviations
void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
{
- mapblock_T *mp, **mpp;
- int hash;
- int new_hash;
-
- for (hash = 0; hash < 256; hash++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T **mpp;
if (abbr) {
if (hash > 0) { // there is only one abbrlist
break;
@@ -1079,7 +1052,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
}
}
while (*mpp != NULL) {
- mp = *mpp;
+ mapblock_T *mp = *mpp;
if (mp->m_mode & mode) {
mp->m_mode &= ~mode;
if (mp->m_mode == 0) { // entry can be deleted
@@ -1087,7 +1060,7 @@ void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
continue;
}
// May need to put this entry into another hash list.
- new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
+ int new_hash = MAP_HASH(mp->m_mode, (uint8_t)mp->m_keys[0]);
if (!abbr && new_hash != hash) {
*mpp = mp->m_next;
if (local) {
@@ -1119,7 +1092,6 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
int mode = 0;
- int retval;
char *buf = NULL;
const char *const rhs = replace_termcodes(str, strlen(str), &buf, 0,
@@ -1141,7 +1113,7 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
MAPMODE(mode, modechars, 'c', MODE_CMDLINE);
#undef MAPMODE
- retval = map_to_exists_mode(rhs, mode, abbr);
+ int retval = map_to_exists_mode(rhs, mode, abbr);
xfree(buf);
return retval;
@@ -1159,13 +1131,12 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
/// @return true if there is at least one mapping with given parameters.
int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
{
- mapblock_T *mp;
- int hash;
bool exp_buffer = false;
// Do it twice: once for global maps and once for local maps.
while (true) {
- for (hash = 0; hash < 256; hash++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // There is only one abbr list.
break;
@@ -1486,10 +1457,7 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat
// Return true if there is an abbreviation, false if not.
bool check_abbr(int c, char *ptr, int col, int mincol)
{
- int scol; // starting column of the abbr.
uint8_t tb[MB_MAXBYTES + 4];
- mapblock_T *mp;
- mapblock_T *mp2;
int clen = 0; // length in characters
if (typebuf.tb_no_abbr_cnt) { // abbrev. are not recursive
@@ -1509,6 +1477,8 @@ bool check_abbr(int c, char *ptr, int col, int mincol)
return false;
}
+ int scol; // starting column of the abbr.
+
{
bool is_id = true;
bool vim_abbr;
@@ -1539,8 +1509,8 @@ bool check_abbr(int c, char *ptr, int col, int mincol)
if (scol < col) { // there is a word in front of the cursor
ptr += scol;
int len = col - scol;
- mp = curbuf->b_first_abbr;
- mp2 = first_abbr;
+ mapblock_T *mp = curbuf->b_first_abbr;
+ mapblock_T *mp2 = first_abbr;
if (mp == NULL) {
mp = mp2;
mp2 = NULL;
@@ -1715,18 +1685,13 @@ char *eval_map_expr(mapblock_T *mp, int c)
/// @param buf buffer for local mappings or NULL
int makemap(FILE *fd, buf_T *buf)
{
- mapblock_T *mp;
- char c1, c2, c3;
- char *p;
- char *cmd;
- int abbr;
- int hash;
bool did_cpo = false;
// Do the loop twice: Once for mappings, once for abbreviations.
// Then loop over all map hash lists.
- for (abbr = 0; abbr < 2; abbr++) {
- for (hash = 0; hash < 256; hash++) {
+ for (int abbr = 0; abbr < 2; abbr++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // there is only one abbr list
break;
@@ -1755,6 +1720,7 @@ int makemap(FILE *fd, buf_T *buf)
if (mp->m_luaref != LUA_NOREF) {
continue;
}
+ char *p;
for (p = mp->m_str; *p != NUL; p++) {
if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA
&& p[2] == KE_SNR) {
@@ -1768,14 +1734,10 @@ int makemap(FILE *fd, buf_T *buf)
// It's possible to create a mapping and then ":unmap" certain
// modes. We recreate this here by mapping the individual
// modes, which requires up to three of them.
- c1 = NUL;
- c2 = NUL;
- c3 = NUL;
- if (abbr) {
- cmd = "abbr";
- } else {
- cmd = "map";
- }
+ char c1 = NUL;
+ char c2 = NUL;
+ char c3 = NUL;
+ char *cmd = abbr ? "abbr" : "map";
switch (mp->m_mode) {
case MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
break;
@@ -1929,7 +1891,6 @@ int makemap(FILE *fd, buf_T *buf)
int put_escstr(FILE *fd, char *strstart, int what)
{
uint8_t *str = (uint8_t *)strstart;
- int c;
// :map xx <Nop>
if (*str == NUL && what == 1) {
@@ -1953,7 +1914,7 @@ int put_escstr(FILE *fd, char *strstart, int what)
continue;
}
- c = *str;
+ int c = *str;
// Special key codes have to be translated to be able to make sense
// when they are read back.
if (c == K_SPECIAL && what != 2) {
@@ -2030,14 +1991,13 @@ int put_escstr(FILE *fd, char *strstart, int what)
char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
int *local_ptr, int *rhs_lua)
{
- int len, minlen;
- mapblock_T *mp;
*rhs_lua = LUA_NOREF;
- len = (int)strlen(keys);
+ int len = (int)strlen(keys);
for (int local = 1; local >= 0; local--) {
// loop over all hash lists
for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
if (abbr) {
if (hash > 0) { // there is only one list.
break;
@@ -2062,7 +2022,7 @@ char *check_map(char *keys, int mode, int exact, int ign_mod, int abbr, mapblock
s += 3;
keylen -= 3;
}
- minlen = keylen < len ? keylen : len;
+ int minlen = keylen < len ? keylen : len;
if (strncmp(s, keys, (size_t)minlen) == 0) {
if (mp_ptr != NULL) {
*mp_ptr = mp;
@@ -2097,11 +2057,7 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
- if (map_to_exists(name, mode, abbr)) {
- rettv->vval.v_number = true;
- } else {
- rettv->vval.v_number = false;
- }
+ rettv->vval.v_number = map_to_exists(name, mode, abbr);
}
/// Fill a Dictionary with all applicable maparg() like dictionaries
@@ -2439,14 +2395,11 @@ void langmap_init(void)
/// changed at any time!
const char *did_set_langmap(optset_T *args)
{
- char *p;
- char *p2;
- int from, to;
-
- ga_clear(&langmap_mapga); // clear the previous map first
- langmap_init(); // back to one-to-one map
+ ga_clear(&langmap_mapga); // clear the previous map first
+ langmap_init(); // back to one-to-one map
- for (p = p_langmap; p[0] != NUL;) {
+ for (char *p = p_langmap; p[0] != NUL;) {
+ char *p2;
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
MB_PTR_ADV(p2)) {
if (p2[0] == '\\' && p2[1] != NUL) {
@@ -2466,8 +2419,8 @@ const char *did_set_langmap(optset_T *args)
if (p[0] == '\\' && p[1] != NUL) {
p++;
}
- from = utf_ptr2char(p);
- to = NUL;
+ int from = utf_ptr2char(p);
+ int to = NUL;
if (p2 == NULL) {
MB_PTR_ADV(p);
if (p[0] != ',') {
@@ -2524,9 +2477,8 @@ const char *did_set_langmap(optset_T *args)
static void do_exmap(exarg_T *eap, int isabbrev)
{
- int mode;
char *cmdp = eap->cmd;
- mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
+ int mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP
: (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP,
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index d8b8dbba29..627efa9e96 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -1178,6 +1178,9 @@ void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
}
if (internal) {
+ if (key.pos.row == newpos.row && key.pos.col == newpos.col) {
+ return;
+ }
key.pos = newpos;
bool match;
// tricky: could minimize movement in either direction better
@@ -1185,14 +1188,14 @@ void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col)
if (!match) {
new_i++;
}
- if (new_i == itr->i || key_cmp(key, x->key[new_i]) == 0) {
+ if (new_i == itr->i) {
x->key[itr->i].pos = newpos;
} else if (new_i < itr->i) {
memmove(&x->key[new_i + 1], &x->key[new_i], sizeof(MTKey) * (size_t)(itr->i - new_i));
x->key[new_i] = key;
} else if (new_i > itr->i) {
- memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(MTKey) * (size_t)(new_i - itr->i));
- x->key[new_i] = key;
+ memmove(&x->key[itr->i], &x->key[itr->i + 1], sizeof(MTKey) * (size_t)(new_i - itr->i - 1));
+ x->key[new_i - 1] = key;
}
return;
}
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index b8c80cadf5..e35385dd43 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1849,7 +1849,6 @@ 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);
@@ -1864,7 +1863,6 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
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;
@@ -1901,22 +1899,12 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
// 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 = ' ';
}
}
- 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..d730f247a9 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -4513,7 +4513,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 df90fadc97..c34999ed32 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1970,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/options.lua b/src/nvim/options.lua
index f0666101b8..3221e5b6e9 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -566,7 +566,7 @@ return {
backups if you don't care about losing the file.
Note that environment variables are not expanded. If you want to use
- $HOME you must expand it explicitly, e.g.: >
+ $HOME you must expand it explicitly, e.g.: >vim
:let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
< Note that the default also makes sure that "crontab -e" works (when a
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/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/spell.c b/src/nvim/spell.c
index 72e21a9130..38e045a08b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1280,7 +1280,7 @@ static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_ln
decor_redraw_line(wp, lnum - 1, &decor_state);
*decor_lnum = lnum;
}
- decor_redraw_col(wp, col, col, false, &decor_state);
+ decor_redraw_col(wp, col, 0, false, &decor_state);
return decor_state.spell;
}
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 30c98a2f1e..4e000b8d29 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -1663,7 +1663,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..9fea6442a9 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"
@@ -675,15 +676,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 +704,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 +832,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 +850,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 +979,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 +1278,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 +1286,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 +1404,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 +1454,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 +1477,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_compositor.c b/src/nvim/ui_compositor.c
index e9b23d1298..fcb63801e5 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,19 +395,18 @@ 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;
}
@@ -568,7 +566,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/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,