aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/deprecated.c1
-rw-r--r--src/nvim/api/options.c4
-rw-r--r--src/nvim/api/private/converter.c1
-rw-r--r--src/nvim/api/ui.c16
-rw-r--r--src/nvim/api/win_config.c2
-rw-r--r--src/nvim/arabic.c3
-rw-r--r--src/nvim/autocmd.c1
-rw-r--r--src/nvim/base64.c6
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/buffer_defs.h6
-rw-r--r--src/nvim/change.c17
-rw-r--r--src/nvim/charset.c19
-rw-r--r--src/nvim/cmdexpand.c2
-rw-r--r--src/nvim/cmdhist.c3
-rw-r--r--src/nvim/context.c2
-rw-r--r--src/nvim/cursor.c4
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/drawline.c259
-rw-r--r--src/nvim/edit.c17
-rw-r--r--src/nvim/eval.c3
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval/funcs.c33
-rw-r--r--src/nvim/eval/vars.c2
-rw-r--r--src/nvim/eval/window.c3
-rw-r--r--src/nvim/event/libuv_process.c1
-rw-r--r--src/nvim/event/loop.c2
-rw-r--r--src/nvim/event/loop.h1
-rw-r--r--src/nvim/event/process.c2
-rw-r--r--src/nvim/event/socket.c1
-rw-r--r--src/nvim/event/stream.c1
-rw-r--r--src/nvim/ex_cmds.c118
-rw-r--r--src/nvim/ex_docmd.c3
-rw-r--r--src/nvim/ex_eval.c1
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/extmark.c1
-rw-r--r--src/nvim/globals.h6
-rw-r--r--src/nvim/grid.c65
-rw-r--r--src/nvim/grid_defs.h6
-rw-r--r--src/nvim/indent.c1
-rw-r--r--src/nvim/input.c3
-rw-r--r--src/nvim/insexpand.c25
-rw-r--r--src/nvim/linematch.c1
-rw-r--r--src/nvim/lua/base64.c5
-rw-r--r--src/nvim/lua/executor.c12
-rw-r--r--src/nvim/lua/stdlib.c2
-rw-r--r--src/nvim/lua/xdiff.c1
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/match.c2
-rw-r--r--src/nvim/mbyte.c123
-rw-r--r--src/nvim/mbyte.h1
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/message.c12
-rw-r--r--src/nvim/move.c14
-rw-r--r--src/nvim/normal.c12
-rw-r--r--src/nvim/option_vars.h1
-rw-r--r--src/nvim/optionstr.c5
-rw-r--r--src/nvim/path.c1
-rw-r--r--src/nvim/plines.c6
-rw-r--r--src/nvim/quickfix.c6
-rw-r--r--src/nvim/regexp.c59
-rw-r--r--src/nvim/spellsuggest.c2
-rw-r--r--src/nvim/tui/input.c138
-rw-r--r--src/nvim/tui/input.h18
-rw-r--r--src/nvim/tui/tui.c219
-rw-r--r--src/nvim/tui/tui.h16
-rw-r--r--src/nvim/ui.c1
-rw-r--r--src/nvim/window.c274
-rw-r--r--src/nvim/winfloat.c288
-rw-r--r--src/nvim/winfloat.h8
70 files changed, 906 insertions, 980 deletions
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 8398a3a5b1..ff9f8ff18e 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,4 +1,3 @@
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 61debb70fe..b0053dbb34 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -1,7 +1,6 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
-#include <stdlib.h>
#include <string.h>
#include "nvim/api/keysets.h"
@@ -13,9 +12,10 @@
#include "nvim/buffer.h"
#include "nvim/eval/window.h"
#include "nvim/globals.h"
+#include "nvim/macros.h"
#include "nvim/memory.h"
#include "nvim/option.h"
-#include "nvim/types.h"
+#include "nvim/option_vars.h"
#include "nvim/vim.h"
#include "nvim/window.h"
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 1188b04bdc..6e6d054374 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -2,7 +2,6 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <stdlib.h>
#include "klib/kvec.h"
#include "nvim/api/private/converter.h"
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index c898925af8..c1fc986029 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -2,6 +2,7 @@
#include <inttypes.h>
#include <msgpack/pack.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -512,12 +513,13 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
ui->pum_pos = true;
}
-/// Tells Nvim when a terminal event has occurred: sets |v:termresponse| and fires |TermResponse|.
+/// Tells Nvim when a terminal event has occurred
///
/// The following terminal events are supported:
///
-/// - "osc_response": The terminal sent a OSC response sequence to Nvim. The
-/// payload is the received OSC sequence.
+/// - "termresponse": The terminal sent an OSC or DCS response sequence to
+/// Nvim. The payload is the received response. Sets
+/// |v:termresponse| and fires |TermResponse|.
///
/// @param channel_id
/// @param event Event name
@@ -526,14 +528,14 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
{
- if (strequal("osc_response", event.data)) {
+ if (strequal("termresponse", event.data)) {
if (value.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation, "osc_response must be a string");
+ api_set_error(err, kErrorTypeValidation, "termresponse must be a string");
return;
}
- const String osc_response = value.data.string;
- set_vim_var_string(VV_TERMRESPONSE, osc_response.data, (ptrdiff_t)osc_response.size);
+ const String termresponse = value.data.string;
+ set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, false, AUGROUP_ALL, NULL, NULL, &value);
}
}
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 83c8ba832c..4b16e26103 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,5 +1,4 @@
#include <stdbool.h>
-#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
@@ -25,6 +24,7 @@
#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/win_config.c.generated.h"
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 50ef761066..226b042471 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -19,14 +19,11 @@
#include <stdbool.h>
#include <stddef.h>
-#include <stdint.h>
#include "nvim/arabic.h"
#include "nvim/ascii.h"
#include "nvim/macros.h"
-#include "nvim/mbyte.h"
#include "nvim/option_vars.h"
-#include "nvim/vim.h"
// Unicode values for Arabic characters.
enum {
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 80573696d4..65e902ed4b 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -48,6 +48,7 @@
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "auevents_name_map.generated.h"
diff --git a/src/nvim/base64.c b/src/nvim/base64.c
index c647019fb1..5bc0c34f06 100644
--- a/src/nvim/base64.c
+++ b/src/nvim/base64.c
@@ -1,7 +1,9 @@
#include <assert.h>
#include <stddef.h>
+#include <stdint.h>
#include <string.h>
+#include "auto/config.h"
#include "nvim/base64.h"
#include "nvim/memory.h"
@@ -9,6 +11,10 @@
# include ENDIAN_INCLUDE_FILE
#endif
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "base64.c.generated.h" // IWYU pragma: export
+#endif
+
static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Indices are 1-based because we use 0 to indicate a letter that is not part of the alphabet
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8c522a6c44..deec0662a1 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -84,6 +84,7 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
@@ -101,6 +102,7 @@
#include "nvim/version.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer.c.generated.h"
@@ -4154,6 +4156,10 @@ bool buf_contents_changed(buf_T *buf)
aco_save_T aco;
aucmd_prepbuf(&aco, newbuf);
+ // We don't want to trigger autocommands now, they may have nasty
+ // side-effects like wiping buffers
+ block_autocmds();
+
if (ml_open(curbuf) == OK
&& readfile(buf->b_ffname, buf->b_fname,
0, 0, (linenr_T)MAXLNUM,
@@ -4178,6 +4184,8 @@ bool buf_contents_changed(buf_T *buf)
wipe_buffer(newbuf, false);
}
+ unblock_autocmds();
+
return differ;
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 73fad15239..039484d2e2 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -264,9 +264,6 @@ struct wininfo_S {
#define SYNFLD_START 0 // use level of item at start of line
#define SYNFLD_MINIMUM 1 // use lowest local minimum level on line
-// avoid #ifdefs for when b_spell is not available
-#define B_SPELL(buf) ((buf)->b_spell)
-
typedef struct qf_info_S qf_info_T;
// Used for :syntime: timing of executing a syntax pattern.
@@ -695,8 +692,7 @@ struct file_buffer {
bool b_help; // true for help file buffer (when set b_p_bt
// is "help")
bool b_spell; // True for a spell file buffer, most fields
- // are not used! Use the B_SPELL macro to
- // access b_spell without #ifdef.
+ // are not used!
char *b_prompt_text; // set by prompt_setprompt()
Callback b_prompt_callback; // set by prompt_setcallback()
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 46c21da384..aa58779f5b 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -262,9 +262,9 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum
&& (last < wp->w_topline
|| (wp->w_topline >= lnum
&& wp->w_topline < lnume
- && win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), (colnr_T)MAXCOL)
- <= (unsigned)(wp->w_skipcol + sms_marker_overlap(wp, win_col_off(wp)
- - win_col_off2(wp)))))) {
+ && win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), MAXCOL)
+ <= (wp->w_skipcol
+ + sms_marker_overlap(wp, win_col_off(wp) - win_col_off2(wp)))))) {
wp->w_skipcol = 0;
}
@@ -665,7 +665,7 @@ void ins_bytes_len(char *p, size_t len)
/// convert bytes to a character.
void ins_char(int c)
{
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
size_t n = (size_t)utf_char2bytes(c, buf);
// When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
@@ -869,12 +869,9 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// If 'delcombine' is set and deleting (less than) one character, only
// delete the last combining character.
- if (p_deco && use_delcombine
- && utfc_ptr2len(oldp + col) >= count) {
- int cc[MAX_MCO];
-
- (void)utfc_ptr2char(oldp + col, cc);
- if (cc[0] != NUL) {
+ if (p_deco && use_delcombine && utfc_ptr2len(oldp + col) >= count) {
+ char *p0 = oldp + col;
+ if (utf_composinglike(p0, p0 + utf_ptr2len(p0))) {
// Find the last composing char, there can be several.
int n = col;
do {
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 0adcc09ec7..5dfc9c444d 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -302,15 +302,13 @@ size_t transstr_len(const char *const s, bool untab)
while (*p) {
const size_t l = (size_t)utfc_ptr2len(p);
if (l > 1) {
- int pcc[MAX_MCO + 1];
- pcc[0] = utfc_ptr2char(p, &pcc[1]);
-
- if (vim_isprintc(pcc[0])) {
+ if (vim_isprintc(utf_ptr2char(p))) {
len += l;
} else {
- for (size_t i = 0; i < ARRAY_SIZE(pcc) && pcc[i]; i++) {
+ for (size_t off = 0; off < l; off += (size_t)utf_ptr2len(p + off)) {
+ int c = utf_ptr2char(p + off);
char hexbuf[9];
- len += transchar_hex(hexbuf, pcc[i]);
+ len += transchar_hex(hexbuf, c);
}
}
p += l;
@@ -349,16 +347,15 @@ size_t transstr_buf(const char *const s, const ssize_t slen, char *const buf, co
if (buf_p + l > buf_e) {
break; // Exceeded `buf` size.
}
- int pcc[MAX_MCO + 1];
- pcc[0] = utfc_ptr2char(p, &pcc[1]);
- if (vim_isprintc(pcc[0])) {
+ if (vim_isprintc(utf_ptr2char(p))) {
memmove(buf_p, p, l);
buf_p += l;
} else {
- for (size_t i = 0; i < ARRAY_SIZE(pcc) && pcc[i]; i++) {
+ for (size_t off = 0; off < l; off += (size_t)utf_ptr2len(p + off)) {
+ int c = utf_ptr2char(p + off);
char hexbuf[9]; // <up to 6 bytes>NUL
- const size_t hexlen = transchar_hex(hexbuf, pcc[i]);
+ const size_t hexlen = transchar_hex(hexbuf, c);
if (buf_p + hexlen > buf_e) {
break;
}
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index cba06976c9..487a3ec482 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -7,7 +7,6 @@
#include <stdlib.h>
#include <string.h>
-#include "auto/config.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
@@ -50,7 +49,6 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index e2916d1641..1f1d7d2eab 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -21,7 +21,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
-#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
#include "nvim/types.h"
@@ -31,8 +30,6 @@
# include "cmdhist.c.generated.h"
#endif
-static const char e_val_too_large[] = N_("E1510: Value too large: %s");
-
static histentry_T *(history[HIST_COUNT]) = { NULL, NULL, NULL, NULL, NULL };
static int hisidx[HIST_COUNT] = { -1, -1, -1, -1, -1 }; ///< lastused entry
/// identifying (unique) number of newest history entry
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 3114fc8ab5..857f26af37 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -16,11 +16,9 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
-#include "nvim/gettext.h"
#include "nvim/hashtab.h"
#include "nvim/keycodes.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/shada.h"
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 4e8457eb2d..32ee1d6c08 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -111,7 +111,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
col = wcol;
if ((addspaces || finetune) && !VIsual_active) {
- curwin->w_curswant = (int)linetabsize(curwin, pos->lnum) + one_more;
+ curwin->w_curswant = linetabsize(curwin, pos->lnum) + one_more;
if (curwin->w_curswant > 0) {
curwin->w_curswant--;
}
@@ -125,7 +125,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
&& curwin->w_width_inner != 0
&& wcol >= (colnr_T)width
&& width > 0) {
- csize = (int)linetabsize(curwin, pos->lnum);
+ csize = linetabsize(curwin, pos->lnum);
if (csize > 0) {
csize--;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index bc0ce99c5e..1bff78f90a 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -1654,7 +1654,7 @@ static void registerdigraph(int char1, int char2, int n)
bool check_digraph_chars_valid(int char1, int char2)
{
if (char2 == 0) {
- char msg[MB_MAXBYTES + 1];
+ char msg[MB_MAXCHAR + 1];
msg[utf_char2bytes(char1, msg)] = NUL;
semsg(_(e_digraph_must_be_just_two_characters_str), msg);
return false;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index d08154e24e..172c72145b 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -228,14 +228,12 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells
const char *p = *pp;
int cells = utf_ptr2cells(p);
int c_len = utfc_ptr2len(p);
- int u8c, u8cc[MAX_MCO];
assert(maxcells > 0);
if (cells > maxcells) {
dest[0] = schar_from_ascii(' ');
return 1;
}
- u8c = utfc_ptr2char(p, u8cc);
if (*p == TAB) {
cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
}
@@ -247,16 +245,14 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells
for (int c = 0; c < cells; c++) {
dest[c] = schar_from_ascii(' ');
}
- goto done;
- } else if ((uint8_t)(*p) < 0x80 && u8cc[0] == 0) {
- dest[0] = schar_from_ascii(*p);
} else {
- dest[0] = schar_from_cc(u8c, u8cc);
- }
- if (cells > 1) {
- dest[1] = 0;
+ int u8c;
+ dest[0] = utfc_ptr2schar(p, &u8c);
+ if (cells > 1) {
+ dest[1] = 0;
+ }
}
-done:
+
*pp += c_len;
return cells;
}
@@ -897,16 +893,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
}
}
-static bool check_mb_utf8(int *c, int *u8cc)
-{
- if (utf_char2len(*c) > 1) {
- *u8cc = 0;
- *c = 0xc0;
- return true;
- }
- return false;
-}
-
static colnr_T get_trailcol(win_T *wp, const char *ptr, const char *line)
{
colnr_T trailcol = MAXCOL;
@@ -1002,7 +988,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
{
winlinevars_T wlv; // variables passed between functions
- int c = 0; // init for GCC
colnr_T vcol_prev = -1; // "wlv.vcol" of previous character
char *line; // current line
char *ptr; // current position in "line"
@@ -1047,8 +1032,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int multi_attr = 0; // attributes desired by multibyte
int mb_l = 1; // multi-byte byte length
int mb_c = 0; // decoded multi-byte character
- bool mb_utf8 = false; // screen char is UTF-8 char
- int u8cc[MAX_MCO]; // composing UTF-8 chars
+ schar_T mb_schar; // complete screen char
int change_start = MAXCOL; // first col of changed area
int change_end = -1; // last col of changed area
bool in_multispace = false; // in multiple consecutive spaces
@@ -1898,34 +1882,25 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// For the '$' of the 'list' option, n_extra == 1, p_extra == "".
if (wlv.n_extra > 0) {
if (wlv.c_extra != NUL || (wlv.n_extra == 1 && wlv.c_final != NUL)) {
- c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra;
- mb_c = c; // doesn't handle non-utf-8 multi-byte!
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra;
+ mb_schar = schar_from_char(mb_c);
+ wlv.n_extra--;
} else {
assert(wlv.p_extra != NULL);
- c = (uint8_t)(*wlv.p_extra);
- mb_c = c;
- // If the UTF-8 character is more than one byte:
- // Decode it into "mb_c".
mb_l = utfc_ptr2len(wlv.p_extra);
- mb_utf8 = false;
- if (mb_l > wlv.n_extra) {
- mb_l = 1;
- } else if (mb_l > 1) {
- mb_c = utfc_ptr2char(wlv.p_extra, u8cc);
- mb_utf8 = true;
- c = 0xc0;
- }
- if (mb_l == 0) { // at the NUL at end-of-line
+ mb_schar = utfc_ptr2schar(wlv.p_extra, &mb_c);
+ // mb_l=0 at the end-of-line NUL
+ if (mb_l > wlv.n_extra || mb_l == 0) {
mb_l = 1;
}
// If a double-width char doesn't fit display a '>' in the last column.
+ // Don't advance the pointer but put the character at the start of the next line.
if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
+ mb_c = '>';
mb_l = 1;
(void)mb_l;
+ mb_schar = schar_from_ascii(mb_c);
multi_attr = win_hl_attr(wp, HLF_AT);
if (wlv.cul_attr) {
@@ -1933,18 +1908,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
? hl_combine_attr(wlv.cul_attr, multi_attr)
: hl_combine_attr(multi_attr, wlv.cul_attr);
}
-
- // put the pointer back to output the double-width
- // character at the start of the next line.
- wlv.n_extra++;
- wlv.p_extra--;
} else {
- wlv.n_extra -= mb_l - 1;
- wlv.p_extra += mb_l - 1;
+ wlv.n_extra -= mb_l;
+ wlv.p_extra += mb_l;
}
- wlv.p_extra++;
}
- wlv.n_extra--;
// Only restore search_attr and area_attr after "n_extra" in
// the next screen line is also done.
@@ -1973,58 +1941,40 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
} else if (has_fold) {
// skip writing the buffer line itself
- c = NUL;
+ mb_c = NUL;
} else {
- int c0;
char *prev_ptr = ptr;
- // Get a character from the line itself.
- c0 = c = (uint8_t)(*ptr);
- mb_c = c;
-
- if (c == NUL) {
+ // first byte of next char
+ int c0 = (uint8_t)(*ptr);
+ if (c0 == NUL) {
// no more cells to skip
wlv.skip_cells = 0;
}
- // If the UTF-8 character is more than one byte: Decode it
- // into "mb_c".
+ // Get a character from the line itself.
mb_l = utfc_ptr2len(ptr);
- mb_utf8 = false;
- if (mb_l > 1) {
- mb_c = utfc_ptr2char(ptr, u8cc);
- // Overlong encoded ASCII or ASCII with composing char
- // is displayed normally, except a NUL.
- if (mb_c < 0x80) {
- c0 = c = mb_c;
- }
- mb_utf8 = true;
+ mb_schar = utfc_ptr2schar(ptr, &mb_c);
- // At start of the line we can have a composing char.
- // Draw it as a space with a composing char.
- if (utf_iscomposing(mb_c)) {
- for (int i = MAX_MCO - 1; i > 0; i--) {
- u8cc[i] = u8cc[i - 1];
- }
- u8cc[0] = mb_c;
- mb_c = ' ';
- }
+ // Overlong encoded ASCII or ASCII with composing char
+ // is displayed normally, except a NUL.
+ if (mb_l > 1 && mb_c < 0x80) {
+ c0 = mb_c;
}
- if ((mb_l == 1 && c >= 0x80)
+ if ((mb_l == 1 && c0 >= 0x80)
|| (mb_l >= 1 && mb_c == 0)
|| (mb_l > 1 && (!vim_isprintc(mb_c)))) {
// Illegal UTF-8 byte: display as <xx>.
- // Non-BMP character : display as ? or fullwidth ?.
+ // Non-printable character : display as ? or fullwidth ?.
transchar_hex(wlv.extra, mb_c);
if (wp->w_p_rl) { // reverse
rl_mirror_ascii(wlv.extra, NULL);
}
wlv.p_extra = wlv.extra;
- c = (uint8_t)(*wlv.p_extra);
mb_c = mb_ptr2char_adv((const char **)&wlv.p_extra);
- mb_utf8 = (c >= 0x80);
+ mb_schar = schar_from_char(mb_c);
wlv.n_extra = (int)strlen(wlv.p_extra);
wlv.c_extra = NUL;
wlv.c_final = NUL;
@@ -2040,10 +1990,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// last column; the character is displayed at the start of the
// next line.
if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
- mb_utf8 = false;
+ mb_c = '>';
mb_l = 1;
+ mb_schar = schar_from_ascii(mb_c);
multi_attr = win_hl_attr(wp, HLF_AT);
// Put pointer back so that the character will be
// displayed at the start of the next line.
@@ -2059,15 +2008,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_extra = 1;
wlv.c_extra = MB_FILLER_CHAR;
wlv.c_final = NUL;
- c = ' ';
+ mb_c = ' ';
+ mb_l = 1;
+ mb_schar = schar_from_ascii(mb_c);
if (area_attr == 0 && search_attr == 0) {
wlv.n_attr = wlv.n_extra + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
saved_attr2 = wlv.char_attr; // save current attr
}
- mb_c = c;
- mb_utf8 = false;
- mb_l = 1;
}
ptr++;
@@ -2106,11 +2054,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// no concealing past the end of the line, it interferes
// with line highlighting.
- if (c == NUL) {
- syntax_flags = 0;
- } else {
- syntax_flags = get_syntax_info(&syntax_seqnr);
- }
+ syntax_flags = (mb_c == 0) ? 0 : get_syntax_info(&syntax_seqnr);
}
if (has_decor && v > 0) {
@@ -2145,7 +2089,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
spell_attr = 0;
// do not calculate cap_col at the end of the line or when
// only white space is following
- if (c != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) {
+ if (mb_c != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) {
char *p;
hlf_T spell_hlf = HLF_COUNT;
v -= mb_l - 1;
@@ -2219,13 +2163,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
//
// So only allow to linebreak, once we have found chars not in
// 'breakat' in the line.
- if (wp->w_p_lbr && !wlv.need_lbr && c != NUL
+ if (wp->w_p_lbr && !wlv.need_lbr && mb_c != NUL
&& !vim_isbreak((uint8_t)(*ptr))) {
wlv.need_lbr = true;
}
// Found last space before word: check for line break.
- if (wp->w_p_lbr && c0 == c && wlv.need_lbr
- && vim_isbreak(c) && !vim_isbreak((uint8_t)(*ptr))) {
+ if (wp->w_p_lbr && c0 == mb_c && mb_c < 128 && wlv.need_lbr
+ && vim_isbreak(mb_c) && !vim_isbreak((uint8_t)(*ptr))) {
int mb_off = utf_head_off(line, ptr - 1);
char *p = ptr - (mb_off + 1);
chartabsize_T cts;
@@ -2236,33 +2180,33 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
clear_chartabsize_arg(&cts);
- if (on_last_col && c != TAB) {
+ if (on_last_col && mb_c != TAB) {
// Do not continue search/match highlighting over the
// line break, but for TABs the highlighting should
// include the complete width of the character
search_attr = 0;
}
- if (c == TAB && wlv.n_extra + wlv.col > grid->cols) {
+ if (mb_c == TAB && wlv.n_extra + wlv.col > grid->cols) {
wlv.n_extra = tabstop_padding(wlv.vcol, wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array) - 1;
}
wlv.c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
wlv.c_final = NUL;
- if (ascii_iswhite(c)) {
- if (c == TAB) {
+ if (mb_c < 128 && ascii_iswhite(mb_c)) {
+ if (mb_c == TAB) {
// See "Tab alignment" below.
FIX_FOR_BOGUSCOLS;
}
if (!wp->w_p_list) {
- c = ' ';
+ mb_c = ' ';
+ mb_schar = schar_from_ascii(mb_c);
}
}
}
if (wp->w_p_list) {
- in_multispace = c == ' ' && (*ptr == ' '
- || (prev_ptr > line && prev_ptr[-1] == ' '));
+ in_multispace = mb_c == ' ' && (*ptr == ' ' || (prev_ptr > line && prev_ptr[-1] == ' '));
if (!in_multispace) {
multispace_pos = 0;
}
@@ -2272,61 +2216,56 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// But not when the character is followed by a composing
// character (use mb_l to check that).
if (wp->w_p_list
- && ((((c == 160 && mb_l == 1)
- || (mb_utf8
- && ((mb_c == 160 && mb_l == 2)
- || (mb_c == 0x202f && mb_l == 3))))
+ && ((((mb_c == 160 && mb_l == 2) || (mb_c == 0x202f && mb_l == 3))
&& wp->w_p_lcs_chars.nbsp)
- || (c == ' '
+ || (mb_c == ' '
&& mb_l == 1
&& (wp->w_p_lcs_chars.space
|| (in_multispace && wp->w_p_lcs_chars.multispace != NULL))
&& ptr - line >= leadcol
&& ptr - line <= trailcol))) {
if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
- c = wp->w_p_lcs_chars.multispace[multispace_pos++];
+ mb_c = wp->w_p_lcs_chars.multispace[multispace_pos++];
if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
multispace_pos = 0;
}
} else {
- c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
+ mb_c = (mb_c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
}
wlv.n_attr = 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_schar = schar_from_char(mb_c);
}
- if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
- || (leadcol != 0 && ptr < line + leadcol))) {
+ if (mb_c == ' ' && mb_l == 1 && ((trailcol != MAXCOL && ptr > line + trailcol)
+ || (leadcol != 0 && ptr < line + leadcol))) {
if (leadcol != 0 && in_multispace && ptr < line + leadcol
&& wp->w_p_lcs_chars.leadmultispace != NULL) {
- c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
+ mb_c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
multispace_pos = 0;
}
} else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
- c = wp->w_p_lcs_chars.trail;
+ mb_c = wp->w_p_lcs_chars.trail;
} else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
- c = wp->w_p_lcs_chars.lead;
+ mb_c = wp->w_p_lcs_chars.lead;
} else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
- c = wp->w_p_lcs_chars.space;
+ mb_c = wp->w_p_lcs_chars.space;
}
wlv.n_attr = 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_schar = schar_from_char(mb_c);
}
}
// Handling of non-printable characters.
- if (!vim_isprintc(c)) {
+ if (!vim_isprintc(mb_c)) {
// when getting a character from the file, we may have to
// turn it into something else on the way to putting it on the screen.
- if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ if (mb_c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
int tab_len = 0;
colnr_T vcol_adjusted = wlv.vcol; // removed showbreak length
char *const sbr = get_showbreak_value(wp);
@@ -2369,7 +2308,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (wlv.n_extra > 0) {
len += wlv.n_extra - tab_len;
}
- c = wp->w_p_lcs_chars.tab1;
+ mb_c = wp->w_p_lcs_chars.tab1;
p = get_extra_buf((size_t)len + 1);
memset(p, ' ', (size_t)len);
p[len] = NUL;
@@ -2417,11 +2356,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
}
- mb_utf8 = false; // don't draw as UTF-8
if (wp->w_p_list) {
- c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
- ? wp->w_p_lcs_chars.tab3
- : wp->w_p_lcs_chars.tab1;
+ mb_c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
+ ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1;
if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) {
wlv.c_extra = NUL; // using p_extra from above
} else {
@@ -2431,14 +2368,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_attr = tab_len + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
} else {
wlv.c_final = NUL;
wlv.c_extra = ' ';
- c = ' ';
+ mb_c = ' ';
}
- } else if (c == NUL
+ mb_schar = schar_from_char(mb_c);
+ } else if (mb_c == NUL
&& (wp->w_p_list
|| ((wlv.fromcol >= 0 || fromcol_prev >= 0)
&& wlv.tocol > wlv.vcol
@@ -2462,20 +2398,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_extra = 0;
}
if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
- c = wp->w_p_lcs_chars.eol;
+ mb_c = wp->w_p_lcs_chars.eol;
} else {
- c = ' ';
+ mb_c = ' ';
}
lcs_eol_one = -1;
ptr--; // put it back at the NUL
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
wlv.n_attr = 1;
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
- } else if (c != NUL) {
- wlv.p_extra = transchar_buf(wp->w_buffer, c);
+ mb_schar = schar_from_char(mb_c);
+ } else if (mb_c != NUL) {
+ wlv.p_extra = transchar_buf(wp->w_buffer, mb_c);
if (wlv.n_extra == 0) {
- wlv.n_extra = byte2cells(c) - 1;
+ wlv.n_extra = byte2cells(mb_c) - 1;
}
if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>"
@@ -2485,7 +2420,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (wp->w_p_lbr) {
char *p;
- c = (uint8_t)(*wlv.p_extra);
+ mb_c = (uint8_t)(*wlv.p_extra);
p = get_extra_buf((size_t)wlv.n_extra + 1);
memset(p, ' ', (size_t)wlv.n_extra);
strncpy(p, // NOLINT(runtime/printf)
@@ -2494,20 +2429,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
p[wlv.n_extra] = NUL;
wlv.p_extra = p;
} else {
- wlv.n_extra = byte2cells(c) - 1;
- c = (uint8_t)(*wlv.p_extra++);
+ wlv.n_extra = byte2cells(mb_c) - 1;
+ mb_c = (uint8_t)(*wlv.p_extra++);
}
wlv.n_attr = wlv.n_extra + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_8);
saved_attr2 = wlv.char_attr; // save current attr
- mb_utf8 = false; // don't draw as UTF-8
+ mb_schar = schar_from_ascii(mb_c);
} else if (VIsual_active
&& (VIsual_mode == Ctrl_V || VIsual_mode == 'v')
&& virtual_active()
&& wlv.tocol != MAXCOL
&& wlv.vcol < wlv.tocol
&& wlv.col < grid->cols) {
- c = ' ';
+ mb_c = ' ';
+ mb_schar = schar_from_char(mb_c);
ptr--; // put it back at the NUL
}
}
@@ -2527,18 +2463,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// First time at this concealed item: display one
// character.
if (has_match_conc && match_conc) {
- c = match_conc;
+ mb_c = match_conc;
} else if (decor_conceal && decor_state.conceal_char) {
- c = decor_state.conceal_char;
+ mb_c = decor_state.conceal_char;
if (decor_state.conceal_attr) {
wlv.char_attr = decor_state.conceal_attr;
}
} else if (syn_get_sub_char() != NUL) {
- c = syn_get_sub_char();
+ mb_c = syn_get_sub_char();
} else if (wp->w_p_lcs_chars.conceal != NUL) {
- c = wp->w_p_lcs_chars.conceal;
+ mb_c = wp->w_p_lcs_chars.conceal;
} else {
- c = ' ';
+ mb_c = ' ';
}
prev_syntax_id = syntax_seqnr;
@@ -2557,8 +2493,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
is_concealing = true;
wlv.skip_cells = 1;
}
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_schar = schar_from_char(mb_c);
} else {
prev_syntax_id = 0;
is_concealing = false;
@@ -2601,8 +2536,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0)
&& wlv.filler_todo <= 0
&& wlv.draw_state > WL_STC
- && c != NUL) {
- c = wp->w_p_lcs_chars.prec;
+ && mb_c != NUL) {
+ mb_c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
if (utf_char2cells(mb_c) > 1) {
// Double-width character being overwritten by the "precedes"
@@ -2613,15 +2548,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_attr = 2;
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
}
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_schar = schar_from_char(mb_c);
saved_attr3 = wlv.char_attr; // save current attr
wlv.char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
n_attr3 = 1;
}
// At end of the text line or just after the last character.
- if (c == NUL && eol_hl_off == 0) {
+ if (mb_c == NUL && eol_hl_off == 0) {
// flag to indicate whether prevcol equals startcol of search_hl or
// one of the matches
bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl,
@@ -2675,7 +2609,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// At end of the text line.
- if (c == NUL) {
+ if (mb_c == NUL) {
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap) {
v = wlv.startrow == 0 ? wp->w_skipcol : 0;
@@ -2821,10 +2755,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|| lcs_eol_one > 0
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
|| has_more_inline_virt(&wlv, v)) {
- c = wp->w_p_lcs_chars.ext;
+ mb_c = wp->w_p_lcs_chars.ext;
wlv.char_attr = win_hl_attr(wp, HLF_AT);
- mb_c = c;
- mb_utf8 = check_mb_utf8(&c, u8cc);
+ mb_schar = schar_from_char(mb_c);
}
}
@@ -2870,11 +2803,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// Skip characters that are left of the screen for 'nowrap'.
if (wlv.draw_state < WL_LINE || wlv.skip_cells <= 0) {
// Store the character.
- if (mb_utf8) {
- linebuf_char[wlv.off] = schar_from_cc(mb_c, u8cc);
- } else {
- linebuf_char[wlv.off] = schar_from_ascii((char)c);
- }
+ linebuf_char[wlv.off] = mb_schar;
if (multi_attr) {
linebuf_attr[wlv.off] = multi_attr;
multi_attr = 0;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index ce547b55fe..eb5ea2c873 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1462,7 +1462,7 @@ void edit_putchar(int c, bool highlight)
pc_status = PC_STATUS_SET;
}
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr);
grid_line_flush();
}
@@ -2176,7 +2176,7 @@ void insertchar(int c, int flags, int second_indent)
int cc;
if ((cc = utf_char2len(c)) > 1) {
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
utf_char2bytes(c, buf);
buf[cc] = NUL;
@@ -3681,7 +3681,6 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
int cc;
int temp = 0; // init for GCC
bool did_backspace = false;
- int cpc[MAX_MCO]; // composing characters
bool call_fix_indent = false;
// can't delete anything in an empty file
@@ -3910,15 +3909,15 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
if (State & REPLACE_FLAG) {
replace_do_bs(-1);
} else {
- const int l_p_deco = p_deco;
- if (l_p_deco) {
- (void)utfc_ptr2char(get_cursor_pos_ptr(), cpc);
+ bool has_composing = false;
+ if (p_deco) {
+ char *p0 = get_cursor_pos_ptr();
+ has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0));
}
(void)del_char(false);
// If there are combining characters and 'delcombine' is set
- // move the cursor back. Don't back up before the base
- // character.
- if (l_p_deco && cpc[0] != NUL) {
+ // move the cursor back. Don't back up before the base character.
+ if (has_composing) {
inc_cursor();
}
if (revins_chars) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6d2c276df4..c073f30547 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2,7 +2,6 @@
#include <assert.h>
#include <ctype.h>
-#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -7118,7 +7117,7 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
/// Set v:char to character "c".
void set_vim_var_char(int c)
{
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
buf[utf_char2bytes(c, buf)] = NUL;
set_vim_var_string(VV_CHAR, buf, -1);
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 90e51c5c12..d4bf52c619 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index c6909245af..8ef208f291 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -5134,7 +5134,7 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- char buf[MB_MAXBYTES];
+ char buf[MB_MAXCHAR];
const int len = utf_char2bytes((int)num, buf);
rettv->v_type = VAR_STRING;
@@ -6891,7 +6891,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- char buf[MB_MAXBYTES + 1];
+ char buf[MAX_SCHAR_SIZE + 1];
schar_get(buf, grid_getchar(grid, row, col, NULL));
c = utf_ptr2char(buf);
}
@@ -6907,24 +6907,22 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col);
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
- tv_list_alloc_ret(rettv, 0);
return;
}
- char buf[MB_MAXBYTES + 1];
+ char buf[MAX_SCHAR_SIZE + 1];
schar_get(buf, grid_getchar(grid, row, col, NULL));
- int pcc[MAX_MCO];
- int c = utfc_ptr2char(buf, pcc);
- int composing_len = 0;
- while (composing_len < MAX_MCO && pcc[composing_len] != 0) {
- composing_len++;
- }
- tv_list_alloc_ret(rettv, composing_len + 1);
- tv_list_append_number(rettv->vval.v_list, c);
- for (int i = 0; i < composing_len; i++) {
- tv_list_append_number(rettv->vval.v_list, pcc[i]);
- }
+
+ // schar values are already processed chars which are always NUL-terminated.
+ // A single [0] is expected when char is NUL.
+ size_t i = 0;
+ do {
+ int c = utf_ptr2char(buf + i);
+ tv_list_append_number(rettv->vval.v_list, c);
+ i += (size_t)utf_ptr2len(buf + i);
+ } while (buf[i] != NUL);
}
/// "screencol()" function
@@ -6957,7 +6955,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
return;
}
- char buf[MB_MAXBYTES + 1];
+ char buf[MAX_SCHAR_SIZE + 1];
schar_get(buf, grid_getchar(grid, row, col, NULL));
rettv->vval.v_string = xstrdup(buf);
}
@@ -7413,8 +7411,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
char *const csearch = tv_dict_get_string(d, "char", false);
if (csearch != NULL) {
- int pcc[MAX_MCO];
- const int c = utfc_ptr2char(csearch, pcc);
+ int c = utf_ptr2char(csearch);
set_last_csearch(c, csearch, utfc_ptr2len(csearch));
}
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 8cc3903f7a..3fd33720c9 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -17,7 +17,6 @@
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
@@ -34,6 +33,7 @@
#include "nvim/message.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/search.h"
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index c0607a4a34..bcc29dfeed 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -26,6 +26,7 @@
#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/window.c.generated.h"
@@ -635,7 +636,7 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (wp == NULL || targetwin == NULL || wp == targetwin
|| !win_valid(wp) || !win_valid(targetwin)
- || win_valid_floating(wp) || win_valid_floating(targetwin)) {
+ || win_float_valid(wp) || win_float_valid(targetwin)) {
emsg(_(e_invalwindow));
rettv->vval.v_number = -1;
return;
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index 73dec2bcab..85fec65177 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -8,7 +8,6 @@
#include "nvim/event/process.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
-#include "nvim/macros.h"
#include "nvim/os/os.h"
#include "nvim/ui_client.h"
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 3d74fe7d6d..d61666e6d4 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -17,6 +17,7 @@ void loop_init(Loop *loop, void *data)
{
uv_loop_init(&loop->uv);
loop->recursive = 0;
+ loop->closing = false;
loop->uv.data = loop;
loop->children = kl_init(WatcherPtr);
loop->events = multiqueue_new_parent(loop_on_put, loop);
@@ -149,6 +150,7 @@ static void loop_walk_cb(uv_handle_t *handle, void *arg)
bool loop_close(Loop *loop, bool wait)
{
bool rv = true;
+ loop->closing = true;
uv_mutex_destroy(&loop->mutex);
uv_close((uv_handle_t *)&loop->children_watcher, NULL);
uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index 58216f7ec3..977ed8a1ee 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -40,6 +40,7 @@ typedef struct loop {
uv_async_t async;
uv_mutex_t mutex;
int recursive;
+ bool closing; ///< Set to true if loop_close() has been called
} Loop;
#define CREATE_EVENT(multiqueue, handler, argc, ...) \
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index a6646c3a7f..b69612337c 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -1,7 +1,6 @@
#include <assert.h>
#include <inttypes.h>
#include <signal.h>
-#include <stdlib.h>
#include <uv.h>
#include "klib/klist.h"
@@ -10,7 +9,6 @@
#include "nvim/event/process.h"
#include "nvim/globals.h"
#include "nvim/log.h"
-#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/os/process.h"
#include "nvim/os/pty_process.h"
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 62326de075..542fb707fd 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -12,7 +12,6 @@
#include "nvim/event/stream.h"
#include "nvim/gettext.h"
#include "nvim/log.h"
-#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/memory.h"
#include "nvim/os/os.h"
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 49b5be23c8..17c1b0a072 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -7,7 +7,6 @@
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
-#include "nvim/macros.h"
#include "nvim/rbuffer.h"
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 94f981dc2b..d92be6404b 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -73,6 +73,7 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
@@ -130,17 +131,22 @@ static const char e_non_numeric_argument_to_z[]
/// ":ascii" and "ga" implementation
void do_ascii(exarg_T *eap)
{
- char *dig;
- int cc[MAX_MCO];
- int c = utfc_ptr2char(get_cursor_pos_ptr(), cc);
- if (c == NUL) {
+ char *data = get_cursor_pos_ptr();
+ size_t len = (size_t)utfc_ptr2len(data);
+
+ if (len == 0) {
msg("NUL", 0);
return;
}
- size_t iobuff_len = 0;
+ bool need_clear = true;
+ msg_sb_eol();
+ msg_start();
+
+ int c = utf_ptr2char(data);
+ size_t off = 0;
- int ci = 0;
+ // TODO(bfredl): merge this with the main loop
if (c < 0x80) {
if (c == NL) { // NUL is stored as NL.
c = NUL;
@@ -159,46 +165,29 @@ void do_ascii(exarg_T *eap)
char buf2[20];
buf2[0] = NUL;
- dig = get_digraph_for_char(cval);
+ char *dig = get_digraph_for_char(cval);
if (dig != NULL) {
- iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"),
- transchar(c), buf1, buf2, cval, cval, cval, dig);
+ vim_snprintf(IObuff, sizeof(IObuff),
+ _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"),
+ transchar(c), buf1, buf2, cval, cval, cval, dig);
} else {
- iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- _("<%s>%s%s %d, Hex %02x, Octal %03o"),
- transchar(c), buf1, buf2, cval, cval, cval);
- }
-
- c = cc[ci++];
- }
-
-#define SPACE_FOR_DESC (1 + 1 + 1 + MB_MAXBYTES + 16 + 4 + 3 + 3 + 1)
- // Space for description:
- // - 1 byte for separator (starting from second entry)
- // - 1 byte for "<"
- // - 1 byte for space to draw composing character on (optional, but really
- // mostly required)
- // - up to MB_MAXBYTES bytes for character itself
- // - 16 bytes for raw text ("> , Hex , Octal ").
- // - at least 4 bytes for hexadecimal representation
- // - at least 3 bytes for decimal representation
- // - at least 3 bytes for octal representation
- // - 1 byte for NUL
- //
- // Taking into account MAX_MCO and characters which need 8 bytes for
- // hexadecimal representation, but not taking translation into account:
- // resulting string will occupy less then 400 bytes (conservative estimate).
- //
- // Less then 1000 bytes if translation multiplies number of bytes needed for
- // raw text by 6, so it should always fit into 1025 bytes reserved for IObuff.
+ vim_snprintf(IObuff, sizeof(IObuff),
+ _("<%s>%s%s %d, Hex %02x, Octal %03o"),
+ transchar(c), buf1, buf2, cval, cval, cval);
+ }
+
+ msg_multiline(IObuff, 0, true, &need_clear);
+
+ off += (size_t)utf_ptr2len(data); // needed for overlong ascii?
+ }
// Repeat for combining characters, also handle multiby here.
- while (c >= 0x80 && iobuff_len < sizeof(IObuff) - SPACE_FOR_DESC) {
+ while (off < len) {
+ c = utf_ptr2char(data + off);
+
+ size_t iobuff_len = 0;
// This assumes every multi-byte char is printable...
- if (iobuff_len > 0) {
+ if (off > 0) {
IObuff[iobuff_len++] = ' ';
}
IObuff[iobuff_len++] = '<';
@@ -207,32 +196,30 @@ void do_ascii(exarg_T *eap)
}
iobuff_len += (size_t)utf_char2bytes(c, IObuff + iobuff_len);
- dig = get_digraph_for_char(c);
+ char *dig = get_digraph_for_char(c);
if (dig != NULL) {
- iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- (c < 0x10000
- ? _("> %d, Hex %04x, Oct %o, Digr %s")
- : _("> %d, Hex %08x, Oct %o, Digr %s")),
- c, c, c, dig);
+ vim_snprintf(IObuff + iobuff_len, sizeof(IObuff) - iobuff_len,
+ (c < 0x10000
+ ? _("> %d, Hex %04x, Oct %o, Digr %s")
+ : _("> %d, Hex %08x, Oct %o, Digr %s")),
+ c, c, c, dig);
} else {
- iobuff_len += (size_t)vim_snprintf(IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- (c < 0x10000
- ? _("> %d, Hex %04x, Octal %o")
- : _("> %d, Hex %08x, Octal %o")),
- c, c, c);
- }
- if (ci == MAX_MCO) {
- break;
+ vim_snprintf(IObuff + iobuff_len, sizeof(IObuff) - iobuff_len,
+ (c < 0x10000
+ ? _("> %d, Hex %04x, Octal %o")
+ : _("> %d, Hex %08x, Octal %o")),
+ c, c, c);
}
- c = cc[ci++];
- }
- if (ci != MAX_MCO && c != 0) {
- xstrlcpy(IObuff + iobuff_len, " ...", sizeof(IObuff) - iobuff_len);
+
+ msg_multiline(IObuff, 0, true, &need_clear);
+
+ off += (size_t)utf_ptr2len(data + off); // needed for overlong ascii?
}
- msg(IObuff, 0);
+ if (need_clear) {
+ msg_clr_eos();
+ }
+ msg_end();
}
/// ":left", ":center" and ":right": align text.
@@ -3410,10 +3397,15 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
// check for a trailing count
cmd = skipwhite(cmd);
if (ascii_isdigit(*cmd)) {
- i = getdigits_int(&cmd, true, 0);
+ i = getdigits_int(&cmd, true, INT_MAX);
if (i <= 0 && !eap->skip && subflags.do_error) {
emsg(_(e_zerocount));
return 0;
+ } else if (i >= INT_MAX) {
+ char buf[20];
+ vim_snprintf(buf, sizeof(buf), "%d", i);
+ semsg(_(e_val_too_large), buf);
+ return 0;
}
eap->line1 = eap->line2;
eap->line2 += (linenr_T)i - 1;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 13043930b2..0ca6e8bedb 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -83,6 +83,7 @@
#include "nvim/usercmd.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
static const char e_ambiguous_use_of_user_defined_command[]
= N_("E464: Ambiguous use of user-defined command");
@@ -3551,7 +3552,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
if (i == '-') {
lnum -= n;
} else {
- if (n >= INT32_MAX - lnum) {
+ if (lnum >= 0 && n >= INT32_MAX - lnum) {
*errormsg = _(e_line_number_out_of_range);
goto error;
}
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 00abade4b0..4ec5be5157 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -23,7 +23,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
-#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 69025d81c7..cae3a65825 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -38,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/grid.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index dd32bdbea7..ff9fa55388 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -26,7 +26,6 @@
// code for redrawing the line with the deleted decoration.
#include <assert.h>
-#include <sys/types.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 2e9fcf8e75..4144a7c8ac 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1022,11 +1022,13 @@ EXTERN const char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight
EXTERN const char e_invalid_line_number_nr[] INIT(= N_("E966: Invalid line number: %ld"));
-EXTERN char e_stray_closing_curly_str[]
+EXTERN const char e_stray_closing_curly_str[]
INIT(= N_("E1278: Stray '}' without a matching '{': %s"));
-EXTERN char e_missing_close_curly_str[]
+EXTERN const char e_missing_close_curly_str[]
INIT(= N_("E1279: Missing '}': %s"));
+EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s"));
+
EXTERN const char e_undobang_cannot_redo_or_move_branch[]
INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch"));
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index f21b7e3a90..6320abe4ea 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -68,21 +68,6 @@ 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.
-schar_T schar_from_cc(int c, int u8cc[MAX_MCO])
-{
- 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], buf + len);
- }
- buf[len] = 0;
- return schar_from_buf(buf, (size_t)len);
-}
-
schar_T schar_from_str(char *str)
{
if (str == NULL) {
@@ -243,22 +228,21 @@ void line_do_arabic_shape(schar_T *buf, int cols)
schar_get(scbuf, buf[i]);
char scbuf_new[MAX_SCHAR_SIZE];
- int len = utf_char2bytes(c0new, scbuf_new);
+ size_t len = (size_t)utf_char2bytes(c0new, scbuf_new);
if (c1new) {
- len += utf_char2bytes(c1new, scbuf_new + len);
+ len += (size_t)utf_char2bytes(c1new, scbuf_new + len);
}
int off = utf_char2len(c0) + (c1 ? utf_char2len(c1) : 0);
size_t rest = strlen(scbuf + off);
- if (rest + (size_t)off + 1 > MAX_SCHAR_SIZE) {
- // TODO(bfredl): this cannot happen just yet, as we only construct
- // schar_T values with up to MAX_MCO+1 composing codepoints. When code
- // is improved so that MAX_SCHAR_SIZE becomes the only/sharp limit,
- // we need be able to peel off a composing char which doesn't fit anymore.
- abort();
+ if (rest + len + 1 > MAX_SCHAR_SIZE) {
+ // Too bigly, discard one code-point.
+ // This should be enough as c0 cannot grow more than from 2 to 4 bytes
+ // (base arabic to extended arabic)
+ rest -= (size_t)utf_cp_head_off(scbuf + off, scbuf + off + rest - 1) + 1;
}
memcpy(scbuf_new + len, scbuf + off, rest);
- buf[i] = schar_from_buf(scbuf_new, (size_t)len + rest);
+ buf[i] = schar_from_buf(scbuf_new, len + rest);
next:
c0prev = c0;
@@ -289,9 +273,9 @@ static bool grid_invalid_row(ScreenGrid *grid, int row)
return grid->attrs[grid->line_offset[row]] < 0;
}
-/// Get a single character directly from grid.chars into "bytes", which must
-/// have a size of "MB_MAXBYTES + 1".
-/// If "attrp" is not NULL, return the character's attribute in "*attrp".
+/// Get a single character directly from grid.chars
+///
+/// @param[out] attrp set to the character's attribute (optional)
schar_T grid_getchar(ScreenGrid *grid, int row, int col, int *attrp)
{
grid_adjust(&grid, &row, &col);
@@ -385,42 +369,35 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
{
const char *ptr = text;
int len = textlen;
- int u8cc[MAX_MCO];
assert(grid_line_grid);
int start_col = col;
int max_col = grid_line_maxcol;
- while (col < max_col
- && (len < 0 || (int)(ptr - text) < len)
- && *ptr != NUL) {
+ while (col < max_col && (len < 0 || (int)(ptr - text) < len) && *ptr != NUL) {
// check if this is the first byte of a multibyte
int mbyte_blen = len > 0
? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
: utfc_ptr2len(ptr);
- int u8c = len >= 0
- ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
- : utfc_ptr2char(ptr, u8cc);
- int mbyte_cells = utf_char2cells(u8c);
+ int firstc;
+ schar_T schar = len >= 0
+ ? utfc_ptr2schar_len(ptr, (int)((text + len) - ptr), &firstc)
+ : utfc_ptr2schar(ptr, &firstc);
+ int mbyte_cells = utf_char2cells(firstc);
if (mbyte_cells > 2) {
mbyte_cells = 1;
- u8c = 0xFFFD;
- u8cc[0] = 0;
+
+ schar = schar_from_char(0xFFFD);
}
if (col + mbyte_cells > max_col) {
// Only 1 cell left, but character requires 2 cells:
// display a '>' in the last column to avoid wrapping. */
- u8c = '>';
- u8cc[0] = 0;
+ schar = schar_from_ascii('>');
mbyte_cells = 1;
}
- schar_T buf;
- // TODO(bfredl): why not just keep the original byte sequence.
- buf = schar_from_cc(u8c, u8cc);
-
// 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 > grid_line_first && col < grid_line_last
@@ -428,7 +405,7 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
linebuf_char[col - 1] = schar_from_ascii('>');
}
- linebuf_char[col] = buf;
+ linebuf_char[col] = schar;
linebuf_attr[col] = attr;
linebuf_vcol[col] = -1;
if (mbyte_cells == 2) {
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 11e736fc0c..3cc2d788d3 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -7,8 +7,8 @@
#include "nvim/pos.h"
#include "nvim/types.h"
-#define MAX_MCO 6 // fixed value for 'maxcombine'
-// Includes final NUL. at least 4*(MAX_MCO+1)+1
+// Includes final NUL. MAX_MCO is no longer used, but at least 4*(MAX_MCO+1)+1=29
+// ensures we can fit all composed chars which did fit before.
#define MAX_SCHAR_SIZE 32
// if data[0] is 0xFF, then data[1..4] is a 24-bit index (in machine endianness)
@@ -35,7 +35,7 @@ enum {
/// we can avoid sending bigger updates than necessary to the Ul layer.
///
/// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can
-/// contain up to MAX_MCO composing characters after the base character.
+/// contain composing characters as many as fits in MAX_SCHAR_SIZE-1 bytes
/// The composing characters are to be drawn on top of the original character.
/// The content after the NUL is not defined (so comparison must be done a
/// single cell at a time). Double-width characters are stored in the left cell,
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 1bf2379bd9..89cf374152 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -1,5 +1,4 @@
#include <assert.h>
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 2f5eb49ce0..d6ade22fdb 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -180,6 +180,9 @@ int get_number(int colon, int *mouse_used)
ui_cursor_goto(msg_row, msg_col);
int c = safe_vgetc();
if (ascii_isdigit(c)) {
+ if (n > INT_MAX / 10) {
+ return 0;
+ }
n = n * 10 + c - '0';
msg_putchar(c);
typed++;
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index bd32c6e2dc..c2bec8b045 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -21,7 +21,6 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
@@ -1744,7 +1743,7 @@ void ins_compl_addleader(int c)
return;
}
if ((cc = utf_char2len(c)) > 1) {
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
utf_char2bytes(c, buf);
buf[cc] = NUL;
@@ -3326,24 +3325,10 @@ static void get_next_bufname_token(void)
{
FOR_ALL_BUFFERS(b) {
if (b->b_p_bl && b->b_sfname != NULL) {
- char *start = get_past_head(b->b_sfname);
- char *current = start;
- char *p = (char *)path_next_component(start);
- while (true) {
- int len = (int)(p - current) - (*p == NUL ? 0 : 1);
- // treat . as a separator, unless it is the first char in a filename
- char *dot = strchr(current, '.');
- if (dot && *p == NUL && *current != '.') {
- len = (int)(dot - current);
- p = dot + 1;
- }
- ins_compl_add(current, len, NULL, NULL, false, NULL, 0,
+ char *tail = path_tail(b->b_sfname);
+ if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) {
+ ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0,
p_ic ? CP_ICASE : 0, false);
- if (*p == NUL) {
- break;
- }
- current = p;
- p = (char *)path_next_component(p);
}
}
}
@@ -3436,7 +3421,7 @@ static int ins_compl_get_exp(pos_T *ini)
compl_started = true;
} else {
// Mark a buffer scanned when it has been scanned completely
- if (type == 0 || type == CTRL_X_PATH_PATTERNS) {
+ if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS)) {
assert(st.ins_buf);
st.ins_buf->b_scanned = true;
}
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index 1524731fab..d835bd5dc1 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -8,6 +8,7 @@
#include "nvim/linematch.h"
#include "nvim/macros.h"
#include "nvim/memory.h"
+#include "nvim/pos.h"
#define LN_MAX_BUFS 8
#define LN_DECISION_MAX 255 // pow(2, LN_MAX_BUFS(8)) - 1 = 255
diff --git a/src/nvim/lua/base64.c b/src/nvim/lua/base64.c
index 3f246839d5..c1f43a37d7 100644
--- a/src/nvim/lua/base64.c
+++ b/src/nvim/lua/base64.c
@@ -1,11 +1,16 @@
#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
+#include <stddef.h>
#include "nvim/base64.h"
#include "nvim/lua/base64.h"
#include "nvim/memory.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/base64.c.generated.h"
+#endif
+
static int nlua_base64_encode(lua_State *L)
{
if (lua_gettop(L) < 1) {
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index aed96a539a..12304b6f11 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -366,17 +366,17 @@ static void nlua_schedule_event(void **argv)
static int nlua_schedule(lua_State *const lstate)
FUNC_ATTR_NONNULL_ALL
{
- // If Nvim is exiting don't schedule tasks to run in the future. Any refs
- // allocated here will not be cleaned up otherwise
- if (exiting) {
- return 0;
- }
-
if (lua_type(lstate, 1) != LUA_TFUNCTION) {
lua_pushliteral(lstate, "vim.schedule: expected function");
return lua_error(lstate);
}
+ // If main_loop is closing don't schedule tasks to run in the future,
+ // otherwise any refs allocated here will not be cleaned up.
+ if (main_loop.closing) {
+ return 0;
+ }
+
LuaRef cb = nlua_ref_global(lstate, 1);
multiqueue_put(main_loop.events, nlua_schedule_event,
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 5072d14c0e..a200b0a32f 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -224,7 +224,7 @@ static int nlua_str_utf_start(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int head_offset = utf_cp_head_off(s1, s1 + offset - 1);
+ int head_offset = -utf_cp_head_off(s1, s1 + offset - 1);
lua_pushinteger(lstate, head_offset);
return 1;
}
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index f3f78b79f5..29e3bbefd0 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -13,6 +13,7 @@
#include "nvim/lua/xdiff.h"
#include "nvim/macros.h"
#include "nvim/memory.h"
+#include "nvim/pos.h"
#include "nvim/vim.h"
#include "xdiff/xdiff.h"
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 8728861145..3fc4b98c6c 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -65,7 +65,6 @@
#include "nvim/os/stdpaths_defs.h"
#include "nvim/path.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
@@ -696,7 +695,7 @@ void getout(int exitval)
for (const tabpage_T *tp = first_tabpage; tp != NULL; tp = next_tp) {
next_tp = tp->tp_next;
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
- if (wp->w_buffer == NULL) {
+ if (wp->w_buffer == NULL || !buf_valid(wp->w_buffer)) {
// Autocmd must have close the buffer already, skip.
continue;
}
diff --git a/src/nvim/match.c b/src/nvim/match.c
index 3420455e5f..0cd0426cff 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -939,7 +939,7 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->mit_id);
if (cur->mit_conceal_char) {
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
buf[utf_char2bytes(cur->mit_conceal_char, buf)] = NUL;
tv_dict_add_str(dict, S_LEN("conceal"), buf);
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 0d468889a4..3a13aeddb8 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -48,6 +48,7 @@
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/grid_defs.h"
#include "nvim/iconv.h"
#include "nvim/keycodes.h"
@@ -722,80 +723,68 @@ bool utf_composinglike(const char *p1, const char *p2)
return arabic_combine(utf_ptr2char(p1), c2);
}
-/// Convert a UTF-8 string to a wide character
+/// Get the screen char at the beginning of a string
///
-/// Also gets up to #MAX_MCO composing characters.
+/// Caller is expected to check for things like unprintable chars etc
+/// If first char in string is a composing char, prepend a space to display it correctly.
///
-/// @param[out] pcc Location where to store composing characters. Must have
-/// space at least for #MAX_MCO + 1 elements.
+/// If "p" starts with an invalid sequence, zero is returned.
///
-/// @return leading character.
-int utfc_ptr2char(const char *p, int *pcc)
+/// @param[out] firstc (required) The first codepoint of the screen char,
+/// or the first byte of an invalid sequence
+///
+/// @return the char
+schar_T utfc_ptr2schar(const char *p, int *firstc)
+ FUNC_ATTR_NONNULL_ALL
{
- int i = 0;
-
int c = utf_ptr2char(p);
- int len = utf_ptr2len(p);
-
- // Only accept a composing char when the first char isn't illegal.
- if ((len > 1 || (uint8_t)(*p) < 0x80)
- && (uint8_t)p[len] >= 0x80
- && utf_composinglike(p, p + len)) {
- int cc = utf_ptr2char(p + len);
- while (true) {
- pcc[i++] = cc;
- if (i == MAX_MCO) {
- break;
- }
- len += utf_ptr2len(p + len);
- if ((uint8_t)p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char(p + len))) {
- break;
- }
- }
- }
+ *firstc = c; // NOT optional, you are gonna need it
+ bool first_compose = utf_iscomposing(c);
+ size_t maxlen = MAX_SCHAR_SIZE - 1 - first_compose;
+ size_t len = (size_t)utfc_ptr2len_len(p, (int)maxlen);
- if (i < MAX_MCO) { // last composing char must be 0
- pcc[i] = 0;
+ if (len == 1 && (uint8_t)(*p) >= 0x80) {
+ return 0; // invalid sequence
}
- return c;
+ return schar_from_buf_first(p, len, first_compose);
}
-// Convert a UTF-8 byte string to a wide character. Also get up to MAX_MCO
-// composing characters. Use no more than p[maxlen].
-//
-// @param [out] pcc: composing chars, last one is 0
-int utfc_ptr2char_len(const char *p, int *pcc, int maxlen)
+/// Get the screen char at the beginning of a string with length
+///
+/// Like utfc_ptr2schar but use no more than p[maxlen].
+schar_T utfc_ptr2schar_len(const char *p, int maxlen, int *firstc)
+ FUNC_ATTR_NONNULL_ALL
{
assert(maxlen > 0);
- int i = 0;
+ size_t len = (size_t)utf_ptr2len_len(p, maxlen);
+ if (len > (size_t)maxlen || (len == 1 && (uint8_t)(*p) >= 0x80) || len == 0) {
+ // invalid or truncated sequence
+ *firstc = (uint8_t)(*p);
+ return 0;
+ }
- int len = utf_ptr2len_len(p, maxlen);
- // Is it safe to use utf_ptr2char()?
- bool safe = len > 1 && len <= maxlen;
- int c = safe ? utf_ptr2char(p) : (uint8_t)(*p);
+ int c = utf_ptr2char(p);
+ *firstc = c;
+ bool first_compose = utf_iscomposing(c);
+ maxlen = MIN(maxlen, MAX_SCHAR_SIZE - 1 - first_compose);
+ len = (size_t)utfc_ptr2len_len(p, maxlen);
- // Only accept a composing char when the first char isn't illegal.
- if ((safe || c < 0x80) && len < maxlen && (uint8_t)p[len] >= 0x80) {
- for (; i < MAX_MCO; i++) {
- int len_cc = utf_ptr2len_len(p + len, maxlen - len);
- safe = len_cc > 1 && len_cc <= maxlen - len;
- if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80
- || !(i == 0 ? utf_composinglike(p, p + len) : utf_iscomposing(pcc[i]))) {
- break;
- }
- len += len_cc;
- }
- }
+ return schar_from_buf_first(p, len, first_compose);
+}
- if (i < MAX_MCO) {
- // last composing char must be 0
- pcc[i] = 0;
+/// Caller must ensure there is space for `first_compose`
+static schar_T schar_from_buf_first(const char *buf, size_t len, bool first_compose)
+{
+ if (first_compose) {
+ char cbuf[MAX_SCHAR_SIZE];
+ cbuf[0] = ' ';
+ memcpy(cbuf + 1, buf, len);
+ return schar_from_buf(cbuf, len + 1);
+ } else {
+ return schar_from_buf(buf, len);
}
-
- return c;
-#undef ISCOMPOSING
}
/// Get the length of a UTF-8 byte sequence representing a single codepoint
@@ -878,8 +867,7 @@ int utfc_ptr2len(const char *const p)
return 1;
}
- // Check for composing characters. We can handle only the first six, but
- // skip all of them (otherwise the cursor would get stuck).
+ // Check for composing characters.
int prevlen = 0;
while (true) {
if ((uint8_t)p[len] < 0x80 || !utf_composinglike(p + prevlen, p + len)) {
@@ -1815,12 +1803,12 @@ int utf_cp_tail_off(const char *base, const char *p_in)
/// Return the offset from "p" to the first byte of the codepoint it points
/// to. Can start anywhere in a stream of bytes.
/// Note: Unlike `utf_head_off`, this counts individual codepoints of composed characters
-/// separately and returns a negative offset.
+/// separately.
///
/// @param[in] base Pointer to start of string
/// @param[in] p Pointer to byte for which to return the offset to the previous codepoint
//
-/// @return 0 if invalid sequence, else offset to previous codepoint
+/// @return 0 if invalid sequence, else number of bytes to previous codepoint
int utf_cp_head_off(const char *base, const char *p)
{
int i;
@@ -1830,17 +1818,20 @@ int utf_cp_head_off(const char *base, const char *p)
}
// Find the first character that is not 10xx.xxxx
- for (i = 0; p - i > base; i--) {
- if (((uint8_t)p[i] & 0xc0) != 0x80) {
+ for (i = 0; p - i >= base; i++) {
+ if (((uint8_t)p[-i] & 0xc0) != 0x80) {
break;
}
}
- // Find the last character that is 10xx.xxxx
- for (int j = 0; ((uint8_t)p[j + 1] & 0xc0) == 0x80; j++) {}
+ // Find the last character that is 10xx.xxxx (condition terminates on NUL)
+ int j = 1;
+ while (((uint8_t)p[j] & 0xc0) == 0x80) {
+ j++;
+ }
// Check for illegal sequence.
- if (utf8len_tab[(uint8_t)p[i]] == 1) {
+ if (utf8len_tab[(uint8_t)p[-i]] != j + i) {
return 0;
}
return i;
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 1d1a9439ad..c177f14ce2 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -7,6 +7,7 @@
#include "nvim/cmdexpand_defs.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/func_attr.h"
+#include "nvim/grid_defs.h"
#include "nvim/mbyte_defs.h"
#include "nvim/os/os_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 087661799a..a77e6dc41d 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -334,7 +334,7 @@ int ml_open(buf_T *buf)
// Only works when there's a swapfile, otherwise it's done when the file
// is created.
mf_put(mfp, hp, true, false);
- if (!buf->b_help && !B_SPELL(buf)) {
+ if (!buf->b_help && !buf->b_spell) {
(void)mf_sync(mfp, 0);
}
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 4c7e42321d..732c9ca39d 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -657,7 +657,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/edit.h"
# include "nvim/ex_cmds.h"
# include "nvim/ex_docmd.h"
-# include "nvim/ex_getln.h"
# include "nvim/file_search.h"
# include "nvim/getchar.h"
# include "nvim/grid.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 8be8581537..9e9aa1fcd6 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -139,7 +139,7 @@ static int msg_grid_pos_at_flush = 0;
static void ui_ext_msg_set_pos(int row, bool scrolled)
{
- char buf[MAX_MCO + 1];
+ char buf[MB_MAXCHAR + 1];
size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf);
buf[size] = '\0';
ui_call_msg_set_pos(msg_grid.handle, row, scrolled,
@@ -468,7 +468,7 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen)
buf[e + 3 + len - 1] = NUL;
} else {
// can't fit in the "...", just truncate it
- buf[e - 1] = NUL;
+ buf[buflen - 1] = NUL;
}
}
@@ -1471,7 +1471,7 @@ void msg_putchar(int c)
void msg_putchar_attr(int c, int attr)
{
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
if (IS_SPECIAL(c)) {
buf[0] = (char)K_SPECIAL;
@@ -1560,12 +1560,6 @@ int msg_outtrans_len(const char *msgstr, int len, int attr)
mode_displayed = false;
}
- // If the string starts with a composing character first draw a space on
- // which the composing char can be drawn.
- if (utf_iscomposing(utf_ptr2char(msgstr))) {
- msg_puts_attr(" ", attr);
- }
-
// Go over the string. Special characters are translated and printed.
// Normal characters are printed several at a time.
while (--len >= 0 && !got_int) {
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 9427719988..b10bdd8ffe 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -45,6 +45,7 @@
#include "nvim/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
typedef struct {
linenr_T lnum; // line number
@@ -64,8 +65,9 @@ int adjust_plines_for_skipcol(win_T *wp)
}
int width = wp->w_width_inner - win_col_off(wp);
- if (wp->w_skipcol >= width) {
- return (wp->w_skipcol - width) / (width + win_col_off2(wp)) + 1;
+ int w2 = width + win_col_off2(wp);
+ if (wp->w_skipcol >= width && w2 > 0) {
+ return (wp->w_skipcol - width) / w2 + 1;
}
return 0;
@@ -1236,8 +1238,8 @@ bool scrolldown(linenr_T line_count, int byfold)
curwin->w_topline = first;
} else {
if (do_sms) {
- int size = (int)win_linetabsize(curwin, curwin->w_topline,
- ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+ int size = win_linetabsize(curwin, curwin->w_topline,
+ ml_get(curwin->w_topline), MAXCOL);
if (size > width1) {
curwin->w_skipcol = width1;
size -= width1;
@@ -1333,7 +1335,7 @@ bool scrollup(linenr_T line_count, int byfold)
if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) {
int width1 = curwin->w_width_inner - curwin_col_off();
int width2 = width1 + curwin_col_off2();
- unsigned size = 0;
+ int size = 0;
const colnr_T prev_skipcol = curwin->w_skipcol;
if (do_sms) {
@@ -1358,7 +1360,7 @@ bool scrollup(linenr_T line_count, int byfold)
// the end of the line, then advance to the next line.
int add = curwin->w_skipcol > 0 ? width2 : width1;
curwin->w_skipcol += add;
- if ((unsigned)curwin->w_skipcol >= size) {
+ if (curwin->w_skipcol >= size) {
if (lnum == curbuf->b_ml.ml_line_count) {
// at the last screen line, can't scroll further
curwin->w_skipcol -= add;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f0f3d35468..38fdff95d7 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2466,7 +2466,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar
/// @return true if able to move cursor, false otherwise.
static bool nv_screengo(oparg_T *oap, int dir, int dist)
{
- int linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
+ int linelen = linetabsize(curwin, curwin->w_cursor.lnum);
bool retval = true;
bool atend = false;
int col_off1; // margin offset for first screen line
@@ -2530,7 +2530,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist)
}
cursor_up_inner(curwin, 1);
- linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
+ linelen = linetabsize(curwin, curwin->w_cursor.lnum);
if (linelen > width1) {
int w = (((linelen - width1 - 1) / width2) + 1) * width2;
assert(curwin->w_curswant <= INT_MAX - w);
@@ -2563,7 +2563,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist)
if (curwin->w_curswant >= width1) {
curwin->w_curswant -= width2;
}
- linelen = (int)linetabsize(curwin, curwin->w_cursor.lnum);
+ linelen = linetabsize(curwin, curwin->w_cursor.lnum);
}
}
}
@@ -2695,6 +2695,10 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
if (nchar == K_DEL || nchar == K_KDEL) {
n /= 10;
} else if (ascii_isdigit(nchar)) {
+ if (n > INT_MAX / 10) {
+ clearopbeep(cap->oap);
+ break;
+ }
n = n * 10 + (nchar - '0');
} else if (nchar == CAR) {
win_setheight(n);
@@ -5487,7 +5491,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case 'M':
oap->motion_type = kMTCharWise;
oap->inclusive = false;
- i = (int)linetabsize(curwin, curwin->w_cursor.lnum);
+ i = linetabsize(curwin, curwin->w_cursor.lnum);
if (cap->count0 > 0 && cap->count0 <= 100) {
coladvance((colnr_T)(i * cap->count0 / 100));
} else {
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index f0c752a2b1..0193e43de7 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -556,6 +556,7 @@ EXTERN char *p_mp; ///< 'makeprg'
EXTERN char *p_mps; ///< 'matchpairs'
EXTERN OptInt p_mat; ///< 'matchtime'
EXTERN OptInt p_mco; ///< 'maxcombine'
+#define MAX_MCO 6 // fixed value for 'maxcombine'
EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index e363f02644..bee08940b4 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -3,7 +3,6 @@
#include <stdint.h>
#include <string.h>
-#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
@@ -15,7 +14,6 @@
#include "nvim/diff.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
-#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
@@ -32,7 +30,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
@@ -41,13 +38,11 @@
#include "nvim/os/os.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/runtime.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
#include "nvim/types.h"
-#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
diff --git a/src/nvim/path.c b/src/nvim/path.c
index dc7e0d9645..1cd663bde4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -29,7 +29,6 @@
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
-#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 3a168320e4..07c77a5d72 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -83,18 +83,18 @@ int linetabsize_col(int startcol, char *s)
/// @param len
///
/// @return Number of characters the string will take on the screen.
-unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
+int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
win_linetabsize_cts(&cts, len);
clear_chartabsize_arg(&cts);
- return (unsigned)cts.cts_vcol;
+ return cts.cts_vcol;
}
/// Return the number of cells line "lnum" of window "wp" will take on the
/// screen, taking into account the size of a tab and inline virtual text.
-unsigned linetabsize(win_T *wp, linenr_T lnum)
+int linetabsize(win_T *wp, linenr_T lnum)
{
return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL);
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 19b34b52b4..2ddee313a3 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -262,10 +262,8 @@ static const char *e_current_location_list_was_changed =
#define IS_QF_LIST(qfl) ((qfl)->qfl_type == QFLT_QUICKFIX)
#define IS_LL_LIST(qfl) ((qfl)->qfl_type == QFLT_LOCATION)
-//
// Return location list for window 'wp'
// For location list window, return the referenced location list
-//
#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? (wp)->w_llist_ref : (wp)->w_llist)
// Macro to loop through all the items in a quickfix list
@@ -3863,13 +3861,11 @@ static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index)
static int is_qf_win(const win_T *win, const qf_info_T *qi)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- //
// A window displaying the quickfix buffer will have the w_llist_ref field
// set to NULL.
// A window displaying a location list buffer will have the w_llist_ref
// pointing to the location list.
- //
- if (bt_quickfix(win->w_buffer)) {
+ if (buf_valid(win->w_buffer) && bt_quickfix(win->w_buffer)) {
if ((IS_QF_STACK(qi) && win->w_llist_ref == NULL)
|| (IS_LL_STACK(qi) && win->w_llist_ref == qi)) {
return true;
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 8151d2e12a..dc7ff30513 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -7,9 +7,11 @@
// #define REGEXP_DEBUG
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
#include <sys/types.h>
@@ -22,6 +24,7 @@
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/keycodes.h"
#include "nvim/macros.h"
#include "nvim/mark.h"
@@ -1291,9 +1294,7 @@ static bool reg_match_visual(void)
rex.line = (uint8_t *)reg_getline(rex.lnum);
rex.input = rex.line + col;
- unsigned cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
- assert(cols_u <= MAXCOL);
- colnr_T cols = (colnr_T)cols_u;
+ colnr_T cols = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
if (cols < start || cols > end - (*p_sel == 'e')) {
return false;
}
@@ -1639,41 +1640,46 @@ static void do_lower(int *d, int c)
char *regtilde(char *source, int magic, bool preview)
{
char *newsub = source;
- char *tmpsub;
- char *p;
- int len;
- int prevlen;
- for (p = newsub; *p; p++) {
+ for (char *p = newsub; *p; p++) {
if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) {
if (reg_prev_sub != NULL) {
// length = len(newsub) - 1 + len(prev_sub) + 1
- prevlen = (int)strlen(reg_prev_sub);
- tmpsub = xmalloc(strlen(newsub) + (size_t)prevlen);
+ // Avoid making the text longer than MAXCOL, it will cause
+ // trouble at some point.
+ size_t prevsublen = strlen(reg_prev_sub);
+ size_t newsublen = strlen(newsub);
+ if (prevsublen > MAXCOL || newsublen > MAXCOL
+ || newsublen + prevsublen > MAXCOL) {
+ emsg(_(e_resulting_text_too_long));
+ break;
+ }
+
+ char *tmpsub = xmalloc(newsublen + prevsublen);
// copy prefix
- len = (int)(p - newsub); // not including ~
- memmove(tmpsub, newsub, (size_t)len);
+ size_t prefixlen = (size_t)(p - newsub); // not including ~
+ memmove(tmpsub, newsub, prefixlen);
// interpret tilde
- memmove(tmpsub + len, reg_prev_sub, (size_t)prevlen);
+ memmove(tmpsub + prefixlen, reg_prev_sub, prevsublen);
// copy postfix
if (!magic) {
- p++; // back off backslash
+ p++; // back off backslash
}
- STRCPY(tmpsub + len + prevlen, p + 1);
+ STRCPY(tmpsub + prefixlen + prevsublen, p + 1);
- if (newsub != source) { // already allocated newsub
+ if (newsub != source) { // allocated newsub before
xfree(newsub);
}
newsub = tmpsub;
- p = newsub + len + prevlen;
+ p = newsub + prefixlen + prevsublen;
} else if (magic) {
- STRMOVE(p, p + 1); // remove '~'
+ STRMOVE(p, p + 1); // remove '~'
} else {
- STRMOVE(p, p + 2); // remove '\~'
+ STRMOVE(p, p + 2); // remove '\~'
}
p--;
} else {
- if (*p == '\\' && p[1]) { // skip escaped characters
+ if (*p == '\\' && p[1]) { // skip escaped characters
p++;
}
p += utfc_ptr2len(p) - 1;
@@ -6021,11 +6027,10 @@ static bool regmatch(uint8_t *scan, proftime_T *tm, int *timed_out)
break;
case RE_VCOL:
- if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
- ? curwin : rex.reg_win,
- rex.reg_firstlnum + rex.lnum,
- (char *)rex.line,
- (colnr_T)(rex.input - rex.line)) + 1,
+ if (!re_num_cmp((unsigned)win_linetabsize(rex.reg_win == NULL ? curwin : rex.reg_win,
+ rex.reg_firstlnum + rex.lnum,
+ (char *)rex.line,
+ (colnr_T)(rex.input - rex.line)) + 1,
scan)) {
status = RA_NOMATCH;
}
@@ -14746,9 +14751,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = col > t->state->val * ts;
}
if (!result) {
- uintmax_t lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
+ int lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, (char *)rex.line, col);
assert(t->state->val >= 0);
- result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
+ result = nfa_re_num_cmp((uintmax_t)t->state->val, op, (uintmax_t)lts + 1);
}
if (result) {
add_here = true;
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 15a58a4434..dab278e383 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -3019,7 +3019,7 @@ static int soundfold_find(slang_T *slang, char *word)
static bool similar_chars(slang_T *slang, int c1, int c2)
{
int m1, m2;
- char buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXCHAR + 1];
hashitem_T *hi;
if (c1 >= 256) {
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index db1281a0b5..6b9361848a 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -4,22 +4,20 @@
#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii.h"
-#include "nvim/charset.h"
#include "nvim/event/defs.h"
-#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/option_vars.h"
#include "nvim/os/os.h"
+#include "nvim/strings.h"
#include "nvim/tui/input.h"
#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
-#include "nvim/types.h"
#include "nvim/ui_client.h"
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
@@ -124,7 +122,7 @@ void tinput_init(TermInput *input, Loop *loop)
input->loop = loop;
input->paste = 0;
input->in_fd = STDIN_FILENO;
- input->extkeys_type = kExtkeysNone;
+ input->key_encoding = kKeyEncodingLegacy;
input->ttimeout = (bool)p_ttimeout;
input->ttimeoutlen = p_ttm;
input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE);
@@ -446,40 +444,9 @@ static void tk_getkeys(TermInput *input, bool force)
} else if (key.type == TERMKEY_TYPE_MOUSE) {
forward_mouse_event(input, &key);
} else if (key.type == TERMKEY_TYPE_UNKNOWN_CSI) {
- // There is no specified limit on the number of parameters a CSI sequence can contain, so just
- // allocate enough space for a large upper bound
- long args[16];
- size_t nargs = 16;
- unsigned long cmd;
- if (termkey_interpret_csi(input->tk, &key, args, &nargs, &cmd) == TERMKEY_RES_KEY) {
- uint8_t intermediate = (cmd >> 16) & 0xFF;
- uint8_t initial = (cmd >> 8) & 0xFF;
- uint8_t command = cmd & 0xFF;
-
- // Currently unused
- (void)intermediate;
-
- if (input->waiting_for_csiu_response > 0) {
- if (initial == '?' && command == 'u') {
- // The first (and only) argument contains the current progressive
- // enhancement flags. Only enable CSI u mode if the first bit
- // (disambiguate escape codes) is not already set
- if (nargs > 0 && (args[0] & 0x1) == 0) {
- input->extkeys_type = kExtkeysCSIu;
- } else {
- input->extkeys_type = kExtkeysNone;
- }
- } else if (initial == '?' && command == 'c') {
- // Received Primary Device Attributes response
- input->waiting_for_csiu_response = 0;
- tui_enable_extkeys(input->tui_data);
- } else {
- input->waiting_for_csiu_response--;
- }
- }
- }
- } else if (key.type == TERMKEY_TYPE_OSC) {
- handle_osc_event(input, &key);
+ handle_unknown_csi(input, &key);
+ } else if (key.type == TERMKEY_TYPE_OSC || key.type == TERMKEY_TYPE_DCS) {
+ handle_term_response(input, &key);
} else if (key.type == TERMKEY_TYPE_MODEREPORT) {
handle_modereport(input, &key);
}
@@ -580,36 +547,105 @@ static HandleState handle_bracketed_paste(TermInput *input)
return kNotApplicable;
}
-static void handle_osc_event(TermInput *input, const TermKeyKey *key)
+/// Handle an OSC or DCS response sequence from the terminal.
+static void handle_term_response(TermInput *input, const TermKeyKey *key)
FUNC_ATTR_NONNULL_ALL
{
const char *str = NULL;
if (termkey_interpret_string(input->tk, key, &str) == TERMKEY_RES_KEY) {
assert(str != NULL);
- // Send an event to nvim core. This will update the v:termresponse variable and fire the
- // TermResponse event
+ // Send an event to nvim core. This will update the v:termresponse variable
+ // and fire the TermResponse event
MAXSIZE_TEMP_ARRAY(args, 2);
- ADD_C(args, STATIC_CSTR_AS_OBJ("osc_response"));
+ ADD_C(args, STATIC_CSTR_AS_OBJ("termresponse"));
- // libtermkey strips the OSC bytes from the response. We add it back in so that downstream
- // consumers of v:termresponse can differentiate between OSC and CSI events.
+ // libtermkey strips the OSC/DCS bytes from the response. We add it back in
+ // so that downstream consumers of v:termresponse can differentiate between
+ // the two.
StringBuilder response = KV_INITIAL_VALUE;
- kv_printf(response, "\x1b]%s", str);
+ switch (key->type) {
+ case TERMKEY_TYPE_OSC:
+ kv_printf(response, "\x1b]%s", str);
+ break;
+ case TERMKEY_TYPE_DCS:
+ kv_printf(response, "\x1bP%s", str);
+ break;
+ default:
+ // Key type already checked for OSC/DCS in termkey_interpret_string
+ UNREACHABLE;
+ }
+
ADD_C(args, STRING_OBJ(cbuf_as_string(response.items, response.size)));
rpc_send_event(ui_client_channel_id, "nvim_ui_term_event", args);
kv_destroy(response);
}
}
+/// Handle a mode report (DECRPM) sequence from the terminal.
static void handle_modereport(TermInput *input, const TermKeyKey *key)
FUNC_ATTR_NONNULL_ALL
{
- // termkey_interpret_modereport incorrectly sign extends the mode so we parse the response
- // ourselves
- int mode = (uint8_t)key->code.mouse[1] << 8 | (uint8_t)key->code.mouse[2];
- TerminalModeState value = (uint8_t)key->code.mouse[3];
- tui_dec_report_mode(input->tui_data, (TerminalDecMode)mode, value);
+ int initial;
+ int mode;
+ int value;
+ if (termkey_interpret_modereport(input->tk, key, &initial, &mode, &value) == TERMKEY_RES_KEY) {
+ (void)initial; // Unused
+ tui_handle_term_mode(input->tui_data, (TermMode)mode, (TermModeState)value);
+ }
+}
+
+/// Handle a CSI sequence from the terminal that is unrecognized by libtermkey.
+static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // There is no specified limit on the number of parameters a CSI sequence can
+ // contain, so just allocate enough space for a large upper bound
+ long args[16];
+ size_t nargs = 16;
+ unsigned long cmd;
+ if (termkey_interpret_csi(input->tk, key, args, &nargs, &cmd) != TERMKEY_RES_KEY) {
+ return;
+ }
+
+ uint8_t intermediate = (cmd >> 16) & 0xFF;
+ uint8_t initial = (cmd >> 8) & 0xFF;
+ uint8_t command = cmd & 0xFF;
+
+ // Currently unused
+ (void)intermediate;
+
+ switch (command) {
+ case 'u':
+ switch (initial) {
+ case '?':
+ // Kitty keyboard protocol query response.
+ if (input->waiting_for_kkp_response) {
+ input->waiting_for_kkp_response = false;
+ input->key_encoding = kKeyEncodingKitty;
+ tui_set_key_encoding(input->tui_data);
+ }
+
+ break;
+ }
+ break;
+ case 'c':
+ switch (initial) {
+ case '?':
+ // Primary Device Attributes response
+ if (input->waiting_for_kkp_response) {
+ input->waiting_for_kkp_response = false;
+
+ // Enable the fallback key encoding (if any)
+ tui_set_key_encoding(input->tui_data);
+ }
+
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
static void handle_raw_buffer(TermInput *input, bool force)
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 01514269be..2d72d1978c 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -14,18 +14,22 @@
#include "nvim/types.h"
typedef enum {
- kExtkeysNone,
- kExtkeysCSIu,
- kExtkeysXterm,
-} ExtkeysType;
+ kKeyEncodingLegacy, ///< Legacy key encoding
+ kKeyEncodingKitty, ///< Kitty keyboard protocol encoding
+ kKeyEncodingXterm, ///< Xterm's modifyOtherKeys encoding (XTMODKEYS)
+} KeyEncoding;
-typedef struct term_input {
+typedef struct {
int in_fd;
// Phases: -1=all 0=disabled 1=first-chunk 2=continue 3=last-chunk
int8_t paste;
bool ttimeout;
- int8_t waiting_for_csiu_response;
- ExtkeysType extkeys_type;
+
+ bool waiting_for_kkp_response; ///< True if we are expecting to receive a response to a query for
+ ///< Kitty keyboard protocol support
+
+ KeyEncoding key_encoding; ///< The key encoding used by the terminal emulator
+
OptInt ttimeoutlen;
TermKey *tk;
TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index aff7b100d8..93b891afff 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -34,6 +34,7 @@
#include "nvim/tui/input.h"
#include "nvim/tui/terminfo.h"
#include "nvim/tui/tui.h"
+#include "nvim/types.h"
#include "nvim/ugrid.h"
#include "nvim/ui.h"
#include "nvim/ui_client.h"
@@ -75,9 +76,6 @@ struct TUIData {
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
size_t bufpos;
- char norm[CNORM_COMMAND_MAX_SIZE];
- char invis[CNORM_COMMAND_MAX_SIZE];
- size_t normlen, invislen;
TermInput input;
uv_loop_t write_loop;
unibi_term *ut;
@@ -136,8 +134,6 @@ struct TUIData {
int save_title, restore_title;
int set_underline_style;
int set_underline_color;
- int enable_extended_keys, disable_extended_keys;
- int get_extkeys;
int sync;
} unibi_ext;
char *space_buf;
@@ -185,44 +181,41 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term)
*term = tui->term;
}
-void tui_enable_extkeys(TUIData *tui)
+void tui_set_key_encoding(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
{
- TermInput input = tui->input;
- unibi_term *ut = tui->ut;
-
- switch (input.extkeys_type) {
- case kExtkeysCSIu:
- tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
- "\x1b[>1u");
- tui->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys",
- "\x1b[<1u");
+ switch (tui->input.key_encoding) {
+ case kKeyEncodingKitty:
+ out(tui, S_LEN("\x1b[>1u"));
break;
- case kExtkeysXterm:
- tui->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
- "\x1b[>4;2m");
- tui->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys",
- "\x1b[>4;0m");
+ case kKeyEncodingXterm:
+ out(tui, S_LEN("\x1b[>4;2m"));
break;
- default:
+ case kKeyEncodingLegacy:
break;
}
-
- unibi_out_ext(tui, tui->unibi_ext.enable_extended_keys);
}
-static size_t unibi_pre_fmt_str(TUIData *tui, unsigned unibi_index, char *buf, size_t len)
+static void tui_reset_key_encoding(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
{
- const char *str = unibi_get_str(tui->ut, unibi_index);
- if (!str) {
- return 0U;
+ switch (tui->input.key_encoding) {
+ case kKeyEncodingKitty:
+ out(tui, S_LEN("\x1b[<1u"));
+ break;
+ case kKeyEncodingXterm:
+ out(tui, S_LEN("\x1b[>4;0m"));
+ break;
+ case kKeyEncodingLegacy:
+ break;
}
- return unibi_run(str, tui->params, buf, len);
}
-/// Request the terminal's DEC mode (DECRQM).
+/// Request the terminal's mode (DECRQM).
///
/// @see handle_modereport
-static void tui_dec_request_mode(TUIData *tui, TerminalDecMode mode)
+static void tui_request_term_mode(TUIData *tui, TermMode mode)
+ FUNC_ATTR_NONNULL_ALL
{
// 5 bytes for \x1b[?$p, 1 byte for null terminator, 6 bytes for mode digits (more than enough)
char buf[12];
@@ -231,22 +224,22 @@ static void tui_dec_request_mode(TUIData *tui, TerminalDecMode mode)
out(tui, buf, (size_t)len);
}
-/// Handle a DECRPM response from the terminal.
-void tui_dec_report_mode(TUIData *tui, TerminalDecMode mode, TerminalModeState state)
+/// Handle a mode report (DECRPM) from the terminal.
+void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state)
+ FUNC_ATTR_NONNULL_ALL
{
- assert(tui);
switch (state) {
- case kTerminalModeNotRecognized:
- case kTerminalModePermanentlySet:
- case kTerminalModePermanentlyReset:
+ case kTermModeNotRecognized:
+ case kTermModePermanentlySet:
+ case kTermModePermanentlyReset:
// If the mode is not recognized, or if the terminal emulator does not allow it to be changed,
// then there is nothing to do
break;
- case kTerminalModeSet:
- case kTerminalModeReset:
+ case kTermModeSet:
+ case kTermModeReset:
// The terminal supports changing the given mode
switch (mode) {
- case kDecModeSynchronizedOutput:
+ case kTermModeSynchronizedOutput:
// Ref: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
tui->unibi_ext.sync = (int)unibi_add_ext_str(tui->ut, "Sync",
"\x1b[?2026%?%p1%{1}%-%tl%eh%;");
@@ -254,6 +247,21 @@ void tui_dec_report_mode(TUIData *tui, TerminalDecMode mode, TerminalModeState s
}
}
+/// Query the terminal emulator to see if it supports Kitty's keyboard protocol.
+///
+/// Write CSI ? u followed by a primary device attributes request (CSI c). If
+/// a primary device attributes response is received without first receiving an
+/// answer to the progressive enhancement query (CSI u), then the terminal does
+/// not support the Kitty keyboard protocol.
+///
+/// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#detection-of-support-for-this-protocol
+static void tui_query_kitty_keyboard(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ tui->input.waiting_for_kkp_response = true;
+ out(tui, S_LEN("\x1b[?u\x1b[c"));
+}
+
static void terminfo_start(TUIData *tui)
{
tui->scroll_region_is_full_screen = true;
@@ -287,9 +295,6 @@ static void terminfo_start(TUIData *tui)
tui->unibi_ext.set_cursor_style = -1;
tui->unibi_ext.reset_cursor_style = -1;
tui->unibi_ext.set_underline_color = -1;
- tui->unibi_ext.enable_extended_keys = -1;
- tui->unibi_ext.disable_extended_keys = -1;
- tui->unibi_ext.get_extkeys = -1;
tui->unibi_ext.sync = -1;
tui->out_fd = STDOUT_FILENO;
tui->out_isatty = os_isatty(tui->out_fd);
@@ -352,10 +357,6 @@ static void terminfo_start(TUIData *tui)
|| terminfo_is_term_family(term, "win32con")
|| terminfo_is_term_family(term, "interix");
tui->bce = unibi_get_bool(tui->ut, unibi_back_color_erase);
- tui->normlen = unibi_pre_fmt_str(tui, unibi_cursor_normal,
- tui->norm, sizeof tui->norm);
- tui->invislen = unibi_pre_fmt_str(tui, unibi_cursor_invisible,
- tui->invis, sizeof tui->invis);
// Set 't_Co' from the result of unibilium & fix_terminfo.
t_colors = unibi_get_num(tui->ut, unibi_max_colors);
// Enter alternate screen, save title, and clear.
@@ -370,11 +371,10 @@ static void terminfo_start(TUIData *tui)
// Query support for mode 2026 (Synchronized Output). Some terminals also
// support an older DCS sequence for synchronized output, but we will only use
// mode 2026
- tui_dec_request_mode(tui, kDecModeSynchronizedOutput);
+ tui_request_term_mode(tui, kTermModeSynchronizedOutput);
- // Query the terminal to see if it supports CSI u
- tui->input.waiting_for_csiu_response = 5;
- unibi_out_ext(tui, tui->unibi_ext.get_extkeys);
+ // Query the terminal to see if it supports Kitty's keyboard protocol
+ tui_query_kitty_keyboard(tui);
int ret;
uv_loop_init(&tui->write_loop);
@@ -417,8 +417,10 @@ static void terminfo_stop(TUIData *tui)
// Reset cursor to normal before exiting alternate screen.
unibi_out(tui, unibi_cursor_normal);
unibi_out(tui, unibi_keypad_local);
- // Disable extended keys before exiting alternate screen.
- unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys);
+
+ // Reset the key encoding
+ tui_reset_key_encoding(tui);
+
// May restore old title before exiting alternate screen.
tui_set_title(tui, (String)STRING_INIT);
if (ui_client_exit_status == 0) {
@@ -1151,9 +1153,7 @@ void tui_set_mode(TUIData *tui, ModeShape mode)
HlAttrs aep = kv_A(tui->attrs, c.id);
tui->want_invisible = aep.hl_blend == 100;
- if (tui->want_invisible) {
- unibi_out(tui, unibi_cursor_invisible);
- } else if (aep.rgb_ae_attr & HL_INVERSE) {
+ if (!tui->want_invisible && aep.rgb_ae_attr & HL_INVERSE) {
// We interpret "inverse" as "default" (no termcode for "inverse"...).
// Hopefully the user's default cursor color is inverse.
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
@@ -1305,18 +1305,37 @@ void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Intege
invalidate(tui, 0, tui->grid.height, 0, tui->grid.width);
}
-/// Enable synchronized output. When enabled, the terminal emulator will preserve the last rendered
-/// state on subsequent re-renders. It will continue to process incoming events. When synchronized
-/// mode is disabled again the emulator renders using the most recent state. This avoids tearing
-/// when the terminal updates the screen faster than Nvim can redraw it.
-static void tui_sync_output(TUIData *tui, bool enable)
+/// Begin flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates,
+/// begin a synchronized update. Otherwise, hide the cursor to avoid cursor jumping.
+static void tui_flush_start(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
{
- if (!tui->sync_output) {
- return;
+ if (tui->sync_output && tui->unibi_ext.sync != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 1);
+ unibi_out_ext(tui, tui->unibi_ext.sync);
+ } else if (!tui->is_invisible) {
+ unibi_out(tui, unibi_cursor_invisible);
+ tui->is_invisible = true;
}
+}
- UNIBI_SET_NUM_VAR(tui->params[0], enable ? 1 : 0);
- unibi_out_ext(tui, tui->unibi_ext.sync);
+/// Finish flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates,
+/// end a synchronized update. Otherwise, make the cursor visible again.
+static void tui_flush_end(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (tui->sync_output && tui->unibi_ext.sync != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ unibi_out_ext(tui, tui->unibi_ext.sync);
+ }
+ bool should_invisible = tui->busy || tui->want_invisible;
+ if (tui->is_invisible && !should_invisible) {
+ unibi_out(tui, unibi_cursor_normal);
+ tui->is_invisible = false;
+ } else if (!tui->is_invisible && should_invisible) {
+ unibi_out(tui, unibi_cursor_invisible);
+ tui->is_invisible = true;
+ }
}
void tui_flush(TUIData *tui)
@@ -1335,7 +1354,7 @@ void tui_flush(TUIData *tui)
tui_busy_stop(tui); // avoid hidden cursor
}
- tui_sync_output(tui, true);
+ tui_flush_start(tui);
while (kv_size(tui->invalid_regions)) {
Rect r = kv_pop(tui->invalid_regions);
@@ -1364,7 +1383,7 @@ void tui_flush(TUIData *tui)
cursor_goto(tui, tui->row, tui->col);
- tui_sync_output(tui, false);
+ tui_flush_end(tui);
flush_buf(tui);
}
@@ -1946,12 +1965,6 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo
#define XTERM_SETAB_16 \
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m"
- // Query the terminal to see if it supports CSI u key encoding by writing CSI
- // ? u followed by a request for the primary device attributes (CSI c)
- // See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#detection-of-support-for-this-protocol
- tui->unibi_ext.get_extkeys = (int)unibi_add_ext_str(ut, "ext.get_extkeys",
- "\x1b[?u\x1b[c");
-
// Terminals with 256-colour SGR support despite what terminfo says.
if (unibi_get_num(ut, unibi_max_colors) < 256) {
// See http://fedoraproject.org/wiki/Features/256_Color_Terminals
@@ -2244,71 +2257,29 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
}
if (!kitty && (vte_version == 0 || vte_version >= 5400)) {
- // Fallback to Xterm's modifyOtherKeys if terminal does not support CSI u
- tui->input.extkeys_type = kExtkeysXterm;
+ // Fallback to Xterm's modifyOtherKeys if terminal does not support the
+ // Kitty keyboard protocol
+ tui->input.key_encoding = kKeyEncodingXterm;
}
}
static void flush_buf(TUIData *tui)
{
uv_write_t req;
- uv_buf_t bufs[3];
- uv_buf_t *bufp = &bufs[0];
-
- // The content of the output for each condition is shown in the following
- // table. Therefore, if tui->bufpos == 0 and N/A or invis + norm, there is
- // no need to output it.
- //
- // | is_invisible | !is_invisible
- // ------+-----------------+--------------+---------------
- // busy | want_invisible | N/A | invis
- // | !want_invisible | N/A | invis
- // ------+-----------------+--------------+---------------
- // !busy | want_invisible | N/A | invis
- // | !want_invisible | norm | invis + norm
- // ------+-----------------+--------------+---------------
- //
- if (tui->bufpos <= 0
- && ((tui->is_invisible && tui->busy)
- || (tui->is_invisible && !tui->busy && tui->want_invisible)
- || (!tui->is_invisible && !tui->busy && !tui->want_invisible))) {
- return;
- }
-
- if (!tui->is_invisible) {
- // cursor is visible. Write a "cursor invisible" command before writing the
- // buffer.
- bufp->base = tui->invis;
- bufp->len = UV_BUF_LEN(tui->invislen);
- bufp++;
- tui->is_invisible = true;
- }
+ uv_buf_t buf;
- if (tui->bufpos > 0) {
- bufp->base = tui->buf;
- bufp->len = UV_BUF_LEN(tui->bufpos);
- bufp++;
+ if (tui->bufpos <= 0) {
+ return;
}
- if (!tui->busy) {
- assert(tui->is_invisible);
- // not busy and the cursor is invisible. Write a "cursor normal" command
- // after writing the buffer.
- if (!tui->want_invisible) {
- bufp->base = tui->norm;
- bufp->len = UV_BUF_LEN(tui->normlen);
- bufp++;
- tui->is_invisible = false;
- }
- }
+ buf.base = tui->buf;
+ buf.len = UV_BUF_LEN(tui->bufpos);
if (tui->screenshot) {
- for (size_t i = 0; i < (size_t)(bufp - bufs); i++) {
- fwrite(bufs[i].base, bufs[i].len, 1, tui->screenshot);
- }
+ fwrite(buf.base, buf.len, 1, tui->screenshot);
} else {
int ret
- = uv_write(&req, (uv_stream_t *)&tui->output_handle, bufs, (unsigned)(bufp - bufs), NULL);
+ = uv_write(&req, (uv_stream_t *)&tui->output_handle, &buf, 1, NULL);
if (ret) {
ELOG("uv_write failed: %s", uv_strerror(ret));
}
diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h
index 29afdef4de..1cd283cf49 100644
--- a/src/nvim/tui/tui.h
+++ b/src/nvim/tui/tui.h
@@ -6,16 +6,16 @@
typedef struct TUIData TUIData;
typedef enum {
- kDecModeSynchronizedOutput = 2026,
-} TerminalDecMode;
+ kTermModeSynchronizedOutput = 2026,
+} TermMode;
typedef enum {
- kTerminalModeNotRecognized = 0,
- kTerminalModeSet = 1,
- kTerminalModeReset = 2,
- kTerminalModePermanentlySet = 3,
- kTerminalModePermanentlyReset = 4,
-} TerminalModeState;
+ kTermModeNotRecognized = 0,
+ kTermModeSet = 1,
+ kTermModeReset = 2,
+ kTermModePermanentlySet = 3,
+ kTermModePermanentlyReset = 4,
+} TermModeState;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.h.generated.h"
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 3e5bfba315..a2791be583 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -35,6 +35,7 @@
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.c.generated.h"
diff --git a/src/nvim/window.c b/src/nvim/window.c
index a49dcd95bc..00524b2f56 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -56,7 +56,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
@@ -75,6 +74,7 @@
#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.c.generated.h"
@@ -734,204 +734,6 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
RedrawingDisabled--;
}
-/// Create a new float.
-///
-/// @param wp if NULL, allocate a new window, otherwise turn existing window into a float.
-/// It must then already belong to the current tabpage!
-/// @param last make the window the last one in the window list.
-/// Only used when allocating the autocommand window.
-/// @param config must already have been validated!
-win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
-{
- if (wp == NULL) {
- wp = win_alloc(last ? lastwin : lastwin_nofloating(), false);
- win_init(wp, curwin, 0);
- } else {
- assert(!last);
- assert(!wp->w_floating);
- if (firstwin == wp && lastwin_nofloating() == wp) {
- // last non-float
- api_set_error(err, kErrorTypeException,
- "Cannot change last window into float");
- return NULL;
- } else if (!win_valid(wp)) {
- api_set_error(err, kErrorTypeException,
- "Cannot change window from different tabpage into float");
- return NULL;
- }
- int dir;
- winframe_remove(wp, &dir, NULL);
- XFREE_CLEAR(wp->w_frame);
- (void)win_comp_pos(); // recompute window positions
- win_remove(wp, NULL);
- win_append(lastwin_nofloating(), wp);
- }
- wp->w_floating = true;
- wp->w_status_height = 0;
- wp->w_winbar_height = 0;
- wp->w_hsep_height = 0;
- wp->w_vsep_width = 0;
-
- win_config_float(wp, fconfig);
- win_set_inner_size(wp, true);
- wp->w_pos_changed = true;
- redraw_later(wp, UPD_VALID);
- return wp;
-}
-
-void win_set_minimal_style(win_T *wp)
-{
- wp->w_p_nu = false;
- wp->w_p_rnu = false;
- wp->w_p_cul = false;
- wp->w_p_cuc = false;
- wp->w_p_spell = false;
- wp->w_p_list = false;
-
- // Hide EOB region: use " " fillchar and cleared highlighting
- if (wp->w_p_fcs_chars.eob != ' ') {
- char *old = wp->w_p_fcs;
- wp->w_p_fcs = ((*old == NUL)
- ? xstrdup("eob: ")
- : concat_str(old, ",eob: "));
- free_string_option(old);
- }
-
- // TODO(bfredl): this could use a highlight namespace directly,
- // and avoid peculiarities around window options
- char *old = wp->w_p_winhl;
- wp->w_p_winhl = ((*old == NUL)
- ? xstrdup("EndOfBuffer:")
- : concat_str(old, ",EndOfBuffer:"));
- free_string_option(old);
- parse_winhl_opt(wp);
-
- // signcolumn: use 'auto'
- if (wp->w_p_scl[0] != 'a' || strlen(wp->w_p_scl) >= 8) {
- free_string_option(wp->w_p_scl);
- wp->w_p_scl = xstrdup("auto");
- }
-
- // foldcolumn: use '0'
- if (wp->w_p_fdc[0] != '0') {
- free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = xstrdup("0");
- }
-
- // colorcolumn: cleared
- if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) {
- free_string_option(wp->w_p_cc);
- wp->w_p_cc = xstrdup("");
- }
-
- // statuscolumn: cleared
- if (wp->w_p_stc != NULL && *wp->w_p_stc != NUL) {
- free_string_option(wp->w_p_stc);
- wp->w_p_stc = xstrdup("");
- }
-}
-
-void win_config_float(win_T *wp, FloatConfig fconfig)
-{
- wp->w_width = MAX(fconfig.width, 1);
- wp->w_height = MAX(fconfig.height, 1);
-
- if (fconfig.relative == kFloatRelativeCursor) {
- fconfig.relative = kFloatRelativeWindow;
- fconfig.row += curwin->w_wrow;
- fconfig.col += curwin->w_wcol;
- fconfig.window = curwin->handle;
- } else if (fconfig.relative == kFloatRelativeMouse) {
- int row = mouse_row, col = mouse_col, grid = mouse_grid;
- win_T *mouse_win = mouse_find_win(&grid, &row, &col);
- if (mouse_win != NULL) {
- fconfig.relative = kFloatRelativeWindow;
- fconfig.row += row;
- fconfig.col += col;
- fconfig.window = mouse_win->handle;
- }
- }
-
- bool change_external = fconfig.external != wp->w_float_config.external;
- bool change_border = (fconfig.border != wp->w_float_config.border
- || memcmp(fconfig.border_hl_ids,
- wp->w_float_config.border_hl_ids,
- sizeof fconfig.border_hl_ids) != 0);
-
- wp->w_float_config = fconfig;
-
- bool has_border = wp->w_floating && wp->w_float_config.border;
- for (int i = 0; i < 4; i++) {
- int new_adj = has_border && wp->w_float_config.border_chars[2 * i + 1][0];
- if (new_adj != wp->w_border_adj[i]) {
- change_border = true;
- wp->w_border_adj[i] = new_adj;
- }
- }
-
- if (!ui_has(kUIMultigrid)) {
- wp->w_height = MIN(wp->w_height, Rows - win_border_height(wp));
- wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp));
- }
-
- win_set_inner_size(wp, true);
- must_redraw = MAX(must_redraw, UPD_VALID);
-
- wp->w_pos_changed = true;
- if (change_external || change_border) {
- wp->w_hl_needs_update = true;
- redraw_later(wp, UPD_NOT_VALID);
- }
-
- // compute initial position
- if (wp->w_float_config.relative == kFloatRelativeWindow) {
- int row = (int)wp->w_float_config.row;
- int col = (int)wp->w_float_config.col;
- Error dummy = ERROR_INIT;
- win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy);
- if (parent) {
- row += parent->w_winrow;
- col += parent->w_wincol;
- ScreenGrid *grid = &parent->w_grid;
- int row_off = 0, col_off = 0;
- grid_adjust(&grid, &row_off, &col_off);
- row += row_off;
- col += col_off;
- if (wp->w_float_config.bufpos.lnum >= 0) {
- pos_T pos = { wp->w_float_config.bufpos.lnum + 1,
- wp->w_float_config.bufpos.col, 0 };
- int trow, tcol, tcolc, tcole;
- textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true);
- row += trow - 1;
- col += tcol - 1;
- }
- }
- api_clear_error(&dummy);
- wp->w_winrow = row;
- wp->w_wincol = col;
- } else {
- wp->w_winrow = (int)fconfig.row;
- wp->w_wincol = (int)fconfig.col;
- }
-
- // changing border style while keeping border only requires redrawing border
- if (fconfig.border) {
- wp->w_redr_border = true;
- redraw_later(wp, UPD_VALID);
- }
-}
-
-void win_check_anchored_floats(win_T *win)
-{
- for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
- // float might be anchored to moved window
- if (wp->w_float_config.relative == kFloatRelativeWindow
- && wp->w_float_config.window == win->handle) {
- wp->w_pos_changed = true;
- }
- }
-}
-
/// Return the number of fold columns to display
int win_fdccol_count(win_T *wp)
{
@@ -1659,7 +1461,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// The windows will both edit the same buffer.
// WSP_NEWLOC may be specified in flags to prevent the location list from
// being copied.
-static void win_init(win_T *newp, win_T *oldp, int flags)
+void win_init(win_T *newp, win_T *oldp, int flags)
{
newp->w_buffer = oldp->w_buffer;
newp->w_s = &(oldp->w_buffer->b_s);
@@ -1736,24 +1538,6 @@ static void win_init_some(win_T *newp, win_T *oldp)
win_copy_options(oldp, newp);
}
-/// Return true if "win" is floating window in the current tab page.
-///
-/// @param win window to check
-bool win_valid_floating(const win_T *win)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (win == NULL) {
- return false;
- }
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp == win) {
- return wp->w_floating;
- }
- }
- return false;
-}
-
/// Check if "win" is a pointer to an existing window in the current tabpage.
///
/// @param win window to check
@@ -2877,6 +2661,9 @@ int win_close(win_T *win, bool free_buf, bool force)
reset_VIsual_and_resel(); // stop Visual mode
other_buffer = true;
+ if (!win_valid(win)) {
+ return FAIL;
+ }
win->w_closing = true;
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
if (!win_valid(win)) {
@@ -3992,6 +3779,12 @@ void close_others(int message, int forceit)
continue;
}
+ // autoccommands messed this one up
+ if (!buf_valid(wp->w_buffer) && win_valid(wp)) {
+ wp->w_buffer = NULL;
+ win_close(wp, false, false);
+ continue;
+ }
// Check if it's allowed to abandon this window
int r = can_abandon(wp->w_buffer, forceit);
if (!win_valid(wp)) { // autocommands messed wp up
@@ -5112,7 +4905,7 @@ win_T *buf_jump_open_tab(buf_T *buf)
/// @param hidden allocate a window structure and link it in the window if
// false.
-static win_T *win_alloc(win_T *after, bool hidden)
+win_T *win_alloc(win_T *after, bool hidden)
{
static int last_win_id = LOWEST_WIN_ID - 1;
@@ -5790,13 +5583,6 @@ int win_comp_pos(void)
return row + global_stl_height();
}
-void win_reconfig_floats(void)
-{
- for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
- win_config_float(wp, wp->w_float_config);
- }
-}
-
// Update the position of the windows in frame "topfrp", using the width and
// height of the frames.
// "*row" and "*col" are the top-left position of the frame. They are updated
@@ -6754,16 +6540,6 @@ void win_set_inner_size(win_T *wp, bool valid_cursor)
wp->w_redr_status = true;
}
-static int win_border_height(win_T *wp)
-{
- return wp->w_border_adj[0] + wp->w_border_adj[2];
-}
-
-static int win_border_width(win_T *wp)
-{
- return wp->w_border_adj[1] + wp->w_border_adj[3];
-}
-
/// Set the width of a window.
void win_new_width(win_T *wp, int width)
{
@@ -7654,29 +7430,3 @@ win_T *lastwin_nofloating(void)
}
return res;
}
-
-static int float_zindex_cmp(const void *a, const void *b)
-{
- return (*(win_T **)b)->w_float_config.zindex - (*(win_T **)a)->w_float_config.zindex;
-}
-
-void win_float_remove(bool bang, int count)
-{
- kvec_t(win_T *) float_win_arr = KV_INITIAL_VALUE;
- for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
- kv_push(float_win_arr, wp);
- }
- qsort(float_win_arr.items, float_win_arr.size, sizeof(win_T *), float_zindex_cmp);
- for (size_t i = 0; i < float_win_arr.size; i++) {
- if (win_close(float_win_arr.items[i], false, false) == FAIL) {
- break;
- }
- if (!bang) {
- count--;
- if (count == 0) {
- break;
- }
- }
- }
- kv_destroy(float_win_arr);
-}
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
new file mode 100644
index 0000000000..7fff17e1c4
--- /dev/null
+++ b/src/nvim/winfloat.c
@@ -0,0 +1,288 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "klib/kvec.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/drawscreen.h"
+#include "nvim/globals.h"
+#include "nvim/grid.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/mouse.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/pos.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+#include "nvim/winfloat.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "winfloat.c.generated.h"
+#endif
+
+/// Create a new float.
+///
+/// @param wp if NULL, allocate a new window, otherwise turn existing window into a float.
+/// It must then already belong to the current tabpage!
+/// @param last make the window the last one in the window list.
+/// Only used when allocating the autocommand window.
+/// @param config must already have been validated!
+win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
+{
+ if (wp == NULL) {
+ wp = win_alloc(last ? lastwin : lastwin_nofloating(), false);
+ win_init(wp, curwin, 0);
+ } else {
+ assert(!last);
+ assert(!wp->w_floating);
+ if (firstwin == wp && lastwin_nofloating() == wp) {
+ // last non-float
+ api_set_error(err, kErrorTypeException,
+ "Cannot change last window into float");
+ return NULL;
+ } else if (!win_valid(wp)) {
+ api_set_error(err, kErrorTypeException,
+ "Cannot change window from different tabpage into float");
+ return NULL;
+ }
+ int dir;
+ winframe_remove(wp, &dir, NULL);
+ XFREE_CLEAR(wp->w_frame);
+ (void)win_comp_pos(); // recompute window positions
+ win_remove(wp, NULL);
+ win_append(lastwin_nofloating(), wp);
+ }
+ wp->w_floating = true;
+ wp->w_status_height = 0;
+ wp->w_winbar_height = 0;
+ wp->w_hsep_height = 0;
+ wp->w_vsep_width = 0;
+
+ win_config_float(wp, fconfig);
+ win_set_inner_size(wp, true);
+ wp->w_pos_changed = true;
+ redraw_later(wp, UPD_VALID);
+ return wp;
+}
+
+void win_set_minimal_style(win_T *wp)
+{
+ wp->w_p_nu = false;
+ wp->w_p_rnu = false;
+ wp->w_p_cul = false;
+ wp->w_p_cuc = false;
+ wp->w_p_spell = false;
+ wp->w_p_list = false;
+
+ // Hide EOB region: use " " fillchar and cleared highlighting
+ if (wp->w_p_fcs_chars.eob != ' ') {
+ char *old = wp->w_p_fcs;
+ wp->w_p_fcs = ((*old == NUL)
+ ? xstrdup("eob: ")
+ : concat_str(old, ",eob: "));
+ free_string_option(old);
+ }
+
+ // TODO(bfredl): this could use a highlight namespace directly,
+ // and avoid peculiarities around window options
+ char *old = wp->w_p_winhl;
+ wp->w_p_winhl = ((*old == NUL)
+ ? xstrdup("EndOfBuffer:")
+ : concat_str(old, ",EndOfBuffer:"));
+ free_string_option(old);
+ parse_winhl_opt(wp);
+
+ // signcolumn: use 'auto'
+ if (wp->w_p_scl[0] != 'a' || strlen(wp->w_p_scl) >= 8) {
+ free_string_option(wp->w_p_scl);
+ wp->w_p_scl = xstrdup("auto");
+ }
+
+ // foldcolumn: use '0'
+ if (wp->w_p_fdc[0] != '0') {
+ free_string_option(wp->w_p_fdc);
+ wp->w_p_fdc = xstrdup("0");
+ }
+
+ // colorcolumn: cleared
+ if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) {
+ free_string_option(wp->w_p_cc);
+ wp->w_p_cc = xstrdup("");
+ }
+
+ // statuscolumn: cleared
+ if (wp->w_p_stc != NULL && *wp->w_p_stc != NUL) {
+ free_string_option(wp->w_p_stc);
+ wp->w_p_stc = xstrdup("");
+ }
+}
+
+int win_border_height(win_T *wp)
+{
+ return wp->w_border_adj[0] + wp->w_border_adj[2];
+}
+
+int win_border_width(win_T *wp)
+{
+ return wp->w_border_adj[1] + wp->w_border_adj[3];
+}
+
+void win_config_float(win_T *wp, FloatConfig fconfig)
+{
+ wp->w_width = MAX(fconfig.width, 1);
+ wp->w_height = MAX(fconfig.height, 1);
+
+ if (fconfig.relative == kFloatRelativeCursor) {
+ fconfig.relative = kFloatRelativeWindow;
+ fconfig.row += curwin->w_wrow;
+ fconfig.col += curwin->w_wcol;
+ fconfig.window = curwin->handle;
+ } else if (fconfig.relative == kFloatRelativeMouse) {
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ win_T *mouse_win = mouse_find_win(&grid, &row, &col);
+ if (mouse_win != NULL) {
+ fconfig.relative = kFloatRelativeWindow;
+ fconfig.row += row;
+ fconfig.col += col;
+ fconfig.window = mouse_win->handle;
+ }
+ }
+
+ bool change_external = fconfig.external != wp->w_float_config.external;
+ bool change_border = (fconfig.border != wp->w_float_config.border
+ || memcmp(fconfig.border_hl_ids,
+ wp->w_float_config.border_hl_ids,
+ sizeof fconfig.border_hl_ids) != 0);
+
+ wp->w_float_config = fconfig;
+
+ bool has_border = wp->w_floating && wp->w_float_config.border;
+ for (int i = 0; i < 4; i++) {
+ int new_adj = has_border && wp->w_float_config.border_chars[2 * i + 1][0];
+ if (new_adj != wp->w_border_adj[i]) {
+ change_border = true;
+ wp->w_border_adj[i] = new_adj;
+ }
+ }
+
+ if (!ui_has(kUIMultigrid)) {
+ wp->w_height = MIN(wp->w_height, Rows - win_border_height(wp));
+ wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp));
+ }
+
+ win_set_inner_size(wp, true);
+ must_redraw = MAX(must_redraw, UPD_VALID);
+
+ wp->w_pos_changed = true;
+ if (change_external || change_border) {
+ wp->w_hl_needs_update = true;
+ redraw_later(wp, UPD_NOT_VALID);
+ }
+
+ // compute initial position
+ if (wp->w_float_config.relative == kFloatRelativeWindow) {
+ int row = (int)wp->w_float_config.row;
+ int col = (int)wp->w_float_config.col;
+ Error dummy = ERROR_INIT;
+ win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy);
+ if (parent) {
+ row += parent->w_winrow;
+ col += parent->w_wincol;
+ ScreenGrid *grid = &parent->w_grid;
+ int row_off = 0, col_off = 0;
+ grid_adjust(&grid, &row_off, &col_off);
+ row += row_off;
+ col += col_off;
+ if (wp->w_float_config.bufpos.lnum >= 0) {
+ pos_T pos = { wp->w_float_config.bufpos.lnum + 1,
+ wp->w_float_config.bufpos.col, 0 };
+ int trow, tcol, tcolc, tcole;
+ textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true);
+ row += trow - 1;
+ col += tcol - 1;
+ }
+ }
+ api_clear_error(&dummy);
+ wp->w_winrow = row;
+ wp->w_wincol = col;
+ } else {
+ wp->w_winrow = (int)fconfig.row;
+ wp->w_wincol = (int)fconfig.col;
+ }
+
+ // changing border style while keeping border only requires redrawing border
+ if (fconfig.border) {
+ wp->w_redr_border = true;
+ redraw_later(wp, UPD_VALID);
+ }
+}
+
+static int float_zindex_cmp(const void *a, const void *b)
+{
+ return (*(win_T **)b)->w_float_config.zindex - (*(win_T **)a)->w_float_config.zindex;
+}
+
+void win_float_remove(bool bang, int count)
+{
+ kvec_t(win_T *) float_win_arr = KV_INITIAL_VALUE;
+ for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
+ kv_push(float_win_arr, wp);
+ }
+ qsort(float_win_arr.items, float_win_arr.size, sizeof(win_T *), float_zindex_cmp);
+ for (size_t i = 0; i < float_win_arr.size; i++) {
+ if (win_close(float_win_arr.items[i], false, false) == FAIL) {
+ break;
+ }
+ if (!bang) {
+ count--;
+ if (count == 0) {
+ break;
+ }
+ }
+ }
+ kv_destroy(float_win_arr);
+}
+
+void win_check_anchored_floats(win_T *win)
+{
+ for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
+ // float might be anchored to moved window
+ if (wp->w_float_config.relative == kFloatRelativeWindow
+ && wp->w_float_config.window == win->handle) {
+ wp->w_pos_changed = true;
+ }
+ }
+}
+
+void win_reconfig_floats(void)
+{
+ for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
+ win_config_float(wp, wp->w_float_config);
+ }
+}
+
+/// Return true if "win" is floating window in the current tab page.
+///
+/// @param win window to check
+bool win_float_valid(const win_T *win)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (win == NULL) {
+ return false;
+ }
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp == win) {
+ return wp->w_floating;
+ }
+ }
+ return false;
+}
diff --git a/src/nvim/winfloat.h b/src/nvim/winfloat.h
new file mode 100644
index 0000000000..c66f897ec0
--- /dev/null
+++ b/src/nvim/winfloat.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "winfloat.h.generated.h"
+#endif