diff options
Diffstat (limited to 'src')
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 |