diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/private/defs.h | 3 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 7 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 6 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/api/window.c | 4 | ||||
-rw-r--r-- | src/nvim/buffer.c | 54 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 8 | ||||
-rw-r--r-- | src/nvim/charset.c | 39 | ||||
-rw-r--r-- | src/nvim/eval.c | 131 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 111 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 28 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 13 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 19 | ||||
-rw-r--r-- | src/nvim/option.c | 55 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 75 | ||||
-rw-r--r-- | src/nvim/screen.c | 20 | ||||
-rw-r--r-- | src/nvim/syntax.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_alot.vim | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr.vim | 25 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr_utf8.vim | 38 | ||||
-rw-r--r-- | src/nvim/testdir/test_tabpage.vim | 58 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 2 | ||||
-rw-r--r-- | src/nvim/version.c | 97 | ||||
-rw-r--r-- | src/nvim/window.c | 56 |
25 files changed, 608 insertions, 249 deletions
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 1d5ecd3071..223aab09dc 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -91,9 +91,6 @@ typedef enum { struct object { ObjectType type; union { - Buffer buffer; - Window window; - Tabpage tabpage; Boolean boolean; Integer integer; Float floating; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index c0ee735d1a..bd83b1ff1d 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -18,6 +18,7 @@ #include "nvim/map.h" #include "nvim/option.h" #include "nvim/option_defs.h" +#include "nvim/version.h" #include "nvim/eval/typval_encode.h" #include "nvim/lib/kvec.h" @@ -615,13 +616,14 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - if (obj.data.integer > INT_MAX || obj.data.integer < INT_MIN) { + if (obj.data.integer > VARNUMBER_MAX + || obj.data.integer < VARNUMBER_MIN) { api_set_error(err, Validation, _("Integer value outside range")); return false; } tv->v_type = VAR_NUMBER; - tv->vval.v_number = (int)obj.data.integer; + tv->vval.v_number = (varnumber_T)obj.data.integer; break; case kObjectTypeFloat: @@ -763,6 +765,7 @@ Dictionary api_metadata(void) static Dictionary metadata = ARRAY_DICT_INIT; if (!metadata.size) { + PUT(metadata, "version", DICTIONARY_OBJ(version_dict())); init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a946e35149..9fe8c351cf 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -37,15 +37,15 @@ #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ - .data.buffer = s }) + .data.integer = s }) #define WINDOW_OBJ(s) ((Object) { \ .type = kObjectTypeWindow, \ - .data.window = s }) + .data.integer = s }) #define TABPAGE_OBJ(s) ((Object) { \ .type = kObjectTypeTabpage, \ - .data.tabpage = s }) + .data.integer = s }) #define ARRAY_OBJ(a) ((Object) { \ .type = kObjectTypeArray, \ diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 15ec52ebab..491375bd73 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -34,7 +34,7 @@ #endif /// Executes an ex-command. -/// On VimL error: Returns the VimL error and updates v:errmsg. +/// On VimL error: Returns the VimL error; v:errmsg is not updated. /// /// @param command Ex-command string /// @param[out] err Error details (including actual VimL error), if any diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ef881fa0eb..1f555a6a05 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -348,7 +348,7 @@ Tabpage nvim_win_get_tabpage(Window window, Error *err) /// @return Window number Integer nvim_win_get_number(Window window, Error *err) { - Integer rv = 0; + int rv = 0; win_T *win = find_window_by_handle(window, err); if (!win) { @@ -356,7 +356,7 @@ Integer nvim_win_get_number(Window window, Error *err) } int tabnr; - win_get_tabwin(window, &tabnr, (int *)&rv); + win_get_tabwin(window, &tabnr, &rv); return rv; } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5fb011885e..a66fdc1304 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -323,13 +323,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) wipe_buf = true; } - if (win_valid(win)) { - /* Set b_last_cursor when closing the last window for the buffer. - * Remember the last cursor position and window options of the buffer. - * This used to be only for the current window, but then options like - * 'foldmethod' may be lost with a ":only" command. */ - if (buf->b_nwindows == 1) + if (win_valid_any_tab(win)) { + // Set b_last_cursor when closing the last window for the buffer. + // Remember the last cursor position and window options of the buffer. + // This used to be only for the current window, but then options like + // 'foldmethod' may be lost with a ":only" command. + if (buf->b_nwindows == 1) { set_last_cursor(win); + } buflist_setfpos(buf, win, win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, win->w_cursor.col, TRUE); @@ -402,7 +403,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) buf->b_nwindows = nwindows; buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - if (win_valid(win) && win->w_buffer == buf) { + if (win_valid_any_tab(win) && win->w_buffer == buf) { win->w_buffer = NULL; // make sure we don't use the buffer now } @@ -478,17 +479,20 @@ void buf_clear_file(buf_T *buf) buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ } -/* - * buf_freeall() - free all things allocated for a buffer that are related to - * the file. flags: - * BFA_DEL buffer is going to be deleted - * BFA_WIPE buffer is going to be wiped out - * BFA_KEEP_UNDO do not free undo information - */ +/// buf_freeall() - free all things allocated for a buffer that are related to +/// the file. Careful: get here with "curwin" NULL when exiting. +/// +/// @param flags BFA_DEL buffer is going to be deleted +/// BFA_WIPE buffer is going to be wiped out +/// BFA_KEEP_UNDO do not free undo information void buf_freeall(buf_T *buf, int flags) { bool is_curbuf = (buf == curbuf); + int is_curwin = (curwin != NULL && curwin->w_buffer == buf); + win_T *the_curwin = curwin; + tabpage_T *the_curtab = curtab; + // Make sure the buffer isn't closed by autocommands. buf->b_closing = true; apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); if (!buf_valid(buf)) /* autocommands may delete the buffer */ @@ -505,8 +509,18 @@ void buf_freeall(buf_T *buf, int flags) return; } buf->b_closing = false; - if (aborting()) /* autocmds may abort script processing */ + + // If the buffer was in curwin and the window has changed, go back to that + // window, if it still exists. This avoids that ":edit x" triggering a + // "tabnext" BufUnload autocmd leaves a window behind without a buffer. + if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) { + block_autocmds(); + goto_tabpage_win(the_curtab, the_curwin); + unblock_autocmds(); + } + if (aborting()) { // autocmds may abort script processing return; + } /* * It's possible that autocommands change curbuf to the one being deleted. @@ -4509,7 +4523,7 @@ chk_modeline ( char_u *e; char_u *linecopy; /* local copy of any modeline found */ int prev; - int vers; + intmax_t vers; int end; int retval = OK; char_u *save_sourcing_name; @@ -4528,7 +4542,10 @@ chk_modeline ( e = s + 4; else e = s + 3; - vers = getdigits_int(&e); + if (getdigits_safe(&e, &vers) != OK) { + continue; + } + if (*e == ':' && (s[0] != 'V' || STRNCMP(skipwhite(e + 1), "set", 3) == 0) @@ -4536,8 +4553,9 @@ chk_modeline ( || (VIM_VERSION_100 >= vers && isdigit(s[3])) || (VIM_VERSION_100 < vers && s[3] == '<') || (VIM_VERSION_100 > vers && s[3] == '>') - || (VIM_VERSION_100 == vers && s[3] == '='))) + || (VIM_VERSION_100 == vers && s[3] == '='))) { break; + } } } prev = *s; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ab5987612c..2e6316c74a 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -488,9 +488,9 @@ struct file_buffer { bool file_id_valid; FileID file_id; - bool b_changed; /* 'modified': Set to true if something in the - file has been changed and not written out. */ - int b_changedtick; /* incremented for each change, also for undo */ + int b_changed; // 'modified': Set to true if something in the + // file has been changed and not written out. + int b_changedtick; // incremented for each change, also for undo bool b_saving; /* Set to true if we are in the middle of saving the buffer. */ @@ -655,7 +655,7 @@ struct file_buffer { long b_p_sts; ///< 'softtabstop' long b_p_sts_nopaste; ///< b_p_sts saved for paste mode char_u *b_p_sua; ///< 'suffixesadd' - bool b_p_swf; ///< 'swapfile' + int b_p_swf; ///< 'swapfile' long b_p_smc; ///< 'synmaxcol' char_u *b_p_syn; ///< 'syntax' long b_p_ts; ///< 'tabstop' diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 78f5d96fc7..61c5b10808 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1732,6 +1732,26 @@ char_u* skiptowhite_esc(char_u *p) { return p; } +/// Get a number from a string and skip over it, signalling overflows +/// +/// @param[out] pp A pointer to a pointer to char_u. +/// It will be advanced past the read number. +/// @param[out] nr Number read from the string. +/// +/// @return OK on success, FAIL on error/overflow +int getdigits_safe(char_u **pp, intmax_t *nr) +{ + errno = 0; + *nr = strtoimax((char *)(*pp), (char **)pp, 10); + + if ((*nr == INTMAX_MIN || *nr == INTMAX_MAX) + && errno == ERANGE) { + return FAIL; + } + + return OK; +} + /// Get a number from a string and skip over it. /// /// @param[out] pp A pointer to a pointer to char_u. @@ -1740,17 +1760,16 @@ char_u* skiptowhite_esc(char_u *p) { /// @return Number read from the string. intmax_t getdigits(char_u **pp) { - errno = 0; - intmax_t number = strtoimax((char *)*pp, (char **)pp, 10); - if (number == INTMAX_MAX || number == INTMAX_MIN) { - assert(errno != ERANGE); - } + intmax_t number; + int ret = getdigits_safe(pp, &number); + + (void)ret; // Avoid "unused variable" warning in Release build + assert(ret == OK); + return number; } -/// Get an int number from a string. -/// -/// A getdigits wrapper restricted to int values. +/// Get an int number from a string. Like getdigits(), but restricted to `int`. int getdigits_int(char_u **pp) { intmax_t number = getdigits(pp); @@ -1760,9 +1779,7 @@ int getdigits_int(char_u **pp) return (int)number; } -/// Get a long number from a string. -/// -/// A getdigits wrapper restricted to long values. +/// Get a long number from a string. Like getdigits(), but restricted to `long`. long getdigits_long(char_u **pp) { intmax_t number = getdigits(pp); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 76f33e7d8c..5d4241c8af 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10524,16 +10524,10 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) : file_pat_to_reg_pat(pat, NULL, NULL, false); } -/* - * "has()" function - */ +/// "has()" function static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int i; - char_u *name; - int n = FALSE; - static char *(has_list[]) = - { + static char *(has_list[]) = { #ifdef UNIX "unix", #endif @@ -10646,36 +10640,42 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) NULL }; - name = get_tv_string(&argvars[0]); - for (i = 0; has_list[i] != NULL; ++i) + bool n = false; + char *name = (char *)get_tv_string(&argvars[0]); + + for (int i = 0; has_list[i] != NULL; i++) { if (STRICMP(name, has_list[i]) == 0) { - n = TRUE; + n = true; break; } + } - if (n == FALSE) { + if (!n) { if (STRNICMP(name, "patch", 5) == 0) { if (name[5] == '-' - && STRLEN(name) > 11 + && strlen(name) > 11 && ascii_isdigit(name[6]) && ascii_isdigit(name[8]) && ascii_isdigit(name[10])) { - int major = atoi((char *)name + 6); - int minor = atoi((char *)name + 8); + int major = atoi(name + 6); + int minor = atoi(name + 8); // Expect "patch-9.9.01234". n = (major < VIM_VERSION_MAJOR || (major == VIM_VERSION_MAJOR && (minor < VIM_VERSION_MINOR || (minor == VIM_VERSION_MINOR - && has_patch(atoi((char *)name + 10)))))); + && has_vim_patch(atoi(name + 10)))))); } else { - n = has_patch(atoi((char *)name + 5)); + n = has_vim_patch(atoi(name + 5)); } + } else if (STRNICMP(name, "nvim-", 5) == 0) { + // Expect "nvim-x.y.z" + n = has_nvim_version(name + 5); } else if (STRICMP(name, "vim_starting") == 0) { n = (starting != 0); } else if (STRICMP(name, "multi_byte_encoding") == 0) { - n = has_mbyte; + n = has_mbyte != 0; #if defined(USE_ICONV) && defined(DYNAMIC_ICONV) } else if (STRICMP(name, "iconv") == 0) { n = iconv_enabled(false); @@ -10685,8 +10685,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - if (n == FALSE && eval_has_provider((char *)name)) { - n = TRUE; + if (!n && eval_has_provider(name)) { + n = true; } rettv->vval.v_number = n; @@ -15612,6 +15612,39 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "strgetchar()" function +static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *str; + int len; + int error = false; + int charidx; + + rettv->vval.v_number = -1; + str = get_tv_string_chk(&argvars[0]); + if (str == NULL) { + return; + } + len = (int)STRLEN(str); + charidx = get_tv_number_chk(&argvars[1], &error); + if (error) { + return; + } + + { + int byteidx = 0; + + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; + } + charidx--; + byteidx += mb_cptr2len(str + byteidx); + } + } +} + /* * "stridx()" function */ @@ -15712,6 +15745,64 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (varnumber_T) mb_string2cells(s); } +// "strcharpart()" function +static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + char_u *p; + int nchar; + int nbyte = 0; + int charlen; + int len = 0; + int slen; + int error = false; + + p = get_tv_string(&argvars[0]); + slen = (int)STRLEN(p); + + nchar = get_tv_number_chk(&argvars[1], &error); + if (!error) { + if (nchar > 0) { + while (nchar > 0 && nbyte < slen) { + nbyte += mb_cptr2len(p + nbyte); + nchar--; + } + } else { + nbyte = nchar; + } + } + if (argvars[2].v_type != VAR_UNKNOWN) { + charlen = get_tv_number(&argvars[2]); + while (charlen > 0 && nbyte + len < slen) { + int off = nbyte + len; + + if (off < 0) { + len += 1; + } else { + len += mb_cptr2len(p + off); + } + charlen--; + } + } else { + len = slen - nbyte; // default: all bytes that are available. + } + + // Only return the overlap between the specified part and the actual + // string. + if (nbyte < 0) { + len += nbyte; + nbyte = 0; + } else if (nbyte > slen) { + nbyte = slen; + } + if (len < 0) { + len = 0; + } else if (nbyte + len > slen) { + len = slen - nbyte; + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strnsave(p + nbyte, len); +} + /* * "strpart()" function */ diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index eaaee81533..bea25b36f3 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -268,9 +268,11 @@ return { sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, str2float={args=1}, str2nr={args={1, 2}}, + strcharpart={args={2, 3}}, strchars={args={1,2}}, strdisplaywidth={args={1, 2}}, strftime={args={1, 2}}, + strgetchar={args={2, 2}}, stridx={args={2, 3}}, string={args=1}, strlen={args=1}, diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6205daf0cb..4674460a16 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2250,28 +2250,28 @@ do_ecmd ( xfree(new_name); goto theend; } - if (buf == curbuf) /* already in new buffer */ - auto_buf = TRUE; - else { - if (curbuf == old_curbuf) + if (buf == curbuf) { // already in new buffer + auto_buf = true; + } else { + win_T *the_curwin = curwin; + + // Set the w_closing flag to avoid that autocommands close the window. + the_curwin->w_closing = true; + if (curbuf == old_curbuf) { buf_copy_options(buf, BCO_ENTER); + } - /* close the link to the current buffer */ - u_sync(FALSE); + // Close the link to the current buffer. This will set + // curwin->w_buffer to NULL. + u_sync(false); close_buffer(oldwin, curbuf, - (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, FALSE); - - /* Autocommands may open a new window and leave oldwin open - * which leads to crashes since the above call sets - * oldwin->w_buffer to NULL. */ - if (curwin != oldwin && oldwin != aucmd_win && win_valid(oldwin)) { - assert(oldwin); - if (oldwin->w_buffer == NULL) { - win_close(oldwin, FALSE); - } - } + (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, + false); + + the_curwin->w_closing = false; - if (aborting()) { /* autocmds may abort script processing */ + // autocmds may abort script processing + if (aborting() && curwin->w_buffer != NULL) { xfree(new_name); goto theend; } @@ -2370,10 +2370,12 @@ do_ecmd ( if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { /* Save all the text, so that the reload can be undone. * Sync first so that this is a separate undo-able action. */ - u_sync(FALSE); - if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE) - == FAIL) + u_sync(false); + if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, true) + == FAIL) { + xfree(new_name); goto theend; + } u_unchanged(curbuf); buf_freeall(curbuf, BFA_KEEP_UNDO); @@ -4072,61 +4074,66 @@ void ex_global(exarg_T *eap) vim_regfree(regmatch.regprog); } -/* - * Execute "cmd" on lines marked with ml_setmarked(). - */ +/// Execute `cmd` on lines marked with ml_setmarked(). void global_exe(char_u *cmd) { - linenr_T old_lcount; /* b_ml.ml_line_count before the command */ - buf_T *old_buf = curbuf; /* remember what buffer we started in */ - linenr_T lnum; /* line number according to old situation */ - - /* - * Set current position only once for a global command. - * If global_busy is set, setpcmark() will not do anything. - * If there is an error, global_busy will be incremented. - */ + linenr_T old_lcount; // b_ml.ml_line_count before the command + buf_T *old_buf = curbuf; // remember what buffer we started in + linenr_T lnum; // line number according to old situation + int save_mapped_ctrl_c = mapped_ctrl_c; + + // Set current position only once for a global command. + // If global_busy is set, setpcmark() will not do anything. + // If there is an error, global_busy will be incremented. setpcmark(); - /* When the command writes a message, don't overwrite the command. */ - msg_didout = TRUE; + // When the command writes a message, don't overwrite the command. + msg_didout = true; + // Disable CTRL-C mapping, let it interrupt (potentially long output). + mapped_ctrl_c = 0; sub_nsubs = 0; sub_nlines = 0; - global_need_beginline = FALSE; + global_need_beginline = false; global_busy = 1; old_lcount = curbuf->b_ml.ml_line_count; + while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1) { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; - if (*cmd == NUL || *cmd == '\n') + if (*cmd == NUL || *cmd == '\n') { do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT); - else + } else { do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT); + } os_breakcheck(); } + mapped_ctrl_c = save_mapped_ctrl_c; global_busy = 0; - if (global_need_beginline) + if (global_need_beginline) { beginline(BL_WHITE | BL_FIX); - else - check_cursor(); /* cursor may be beyond the end of the line */ + } else { + check_cursor(); // cursor may be beyond the end of the line + } - /* the cursor may not have moved in the text but a change in a previous - * line may move it on the screen */ + // the cursor may not have moved in the text but a change in a previous + // line may move it on the screen changed_line_abv_curs(); - /* If it looks like no message was written, allow overwriting the - * command with the report for number of changes. */ - if (msg_col == 0 && msg_scrolled == 0) - msg_didout = FALSE; + // If it looks like no message was written, allow overwriting the + // command with the report for number of changes. + if (msg_col == 0 && msg_scrolled == 0) { + msg_didout = false; + } - /* If substitutes done, report number of substitutes, otherwise report - * number of extra or deleted lines. - * Don't report extra or deleted lines in the edge case where the buffer - * we are in after execution is different from the buffer we started in. */ - if (!do_sub_msg(false) && curbuf == old_buf) + // If substitutes done, report number of substitutes, otherwise report + // number of extra or deleted lines. + // Don't report extra or deleted lines in the edge case where the buffer + // we are in after execution is different from the buffer we started in. + if (!do_sub_msg(false) && curbuf == old_buf) { msgmore(curbuf->b_ml.ml_line_count - old_lcount); + } } #if defined(EXITFREE) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9f83688e1e..5e418bf099 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1302,8 +1302,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, * 2. Handle command modifiers. */ p = ea.cmd; - if (ascii_isdigit(*ea.cmd)) - p = skipwhite(skipdigits(ea.cmd)); + p = skip_range(ea.cmd, NULL); switch (*p) { /* When adding an entry, also modify cmd_exists(). */ case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3)) @@ -1406,12 +1405,18 @@ static char_u * do_one_cmd(char_u **cmdlinep, continue; case 't': if (checkforcmd(&p, "tab", 3)) { - if (ascii_isdigit(*ea.cmd)) - cmdmod.tab = atoi((char *)ea.cmd) + 1; - else - cmdmod.tab = tabpage_index(curtab) + 1; - ea.cmd = p; - continue; + long tabnr = get_address(&ea, &ea.cmd, ADDR_TABS, ea.skip, false); + if (tabnr == MAXLNUM) { + cmdmod.tab = tabpage_index(curtab) + 1; + } else { + if (tabnr < 0 || tabnr > LAST_TAB_NR) { + errormsg = (char_u *)_(e_invrange); + goto doend; + } + cmdmod.tab = tabnr + 1; + } + ea.cmd = p; + continue; } if (!checkforcmd(&ea.cmd, "topleft", 2)) break; @@ -1766,11 +1771,8 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (text_locked() && !(ea.argt & CMDWIN) && !IS_USER_CMDIDX(ea.cmdidx)) { - /* Command not allowed when editing the command line. */ - if (cmdwin_type != 0) - errormsg = (char_u *)_(e_cmdwin); - else - errormsg = (char_u *)_(e_secure); + // Command not allowed when editing the command line. + errormsg = get_text_locked_msg(); goto doend; } /* Disallow editing another buffer when "curbuf_lock" is set. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7444eb8a38..e525c949bd 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1688,10 +1688,15 @@ int text_locked(void) { */ void text_locked_msg(void) { - if (cmdwin_type != 0) - EMSG(_(e_cmdwin)); - else - EMSG(_(e_secure)); + EMSG(_(get_text_locked_msg())); +} + +char_u * get_text_locked_msg(void) { + if (cmdwin_type != 0) { + return e_cmdwin; + } else { + return e_secure; + } } /* diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 14e1c2d978..c3a909692f 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -21,7 +21,8 @@ static msgpack_zone zone; static msgpack_sbuffer sbuffer; #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(const msgpack_object *const obj, t *const arg) \ + bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ + Integer *const arg) \ FUNC_ATTR_NONNULL_ALL \ { \ if (obj->type != MSGPACK_OBJECT_EXT \ @@ -44,12 +45,12 @@ static msgpack_sbuffer sbuffer; return true; \ } \ \ - void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ + void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ FUNC_ATTR_NONNULL_ARG(2) \ { \ msgpack_packer pac; \ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ - msgpack_pack_int64(&pac, o); \ + msgpack_pack_int64(&pac, (handle_T)o); \ msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ msgpack_sbuffer_clear(&sbuffer); \ @@ -213,17 +214,17 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) switch (cur.mobj->via.ext.type) { case kObjectTypeBuffer: { cur.aobj->type = kObjectTypeBuffer; - ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.buffer); + ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.integer); break; } case kObjectTypeWindow: { cur.aobj->type = kObjectTypeWindow; - ret = msgpack_rpc_to_window(cur.mobj, &cur.aobj->data.window); + ret = msgpack_rpc_to_window(cur.mobj, &cur.aobj->data.integer); break; } case kObjectTypeTabpage: { cur.aobj->type = kObjectTypeTabpage; - ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.tabpage); + ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.integer); break; } } @@ -369,15 +370,15 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) break; } case kObjectTypeBuffer: { - msgpack_rpc_from_buffer(cur.aobj->data.buffer, res); + msgpack_rpc_from_buffer(cur.aobj->data.integer, res); break; } case kObjectTypeWindow: { - msgpack_rpc_from_window(cur.aobj->data.window, res); + msgpack_rpc_from_window(cur.aobj->data.integer, res); break; } case kObjectTypeTabpage: { - msgpack_rpc_from_tabpage(cur.aobj->data.tabpage, res); + msgpack_rpc_from_tabpage(cur.aobj->data.integer, res); break; } case kObjectTypeArray: { diff --git a/src/nvim/option.c b/src/nvim/option.c index 81919c00d2..a255165e32 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2527,7 +2527,7 @@ did_set_string_option ( else if (varp == &p_sbo) { if (check_opt_strings(p_sbo, p_scbopt_values, TRUE) != OK) errmsg = e_invarg; - } else if (varp == &p_ambw || (bool *)varp == &p_emoji) { + } else if (varp == &p_ambw || (int *)varp == &p_emoji) { // 'ambiwidth' if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { errmsg = e_invarg; @@ -3706,23 +3706,19 @@ set_bool_option ( } } } - } - - /* - * When 'lisp' option changes include/exclude '-' in - * keyword characters. - */ - else if (varp == (char_u *)&(curbuf->b_p_lisp)) { - (void)buf_init_chartab(curbuf, FALSE); /* ignore errors */ - } - /* when 'title' changed, may need to change the title; same for 'icon' */ - else if ((int *)varp == &p_title) { - did_set_title(FALSE); + } else if (varp == (char_u *)&(curbuf->b_p_lisp)) { + // When 'lisp' option changes include/exclude '-' in + // keyword characters. + (void)buf_init_chartab(curbuf, false); // ignore errors + } else if ((int *)varp == &p_title) { + // when 'title' changed, may need to change the title; same for 'icon' + did_set_title(false); } else if ((int *)varp == &p_icon) { - did_set_title(TRUE); - } else if ((bool *)varp == &curbuf->b_changed) { - if (!value) - save_file_ff(curbuf); /* Buffer is unchanged */ + did_set_title(true); + } else if ((int *)varp == &curbuf->b_changed) { + if (!value) { + save_file_ff(curbuf); // Buffer is unchanged + } redraw_titles(); modified_was_set = value; } @@ -3750,11 +3746,12 @@ set_bool_option ( else if ((int *)varp == &curwin->w_p_wrap) { if (curwin->w_p_wrap) curwin->w_leftcol = 0; - } else if ((bool *)varp == &p_ea) { - if (p_ea && !old_value) + } else if ((int *)varp == &p_ea) { + if (p_ea && !old_value) { win_equal(curwin, false, 0); - } else if ((bool *)varp == &p_acd) { - /* Change directories when the 'acd' option is set now. */ + } + } else if ((int *)varp == &p_acd) { + // Change directories when the 'acd' option is set now. do_autochdir(); } /* 'diff' */ @@ -4513,10 +4510,11 @@ get_option_value ( else { /* Special case: 'modified' is b_changed, but we also want to consider * it set when 'ff' or 'fenc' changed. */ - if ((bool *)varp == &curbuf->b_changed) + if ((int *)varp == &curbuf->b_changed) { *numval = curbufIsChanged(); - else + } else { *numval = *(int *)varp; + } } return 1; } @@ -4884,14 +4882,15 @@ showoneopt ( varp = get_varp_scope(p, opt_flags); - /* for 'modified' we also need to check if 'ff' or 'fenc' changed. */ - if ((p->flags & P_BOOL) && ((bool *)varp == &curbuf->b_changed - ? !curbufIsChanged() : !*(bool *)varp)) + // for 'modified' we also need to check if 'ff' or 'fenc' changed. + if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed + ? !curbufIsChanged() : !*(int *)varp)) { MSG_PUTS("no"); - else if ((p->flags & P_BOOL) && *(int *)varp < 0) + } else if ((p->flags & P_BOOL) && *(int *)varp < 0) { MSG_PUTS("--"); - else + } else { MSG_PUTS(" "); + } MSG_PUTS(p->fullname); if (!(p->flags & P_BOOL)) { msg_putchar('='); diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e085b973ea..8af6f2194f 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -1,8 +1,6 @@ #ifndef NVIM_OPTION_DEFS_H #define NVIM_OPTION_DEFS_H -#include <stdbool.h> - #include "nvim/types.h" #include "nvim/macros.h" // For EXTERN @@ -296,16 +294,16 @@ enum { * The following are actual variables for the options */ -EXTERN long p_aleph; /* 'aleph' */ -EXTERN bool p_acd; /* 'autochdir' */ -EXTERN char_u *p_ambw; /* 'ambiwidth' */ -EXTERN int p_ar; /* 'autoread' */ -EXTERN int p_aw; /* 'autowrite' */ -EXTERN int p_awa; /* 'autowriteall' */ -EXTERN char_u *p_bs; /* 'backspace' */ -EXTERN char_u *p_bg; /* 'background' */ -EXTERN int p_bk; /* 'backup' */ -EXTERN char_u *p_bkc; /* 'backupcopy' */ +EXTERN long p_aleph; // 'aleph' +EXTERN int p_acd; // 'autochdir' +EXTERN char_u *p_ambw; // 'ambiwidth' +EXTERN int p_ar; // 'autoread' +EXTERN int p_aw; // 'autowrite' +EXTERN int p_awa; // 'autowriteall' +EXTERN char_u *p_bs; // 'backspace' +EXTERN char_u *p_bg; // 'background' +EXTERN int p_bk; // 'backup' +EXTERN char_u *p_bkc; // 'backupcopy' EXTERN unsigned int bkc_flags; ///< flags from 'backupcopy' #ifdef IN_OPTION_C static char *(p_bkc_values[]) = @@ -397,14 +395,15 @@ EXTERN char_u *p_dir; /* 'directory' */ EXTERN char_u *p_dy; /* 'display' */ EXTERN unsigned dy_flags; #ifdef IN_OPTION_C -static char *(p_dy_values[]) = {"lastline", "uhex", NULL}; +static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", NULL }; #endif #define DY_LASTLINE 0x001 -#define DY_UHEX 0x002 +#define DY_TRUNCATE 0x002 +#define DY_UHEX 0x004 EXTERN int p_ed; // 'edcompatible' -EXTERN bool p_emoji; // 'emoji' +EXTERN int p_emoji; // 'emoji' EXTERN char_u *p_ead; // 'eadirection' -EXTERN bool p_ea; // 'equalalways' +EXTERN int p_ea; // 'equalalways' EXTERN char_u *p_ep; // 'equalprg' EXTERN int p_eb; // 'errorbells' EXTERN char_u *p_ef; // 'errorfile' @@ -416,7 +415,7 @@ EXTERN int p_ek; // 'esckeys' EXTERN int p_exrc; // 'exrc' EXTERN char_u *p_fencs; // 'fileencodings' EXTERN char_u *p_ffs; // 'fileformats' -EXTERN bool p_fic; // 'fileignorecase' +EXTERN int p_fic; // 'fileignorecase' EXTERN char_u *p_fcl; // 'foldclose' EXTERN long p_fdls; // 'foldlevelstart' EXTERN char_u *p_fdo; // 'foldopen' @@ -621,7 +620,7 @@ EXTERN long p_titlelen; ///< 'titlelen' EXTERN char_u *p_titleold; ///< 'titleold' EXTERN char_u *p_titlestring; ///< 'titlestring' EXTERN char_u *p_tsr; ///< 'thesaurus' -EXTERN bool p_tgc; ///< 'termguicolors' +EXTERN int p_tgc; ///< 'termguicolors' EXTERN int p_ttimeout; ///< 'ttimeout' EXTERN long p_ttm; ///< 'ttimeoutlen' EXTERN char_u *p_udir; ///< 'undodir' @@ -650,26 +649,26 @@ char_u *p_vfile = (char_u *)""; /* used before options are initialized */ #else extern char_u *p_vfile; /* 'verbosefile' */ #endif -EXTERN int p_warn; /* 'warn' */ -EXTERN char_u *p_wop; /* 'wildoptions' */ -EXTERN long p_window; /* 'window' */ -EXTERN char_u *p_wak; /* 'winaltkeys' */ -EXTERN char_u *p_wig; /* 'wildignore' */ -EXTERN char_u *p_ww; /* 'whichwrap' */ -EXTERN long p_wc; /* 'wildchar' */ -EXTERN long p_wcm; /* 'wildcharm' */ -EXTERN bool p_wic; ///< 'wildignorecase' -EXTERN char_u *p_wim; /* 'wildmode' */ -EXTERN int p_wmnu; /* 'wildmenu' */ -EXTERN long p_wh; /* 'winheight' */ -EXTERN long p_wmh; /* 'winminheight' */ -EXTERN long p_wmw; /* 'winminwidth' */ -EXTERN long p_wiw; /* 'winwidth' */ -EXTERN bool p_ws; /* 'wrapscan' */ -EXTERN int p_write; /* 'write' */ -EXTERN int p_wa; /* 'writeany' */ -EXTERN int p_wb; /* 'writebackup' */ -EXTERN long p_wd; /* 'writedelay' */ +EXTERN int p_warn; // 'warn' +EXTERN char_u *p_wop; // 'wildoptions' +EXTERN long p_window; // 'window' +EXTERN char_u *p_wak; // 'winaltkeys' +EXTERN char_u *p_wig; // 'wildignore' +EXTERN char_u *p_ww; // 'whichwrap' +EXTERN long p_wc; // 'wildchar' +EXTERN long p_wcm; // 'wildcharm' +EXTERN int p_wic; // 'wildignorecase' +EXTERN char_u *p_wim; // 'wildmode' +EXTERN int p_wmnu; // 'wildmenu' +EXTERN long p_wh; // 'winheight' +EXTERN long p_wmh; // 'winminheight' +EXTERN long p_wmw; // 'winminwidth' +EXTERN long p_wiw; // 'winwidth' +EXTERN int p_ws; // 'wrapscan' +EXTERN int p_write; // 'write' +EXTERN int p_wa; // 'writeany' +EXTERN int p_wb; // 'writebackup' +EXTERN long p_wd; // 'writedelay' EXTERN int p_force_on; ///< options that cannot be turned off. EXTERN int p_force_off; ///< options that cannot be turned on. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 47dd640e59..3e4d016fe7 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1401,7 +1401,7 @@ static void win_update(win_T *wp) && wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline - && !(dy_flags & DY_LASTLINE) + && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) && srow + wp->w_lines[idx].wl_size > wp->w_height && diff_check_fill(wp, lnum) == 0 ) { @@ -1484,10 +1484,20 @@ static void win_update(win_T *wp) /* Window ends in filler lines. */ wp->w_botline = lnum; wp->w_filler_rows = wp->w_height - srow; - } else if (dy_flags & DY_LASTLINE) { /* 'display' has "lastline" */ - /* - * Last line isn't finished: Display "@@@" at the end. - */ + } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" + int scr_row = wp->w_winrow + wp->w_height - 1; + + // Last line isn't finished: Display "@@@" in the last screen line. + screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, + hl_attr(HLF_AT)); + + screen_fill(scr_row, scr_row + 1, + (int)wp->w_wincol + 2, (int)W_ENDCOL(wp), + '@', ' ', hl_attr(HLF_AT)); + set_empty_rows(wp, srow); + wp->w_botline = lnum; + } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + // Last line isn't finished: Display "@@@" at the end. screen_fill(wp->w_winrow + wp->w_height - 1, wp->w_winrow + wp->w_height, W_ENDCOL(wp) - 3, W_ENDCOL(wp), diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 6fd7603629..b49ae9da21 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5537,7 +5537,7 @@ void ex_ownsyntax(exarg_T *eap) } } -int syntax_present(win_T *win) +bool syntax_present(win_T *win) { return win->w_s->b_syn_patterns.ga_len != 0 || win->w_s->b_syn_clusters.ga_len != 0 diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 036a4c0470..87c1cd2c58 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -4,6 +4,8 @@ source test_assign.vim source test_cursor_func.vim source test_ex_undo.vim +source test_expr.vim +source test_expr_utf8.vim source test_feedkeys.vim source test_cmdline.vim source test_menu.vim diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim new file mode 100644 index 0000000000..7ea4ebc7df --- /dev/null +++ b/src/nvim/testdir/test_expr.vim @@ -0,0 +1,25 @@ +" Tests for expressions. + +func Test_strgetchar() + call assert_equal(char2nr('a'), strgetchar('axb', 0)) + call assert_equal(char2nr('x'), strgetchar('axb', 1)) + call assert_equal(char2nr('b'), strgetchar('axb', 2)) + + call assert_equal(-1, strgetchar('axb', -1)) + call assert_equal(-1, strgetchar('axb', 3)) + call assert_equal(-1, strgetchar('', 0)) +endfunc + +func Test_strcharpart() + call assert_equal('a', strcharpart('axb', 0, 1)) + call assert_equal('x', strcharpart('axb', 1, 1)) + call assert_equal('b', strcharpart('axb', 2, 1)) + call assert_equal('xb', strcharpart('axb', 1)) + + call assert_equal('', strcharpart('axb', 1, 0)) + call assert_equal('', strcharpart('axb', 1, -1)) + call assert_equal('', strcharpart('axb', -1, 1)) + call assert_equal('', strcharpart('axb', -2, 2)) + + call assert_equal('a', strcharpart('axb', -1, 2)) +endfunc diff --git a/src/nvim/testdir/test_expr_utf8.vim b/src/nvim/testdir/test_expr_utf8.vim new file mode 100644 index 0000000000..7bdcb4f65f --- /dev/null +++ b/src/nvim/testdir/test_expr_utf8.vim @@ -0,0 +1,38 @@ +" Tests for expressions using utf-8. +if !has('multi_byte') + finish +endif +scriptencoding utf-8 + +func Test_strgetchar_utf8() + call assert_equal(char2nr('á'), strgetchar('áxb', 0)) + call assert_equal(char2nr('x'), strgetchar('áxb', 1)) + + call assert_equal(char2nr('a'), strgetchar('àxb', 0)) + call assert_equal(char2nr('̀'), strgetchar('àxb', 1)) + call assert_equal(char2nr('x'), strgetchar('àxb', 2)) + + call assert_equal(char2nr('あ'), strgetchar('あaい', 0)) + call assert_equal(char2nr('a'), strgetchar('あaい', 1)) + call assert_equal(char2nr('い'), strgetchar('あaい', 2)) +endfunc + +func Test_strcharpart_utf8() + call assert_equal('áxb', strcharpart('áxb', 0)) + call assert_equal('á', strcharpart('áxb', 0, 1)) + call assert_equal('x', strcharpart('áxb', 1, 1)) + + call assert_equal('いうeお', strcharpart('あいうeお', 1)) + call assert_equal('い', strcharpart('あいうeお', 1, 1)) + call assert_equal('いう', strcharpart('あいうeお', 1, 2)) + call assert_equal('いうe', strcharpart('あいうeお', 1, 3)) + call assert_equal('いうeお', strcharpart('あいうeお', 1, 4)) + call assert_equal('eお', strcharpart('あいうeお', 3)) + call assert_equal('e', strcharpart('あいうeお', 3, 1)) + + call assert_equal('あ', strcharpart('あいうeお', -3, 4)) + + call assert_equal('a', strcharpart('àxb', 0, 1)) + call assert_equal('̀', strcharpart('àxb', 1, 1)) + call assert_equal('x', strcharpart('àxb', 2, 1)) +endfunc diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index e6b85d6e14..0bf7d056de 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -186,4 +186,62 @@ function Test_tabpage_with_autocmd() bw! endfunction +function Test_tabpage_with_tab_modifier() + for n in range(4) + tabedit + endfor + + function s:check_tab(pre_nr, cmd, post_nr) + exec 'tabnext ' . a:pre_nr + exec a:cmd + call assert_equal(a:post_nr, tabpagenr()) + call assert_equal('help', &filetype) + helpclose + endfunc + + call s:check_tab(1, 'tab help', 2) + call s:check_tab(1, '3tab help', 4) + call s:check_tab(1, '.tab help', 2) + call s:check_tab(1, '.+1tab help', 3) + call s:check_tab(1, '0tab help', 1) + call s:check_tab(2, '+tab help', 4) + call s:check_tab(2, '+2tab help', 5) + call s:check_tab(4, '-tab help', 4) + call s:check_tab(4, '-2tab help', 3) + call s:check_tab(3, '$tab help', 6) + call assert_fails('99tab help', 'E16:') + call assert_fails('+99tab help', 'E16:') + call assert_fails('-99tab help', 'E16:') + + delfunction s:check_tab + tabonly! + bw! +endfunction + +func Test_tabnext_on_buf_unload1() + " This once caused a crash + new + tabedit + tabfirst + au BufUnload <buffer> tabnext + q + + while tabpagenr('$') > 1 + bwipe! + endwhile +endfunc + +func Test_tabnext_on_buf_unload2() + " This once caused a crash + tabedit + autocmd BufUnload <buffer> tabnext + file x + edit y + + while tabpagenr('$') > 1 + bwipe! + endwhile +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index f252b00be2..5e30517c5a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -611,6 +611,7 @@ static void suspend_event(void **argv) bool enable_mouse = data->mouse_enabled; tui_terminal_stop(ui); data->cont_received = false; + stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) kill(0, SIGTSTP); while (!data->cont_received) { // poll the event loop until SIGCONT is received @@ -620,6 +621,7 @@ static void suspend_event(void **argv) if (enable_mouse) { tui_mouse_on(ui); } + stream_set_blocking(input_global_fd(), false); // libuv expects this // resume the main thread CONTINUE(data->bridge); } diff --git a/src/nvim/version.c b/src/nvim/version.c index 0cb6ea199b..055a997cd4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -7,6 +7,7 @@ #include <assert.h> #include <limits.h> +#include "nvim/api/private/helpers.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/iconv.h" @@ -129,10 +130,10 @@ static int included_patches[] = { // 2315, // 2314, // 2313, - // 2312, + 2312, // 2311, // 2310 NA - // 2309, + 2309, // 2308 NA // 2307, // 2306, @@ -204,7 +205,7 @@ static int included_patches[] = { // 2240, // 2239, // 2238 NA - // 2237, + 2237, // 2236, // 2235, // 2234 NA @@ -229,7 +230,7 @@ static int included_patches[] = { // 2215, // 2214 NA 2213, - // 2212, + 2212, // 2211 NA // 2210 NA // 2209, @@ -313,7 +314,7 @@ static int included_patches[] = { // 2131 NA // 2130 NA // 2129 NA - // 2128, + 2128, // 2127, // 2126, // 2125, @@ -332,7 +333,7 @@ static int included_patches[] = { 2112, // 2111, // 2110, - // 2109, + 2109, // 2108 NA // 2107, // 2106, @@ -660,10 +661,10 @@ static int included_patches[] = { // 1785, // 1784 NA // 1783, - // 1782, + 1782, // 1781, // 1780, - // 1779, + 1779, // 1778 NA // 1777 NA // 1776 NA @@ -701,19 +702,19 @@ static int included_patches[] = { // 1745 NA // 1744 NA // 1743 NA - // 1742, - // 1741, + 1742, + 1741, 1740, // 1739, // 1738, // 1737 NA // 1736 NA // 1735, - // 1734, + 1734, // 1733 NA 1732, // 1731, - // 1730, + 1730, // 1729 NA 1728, // 1727, @@ -2457,20 +2458,72 @@ static char *(extra_patches[]) = { NULL }; -/// Checks whether patch `n` has been included. +/// Compares a version string to the current Nvim version. /// -/// @param n The patch number. +/// @param version Version string like "1.3.42" /// -/// @return TRUE if patch "n" has been included. -int has_patch(int n) +/// @return true if Nvim is at or above the version. +bool has_nvim_version(char *version_str) + FUNC_ATTR_NONNULL_ALL { - int i; - for (i = 0; included_patches[i] != 0; ++i) { + char *p = version_str; + int major = 0; + int minor = 0; + int patch = 0; + + if (!ascii_isdigit(*p)) { + return false; + } + major = atoi(p); + p = strchr(p, '.'); // Find the next dot. + + if (p) { + p++; // Advance past the dot. + if (!ascii_isdigit(*p)) { + return false; + } + minor = atoi(p); + p = strchr(p, '.'); + if (p) { + p++; + if (!ascii_isdigit(*p)) { + return false; + } + patch = atoi(p); + } + } + + return (major < NVIM_VERSION_MAJOR + || (major == NVIM_VERSION_MAJOR + && (minor < NVIM_VERSION_MINOR + || (minor == NVIM_VERSION_MINOR + && patch <= NVIM_VERSION_PATCH)))); +} + +/// Checks whether a Vim patch has been included. +/// +/// @param n Patch number. +/// +/// @return true if patch `n` has been included. +bool has_vim_patch(int n) +{ + for (int i = 0; included_patches[i] != 0; i++) { if (included_patches[i] == n) { - return TRUE; + return true; } } - return FALSE; + return false; +} + +Dictionary version_dict(void) { + Dictionary d = ARRAY_DICT_INIT; + PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR)); + PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR)); + PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH)); + PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL)); + PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT)); + PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE)); + return d; } void ex_version(exarg_T *eap) @@ -2529,7 +2582,11 @@ static void list_features(void) } } else { while (msg_col % width) { + int old_msg_col = msg_col; msg_putchar(' '); + if (old_msg_col == msg_col) { + break; // XXX: Avoid infinite loop. + } } } } else { diff --git a/src/nvim/window.c b/src/nvim/window.c index 03a2e9a842..9c6a2e26a6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1065,6 +1065,23 @@ bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Check if "win" is a pointer to an existing window in any tabpage. +/// +/// @param win window to check +bool win_valid_any_tab(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (win == NULL) { + return false; + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp == win) { + return true; + } + } + return false; +} + /* * Return the number of windows. */ @@ -1918,7 +1935,7 @@ int win_close(win_T *win, int free_buf) if (win->w_buffer != NULL) { win->w_closing = true; close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); - if (win_valid(win)) { + if (win_valid_any_tab(win)) { win->w_closing = false; } @@ -1938,11 +1955,19 @@ int win_close(win_T *win, int free_buf) curwin->w_buffer = curbuf; getout(0); } - /* Autocommands may have closed the window already, or closed the only - * other window or moved to another tab page. */ - else if (!win_valid(win) || last_window() || curtab != prev_curtab - || close_last_window_tabpage(win, free_buf, prev_curtab)) + // Autocommands may have moved to another tab page. + if (curtab != prev_curtab && win_valid_any_tab(win) + && win->w_buffer == NULL) { + // Need to close the window anyway, since the buffer is NULL. + win_close_othertab(win, false, prev_curtab); return FAIL; + } + // Autocommands may have closed the window already, or closed the only + // other window or moved to another tab page. + if (!win_valid(win) || last_window() + || close_last_window_tabpage(win, free_buf, prev_curtab)) { + return FAIL; + } // let terminal buffers know that this window dimensions may be ignored win->w_closing = true; @@ -2019,12 +2044,16 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) tabpage_T *ptp = NULL; int free_tp = FALSE; - assert(win->w_buffer); // to avoid np dereference warning in next line - if (win->w_closing || win->w_buffer->b_closing) - return; /* window is already being closed */ + // Get here with win->w_buffer == NULL when win_close() detects the tab page + // changed. + if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) { + return; // window is already being closed + } - /* Close the link to the buffer. */ - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); + if (win->w_buffer != NULL) { + // Close the link to the buffer. + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false); + } /* Careful: Autocommands may have closed the tab page or made it the * current tab page. */ @@ -3213,11 +3242,8 @@ void goto_tabpage(int n) int i; if (text_locked()) { - /* Not allowed when editing the command line. */ - if (cmdwin_type != 0) - EMSG(_(e_cmdwin)); - else - EMSG(_(e_secure)); + // Not allowed when editing the command line. + text_locked_msg(); return; } |