diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2019-10-02 22:43:59 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-02 22:43:59 -0700 |
commit | 30479417e808eac338025dbbfbd3724e158fb344 (patch) | |
tree | a8f0e660fc18436506f323d0e2be0eb3236485b6 | |
parent | b069e9b20f9e1f24fde34bee7d6e5d95f47ef10d (diff) | |
parent | 4518f230fa84e66737f6fc313fb669984974a1fd (diff) | |
download | rneovim-30479417e808eac338025dbbfbd3724e158fb344.tar.gz rneovim-30479417e808eac338025dbbfbd3724e158fb344.tar.bz2 rneovim-30479417e808eac338025dbbfbd3724e158fb344.zip |
Merge #11087 from janlazo/vim-8.1.0010
vim-patch:8.1.{10,230,315,330,514,517,518,1327,1347,1758,2072,2074,2091,2095,2103}
-rw-r--r-- | runtime/doc/windows.txt | 8 | ||||
-rw-r--r-- | runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 11 | ||||
-rw-r--r-- | src/nvim/buffer.c | 7 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
-rw-r--r-- | src/nvim/fileio.c | 2 | ||||
-rw-r--r-- | src/nvim/getchar.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 19 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 420 | ||||
-rw-r--r-- | src/nvim/testdir/runtest.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_normal.vim | 240 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 14 | ||||
-rw-r--r-- | src/nvim/testdir/test_window_cmd.vim | 102 | ||||
-rw-r--r-- | src/nvim/window.c | 24 | ||||
-rw-r--r-- | test/functional/ui/inccommand_spec.lua | 36 | ||||
-rw-r--r-- | test/functional/ui/mouse_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/ui/popupmenu_spec.lua | 6 |
17 files changed, 613 insertions, 289 deletions
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 76bb096ee3..977e0daef7 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -201,9 +201,11 @@ CTRL-W CTRL_N *CTRL-W_CTRL-N* |:find|. Doesn't split if {file} is not found. CTRL-W CTRL-^ *CTRL-W_CTRL-^* *CTRL-W_^* -CTRL-W ^ Does ":split #", split window in two and edit alternate file. - When a count is given, it becomes ":split #N", split window - and edit buffer N. +CTRL-W ^ Split the current window in two and edit the alternate file. + When a count N is given, split the current window and edit + buffer N. Similar to ":sp #" and ":sp #N", but it allows the + other buffer to be unnamed. This command matches the behavior + of |CTRL-^|, except that it splits a window first. CTRL-W ge *CTRL-W_ge* Detach the current window as an external window. diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a97461ad69..52b4829f5f 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -67,8 +67,8 @@ command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-ar command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) " Name of the gdb command, defaults to "gdb". -if !exists('termdebugger') - let termdebugger = 'gdb' +if !exists('g:termdebugger') + let g:termdebugger = 'gdb' endif let s:pc_id = 12 @@ -106,9 +106,14 @@ endfunc func s:StartDebug_internal(dict) if exists('s:gdbwin') - echoerr 'Terminal debugger already running' + echoerr 'Terminal debugger already running, cannot run two' return endif + if !executable(g:termdebugger) + echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' + return + endif + let s:ptywin = 0 let s:pid = 0 diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b50c764ac3..e5b80693a4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5179,6 +5179,13 @@ bool bt_help(const buf_T *const buf) return buf != NULL && buf->b_help; } +// Return true if "buf" is a normal buffer, 'buftype' is empty. +bool bt_normal(const buf_T *const buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return buf != NULL && buf->b_p_bt[0] == NUL; +} + // Return true if "buf" is the quickfix buffer. bool bt_quickfix(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cb1dd1d631..e3e5bb9a90 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5746,13 +5746,13 @@ static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } item = tv_dict_item_alloc((const char *)key); - tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); } } + tv_clear(&tvkey); if (**arg == '}') break; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a6931f3acd..34b4c10d3e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9553,7 +9553,7 @@ put_view( */ if ((*flagp & SSOP_FOLDS) && wp->w_buffer->b_ffname != NULL - && (*wp->w_buffer->b_p_bt == NUL || bt_help(wp->w_buffer)) + && (bt_normal(wp->w_buffer) || bt_help(wp->w_buffer)) ) { if (put_folds(fd, wp) == FAIL) return FAIL; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 4cf42b41e9..0394639a16 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -4834,7 +4834,7 @@ buf_check_timestamp( if (buf->terminal || buf->b_ffname == NULL || buf->b_ml.ml_mfp == NULL - || *buf->b_p_bt != NUL + || !bt_normal(buf) || buf->b_saving || busy ) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 2469bb5baa..af642a8e11 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1165,12 +1165,12 @@ void free_typebuf(void) if (typebuf.tb_buf == typebuf_init) { internal_error("Free typebuf 1"); } else { - xfree(typebuf.tb_buf); + XFREE_CLEAR(typebuf.tb_buf); } if (typebuf.tb_noremap == noremapbuf_init) { internal_error("Free typebuf 2"); } else { - xfree(typebuf.tb_noremap); + XFREE_CLEAR(typebuf.tb_noremap); } } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 4dfde96e94..e32b738c7e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3932,11 +3932,11 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) while (dist--) { if (dir == BACKWARD) { - if ((long)curwin->w_curswant >= width2) - /* move back within line */ + if (curwin->w_curswant > width2) { + // move back within line curwin->w_curswant -= width2; - else { - /* to previous line */ + } else { + // to previous line if (curwin->w_cursor.lnum == 1) { retval = false; break; @@ -4680,9 +4680,8 @@ static void nv_ctrlo(cmdarg_T *cap) } } -/* - * CTRL-^ command, short for ":e #" - */ +// CTRL-^ command, short for ":e #". Works even when the alternate buffer is +// not named. static void nv_hat(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) @@ -6795,10 +6794,14 @@ static void nv_g_cmd(cmdarg_T *cap) } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) clearopbeep(oap); } else { + if (cap->count1 > 1) { + // if it fails, let the cursor still move to the last char + cursor_down(cap->count1 - 1, false); + } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; coladvance((colnr_T)i); - /* Make sure we stick in this column. */ + // Make sure we stick in this column. validate_virtcol(); curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = false; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 528829e63d..c5e8d4b490 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -264,15 +264,152 @@ static struct fmtpattern { 'o', ".\\+" } }; +// Convert an errorformat pattern to a regular expression pattern. +// See fmt_pat definition above for the list of supported patterns. +static char_u *fmtpat_to_regpat( + const char_u *efmp, + efm_T *fmt_ptr, + int idx, + int round, + char_u *ptr, + char_u *errmsg, + size_t errmsglen) + FUNC_ATTR_NONNULL_ALL +{ + if (fmt_ptr->addr[idx]) { + // Each errorformat pattern can occur only once + snprintf((char *)errmsg, errmsglen, + _("E372: Too many %%%c in format string"), *efmp); + EMSG(errmsg); + return NULL; + } + if ((idx && idx < 6 + && vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL) + || (idx == 6 + && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) { + snprintf((char *)errmsg, errmsglen, + _("E373: Unexpected %%%c in format string"), *efmp); + EMSG(errmsg); + return NULL; + } + fmt_ptr->addr[idx] = (char_u)++round; + *ptr++ = '\\'; + *ptr++ = '('; +#ifdef BACKSLASH_IN_FILENAME + if (*efmp == 'f') { + // Also match "c:" in the file name, even when + // checking for a colon next: "%f:". + // "\%(\a:\)\=" + STRCPY(ptr, "\\%(\\a:\\)\\="); + ptr += 10; + } +#endif + if (*efmp == 'f' && efmp[1] != NUL) { + if (efmp[1] != '\\' && efmp[1] != '%') { + // A file name may contain spaces, but this isn't + // in "\f". For "%f:%l:%m" there may be a ":" in + // the file name. Use ".\{-1,}x" instead (x is + // the next character), the requirement that :999: + // follows should work. + STRCPY(ptr, ".\\{-1,}"); + ptr += 7; + } else { + // File name followed by '\\' or '%': include as + // many file name chars as possible. + STRCPY(ptr, "\\f\\+"); + ptr += 4; + } + } else { + char_u *srcptr = (char_u *)fmt_pat[idx].pattern; + while ((*ptr = *srcptr++) != NUL) { + ptr++; + } + } + *ptr++ = '\\'; + *ptr++ = ')'; + + return ptr; +} + +// Convert a scanf like format in 'errorformat' to a regular expression. +static char_u *scanf_fmt_to_regpat( + const char_u *efm, + int len, + const char_u **pefmp, + char_u *ptr, + char_u *errmsg, + size_t errmsglen) + FUNC_ATTR_NONNULL_ALL +{ + const char_u *efmp = *pefmp; + + if (*++efmp == '[' || *efmp == '\\') { + if ((*ptr++ = *efmp) == '[') { // %*[^a-z0-9] etc. + if (efmp[1] == '^') { + *ptr++ = *++efmp; + } + if (efmp < efm + len) { + *ptr++ = *++efmp; // could be ']' + while (efmp < efm + len && (*ptr++ = *++efmp) != ']') { + } + if (efmp == efm + len) { + EMSG(_("E374: Missing ] in format string")); + return NULL; + } + } + } else if (efmp < efm + len) { // %*\D, %*\s etc. + *ptr++ = *++efmp; + } + *ptr++ = '\\'; + *ptr++ = '+'; + } else { + // TODO(vim): scanf()-like: %*ud, %*3c, %*f, ... ? + snprintf((char *)errmsg, errmsglen, + _("E375: Unsupported %%%c in format string"), *efmp); + EMSG(errmsg); + return NULL; + } + + *pefmp = efmp; + + return ptr; +} + +// Analyze/parse an errorformat prefix. +static int efm_analyze_prefix(const char_u **pefmp, efm_T *fmt_ptr, + char_u *errmsg, size_t errmsglen) + FUNC_ATTR_NONNULL_ALL +{ + const char_u *efmp = *pefmp; + + if (vim_strchr((char_u *)"+-", *efmp) != NULL) { + fmt_ptr->flags = *efmp++; + } + if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) { + fmt_ptr->prefix = *efmp; + } else { + snprintf((char *)errmsg, errmsglen, + _("E376: Invalid %%%c in format string prefix"), *efmp); + EMSG(errmsg); + return FAIL; + } + + *pefmp = efmp; + + return OK; +} + + // Converts a 'errorformat' string to regular expression pattern -static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, - char_u *regpat, char_u *errmsg) +static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, + char_u *regpat, char_u *errmsg, size_t errmsglen) + FUNC_ATTR_NONNULL_ALL { // Build regexp pattern from current 'errorformat' option char_u *ptr = regpat; *ptr++ = '^'; int round = 0; - for (char_u *efmp = efm; efmp < efm + len; efmp++) { + for (const char_u *efmp = efm; efmp < efm + len; efmp++) { if (*efmp == '%') { efmp++; int idx; @@ -282,89 +419,15 @@ static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, } } if (idx < FMT_PATTERNS) { - if (fmt_ptr->addr[idx]) { - snprintf((char *)errmsg, CMDBUFFSIZE + 1, - _("E372: Too many %%%c in format string"), *efmp); - EMSG(errmsg); - return -1; - } - if ((idx - && idx < 6 - && vim_strchr((char_u *)"DXOPQ", fmt_ptr->prefix) != NULL) - || (idx == 6 - && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL)) { - snprintf((char *)errmsg, CMDBUFFSIZE + 1, - _("E373: Unexpected %%%c in format string"), *efmp); - EMSG(errmsg); + ptr = fmtpat_to_regpat(efmp, fmt_ptr, idx, round, ptr, + errmsg, errmsglen); + if (ptr == NULL) { return -1; } round++; - fmt_ptr->addr[idx] = (char_u)round; - *ptr++ = '\\'; - *ptr++ = '('; -#ifdef BACKSLASH_IN_FILENAME - if (*efmp == 'f') { - // Also match "c:" in the file name, even when - // checking for a colon next: "%f:". - // "\%(\a:\)\=" - STRCPY(ptr, "\\%(\\a:\\)\\="); - ptr += 10; - } -#endif - if (*efmp == 'f' && efmp[1] != NUL) { - if (efmp[1] != '\\' && efmp[1] != '%') { - // A file name may contain spaces, but this isn't - // in "\f". For "%f:%l:%m" there may be a ":" in - // the file name. Use ".\{-1,}x" instead (x is - // the next character), the requirement that :999: - // follows should work. - STRCPY(ptr, ".\\{-1,}"); - ptr += 7; - } else { - // File name followed by '\\' or '%': include as - // many file name chars as possible. - STRCPY(ptr, "\\f\\+"); - ptr += 4; - } - } else { - char_u *srcptr = (char_u *)fmt_pat[idx].pattern; - while ((*ptr = *srcptr++) != NUL) { - ptr++; - } - } - *ptr++ = '\\'; - *ptr++ = ')'; } else if (*efmp == '*') { - if (*++efmp == '[' || *efmp == '\\') { - if ((*ptr++ = *efmp) == '[') { // %*[^a-z0-9] etc. - if (efmp[1] == '^') { - *ptr++ = *++efmp; - } - if (efmp < efm + len) { - efmp++; - *ptr++ = *efmp; // could be ']' - while (efmp < efm + len) { - efmp++; - if ((*ptr++ = *efmp) == ']') { - break; - } - } - if (efmp == efm + len) { - EMSG(_("E374: Missing ] in format string")); - return -1; - } - } - } else if (efmp < efm + len) { // %*\D, %*\s etc. - efmp++; - *ptr++ = *efmp; - } - *ptr++ = '\\'; - *ptr++ = '+'; - } else { - // TODO(vim): scanf()-like: %*ud, %*3c, %*f, ... ? - snprintf((char *)errmsg, CMDBUFFSIZE + 1, - _("E375: Unsupported %%%c in format string"), *efmp); - EMSG(errmsg); + ptr = scanf_fmt_to_regpat(efm, len, &efmp, ptr, errmsg, errmsglen); + if (ptr == NULL) { return -1; } } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) { @@ -374,15 +437,7 @@ static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, } else if (*efmp == '>') { fmt_ptr->conthere = true; } else if (efmp == efm + 1) { // analyse prefix - if (vim_strchr((char_u *)"+-", *efmp) != NULL) { - fmt_ptr->flags = *efmp++; - } - if (vim_strchr((char_u *)"DXAEWICZGOPQ", *efmp) != NULL) { - fmt_ptr->prefix = *efmp; - } else { - snprintf((char *)errmsg, CMDBUFFSIZE + 1, - _("E376: Invalid %%%c in format string prefix"), *efmp); - EMSG(errmsg); + if (efm_analyze_prefix(&efmp, fmt_ptr, errmsg, errmsglen) == FAIL) { return -1; } } else { @@ -461,7 +516,7 @@ static efm_T * parse_efm_option(char_u *efm) } } - if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg) == -1) { + if (efm_to_regpat(efm, len, fmt_ptr, fmtstr, errmsg, errmsglen) == -1) { goto parse_efm_error; } if ((fmt_ptr->prog = vim_regcomp(fmtstr, RE_MAGIC + RE_STRING)) == NULL) { @@ -1499,6 +1554,7 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, * Allocate a new location list */ static qf_info_T *ll_new_list(void) + FUNC_ATTR_NONNULL_RET { qf_info_T *qi = xcalloc(1, sizeof(qf_info_T)); qi->qf_refcount++; @@ -2084,7 +2140,7 @@ static win_T *qf_find_win_with_normal_buf(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer->b_p_bt[0] == NUL) { + if (bt_normal(wp->w_buffer)) { return wp; } } @@ -2148,7 +2204,7 @@ static void qf_goto_win_with_ll_file(win_T *use_win, int qf_fnum, // Find a previous usable window win = curwin; do { - if (win->w_buffer->b_p_bt[0] == NUL) { + if (bt_normal(win->w_buffer)) { break; } if (win->w_prev == NULL) { @@ -2202,7 +2258,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Remember a usable window. if (altwin == NULL && !win->w_p_pvw - && win->w_buffer->b_p_bt[0] == NUL) { + && bt_normal(win->w_buffer)) { altwin = win; } } @@ -4412,7 +4468,7 @@ void ex_vimgrep(exarg_T *eap) goto theend; } - /* Jump to first match. */ + // Jump to first match. if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { if ((flags & VGR_NOJUMP) == 0) { vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf, @@ -4974,15 +5030,86 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) return status; } +// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the +// items in the dict 'd'. +static int qf_add_entry_from_dict( + qf_info_T *qi, + int qf_idx, + const dict_T *d, + bool first_entry) + FUNC_ATTR_NONNULL_ALL +{ + static bool did_bufnr_emsg; + + if (first_entry) { + did_bufnr_emsg = false; + } + + char *const filename = tv_dict_get_string(d, "filename", true); + char *const module = tv_dict_get_string(d, "module", true); + int bufnum = (int)tv_dict_get_number(d, "bufnr"); + const long lnum = (long)tv_dict_get_number(d, "lnum"); + const int col = (int)tv_dict_get_number(d, "col"); + const char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); + const int nr = (int)tv_dict_get_number(d, "nr"); + const char *const type = tv_dict_get_string(d, "type", false); + char *const pattern = tv_dict_get_string(d, "pattern", true); + char *text = tv_dict_get_string(d, "text", true); + if (text == NULL) { + text = xcalloc(1, 1); + } + bool valid = true; + if ((filename == NULL && bufnum == 0) + || (lnum == 0 && pattern == NULL)) { + valid = false; + } + + // Mark entries with non-existing buffer number as not valid. Give the + // error message only once. + if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) { + if (!did_bufnr_emsg) { + did_bufnr_emsg = true; + EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum); + } + valid = false; + bufnum = 0; + } + + // If the 'valid' field is present it overrules the detected value. + if (tv_dict_find(d, "valid", -1) != NULL) { + valid = tv_dict_get_number(d, "valid"); + } + + const int status = qf_add_entry(qi, + qf_idx, + NULL, // dir + (char_u *)filename, + (char_u *)module, + bufnum, + (char_u *)text, + lnum, + col, + vcol, // vis_col + (char_u *)pattern, // search pattern + nr, + (char_u)(type == NULL ? NUL : *type), + valid); + + xfree(filename); + xfree(module); + xfree(pattern); + xfree(text); + + return status; +} + /// Add list of entries to quickfix/location list. Each list entry is /// a dictionary with item information. static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char_u *title, int action) { - dict_T *d; qfline_T *old_last = NULL; int retval = OK; - bool did_bufnr_emsg = false; if (action == ' ' || qf_idx == qi->qf_listcount) { // make place for a new list @@ -5001,68 +5128,13 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, continue; // Skip non-dict items. } - d = TV_LIST_ITEM_TV(li)->vval.v_dict; + const dict_T *const d = TV_LIST_ITEM_TV(li)->vval.v_dict; if (d == NULL) { continue; } - char *const filename = tv_dict_get_string(d, "filename", true); - char *const module = tv_dict_get_string(d, "module", true); - int bufnum = (int)tv_dict_get_number(d, "bufnr"); - long lnum = (long)tv_dict_get_number(d, "lnum"); - int col = (int)tv_dict_get_number(d, "col"); - char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); - int nr = (int)tv_dict_get_number(d, "nr"); - const char *type_str = tv_dict_get_string(d, "type", false); - const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); - char *const pattern = tv_dict_get_string(d, "pattern", true); - char *text = tv_dict_get_string(d, "text", true); - if (text == NULL) { - text = xcalloc(1, 1); - } - bool valid = true; - if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { - valid = false; - } - - /* Mark entries with non-existing buffer number as not valid. Give the - * error message only once. */ - if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) { - if (!did_bufnr_emsg) { - did_bufnr_emsg = TRUE; - EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum); - } - valid = false; - bufnum = 0; - } - - // If the 'valid' field is present it overrules the detected value. - if (tv_dict_find(d, "valid", -1) != NULL) { - valid = (int)tv_dict_get_number(d, "valid"); - } - - int status = qf_add_entry(qi, - qf_idx, - NULL, // dir - (char_u *)filename, - (char_u *)module, - bufnum, - (char_u *)text, - lnum, - col, - vcol, // vis_col - (char_u *)pattern, // search pattern - nr, - type, - valid); - - xfree(filename); - xfree(module); - xfree(pattern); - xfree(text); - - if (status == FAIL) { - retval = FAIL; + retval = qf_add_entry_from_dict(qi, qf_idx, d, li == tv_list_first(list)); + if (retval == FAIL) { break; } }); @@ -5595,7 +5667,7 @@ void ex_cexpr(exarg_T *eap) // Get the location list for ":lhelpgrep" static qf_info_T *hgr_get_ll(bool *new_ll) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { win_T *wp; qf_info_T *qi; @@ -5615,9 +5687,7 @@ static qf_info_T *hgr_get_ll(bool *new_ll) } if (qi == NULL) { // Allocate a new location list for help text matches - if ((qi = ll_new_list()) == NULL) { - return NULL; - } + qi = ll_new_list(); *new_ll = true; } @@ -5713,14 +5783,14 @@ static void hgr_search_files_in_dir( } } -// Search for a pattern in all the help files in the 'runtimepath'. +// Search for a pattern in all the help files in the 'runtimepath' +// and add the matches to a quickfix list. +// 'lang' is the language specifier. If supplied, then only matches in the +// specified language are found. static void hgr_search_in_rtp(qf_info_T *qi, regmatch_T *p_regmatch, - char_u *arg) - FUNC_ATTR_NONNULL_ALL + const char_u *lang) + FUNC_ATTR_NONNULL_ARG(1, 2) { - // Check for a specified language - char_u *const lang = check_help_lang(arg); - // Go through all directories in 'runtimepath' char_u *p = p_rtp; while (*p != NUL && !got_int) { @@ -5755,11 +5825,10 @@ void ex_helpgrep(exarg_T *eap) if (eap->cmdidx == CMD_lhelpgrep) { qi = hgr_get_ll(&new_qi); - if (qi == NULL) { - return; - } } + // Check for a specified language + char_u *const lang = check_help_lang(eap->arg); regmatch_T regmatch = { .regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING), .rm_ic = false, @@ -5768,7 +5837,7 @@ void ex_helpgrep(exarg_T *eap) // Create a new quickfix list. qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep)); - hgr_search_in_rtp(qi, ®match, eap->arg); + hgr_search_in_rtp(qi, ®match, lang); vim_regfree(regmatch.regprog); @@ -5778,11 +5847,12 @@ void ex_helpgrep(exarg_T *eap) qi->qf_lists[qi->qf_curlist].qf_index = 1; } - if (p_cpo == empty_option) + if (p_cpo == empty_option) { p_cpo = save_cpo; - else - /* Darn, some plugin changed the value. */ + } else { + // Darn, some plugin changed the value. free_string_option(save_cpo); + } qf_list_changed(qi, qi->qf_curlist); qf_update_buffer(qi, NULL); @@ -5803,8 +5873,8 @@ void ex_helpgrep(exarg_T *eap) EMSG2(_(e_nomatch2), eap->arg); if (eap->cmdidx == CMD_lhelpgrep) { - /* If the help window is not opened or if it already points to the - * correct location list, then free the new location list. */ + // If the help window is not opened or if it already points to the + // correct location list, then free the new location list. if (!bt_help(curwin->w_buffer) || curwin->w_llist == qi) { if (new_qi) { ll_free_all(&qi); diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 593ce6fcdc..8f5f3f82e7 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -282,6 +282,7 @@ endif " Names of flaky tests. let s:flaky_tests = [ + \ 'Test_autocmd_SafeState()', \ 'Test_cursorhold_insert()', \ 'Test_exit_callback_interval()', \ 'Test_map_timeout_with_timer_interrupt()', diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 532beb9c39..b3e43640bb 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2,12 +2,12 @@ source shared.vim -func! Setup_NewWindow() +func Setup_NewWindow() 10new call setline(1, range(1,100)) endfunc -func! MyFormatExpr() +func MyFormatExpr() " Adds '->$' at lines having numbers followed by trailing whitespace for ln in range(v:lnum, v:lnum+v:count-1) let line = getline(ln) @@ -17,7 +17,7 @@ func! MyFormatExpr() endfor endfunc -func! CountSpaces(type, ...) +func CountSpaces(type, ...) " for testing operatorfunc " will count the number of spaces " and return the result in g:a @@ -37,7 +37,7 @@ func! CountSpaces(type, ...) let @@ = reg_save endfunc -func! OpfuncDummy(type, ...) +func OpfuncDummy(type, ...) " for testing operatorfunc let g:opt=&linebreak @@ -81,7 +81,7 @@ fun! Test_normal00_optrans() bw! endfunc -func! Test_normal01_keymodel() +func Test_normal01_keymodel() call Setup_NewWindow() " Test 1: depending on 'keymodel' <s-down> does something different 50 @@ -115,7 +115,7 @@ func! Test_normal01_keymodel() bw! endfunc -func! Test_normal02_selectmode() +func Test_normal02_selectmode() " some basic select mode tests call Setup_NewWindow() 50 @@ -129,7 +129,7 @@ func! Test_normal02_selectmode() bw! endfunc -func! Test_normal02_selectmode2() +func Test_normal02_selectmode2() " some basic select mode tests call Setup_NewWindow() 50 @@ -139,7 +139,7 @@ func! Test_normal02_selectmode2() bw! endfunc -func! Test_normal03_join() +func Test_normal03_join() " basic join test call Setup_NewWindow() 50 @@ -159,7 +159,7 @@ func! Test_normal03_join() bw! endfunc -func! Test_normal04_filter() +func Test_normal04_filter() " basic filter test " only test on non windows platform if has('win32') @@ -185,7 +185,7 @@ func! Test_normal04_filter() bw! endfunc -func! Test_normal05_formatexpr() +func Test_normal05_formatexpr() " basic formatexpr test call Setup_NewWindow() %d_ @@ -222,7 +222,7 @@ func Test_normal05_formatexpr_setopt() set formatexpr= endfunc -func! Test_normal06_formatprg() +func Test_normal06_formatprg() " basic test for formatprg " only test on non windows platform if has('win32') @@ -256,7 +256,7 @@ func! Test_normal06_formatprg() call delete('Xsed_format.sh') endfunc -func! Test_normal07_internalfmt() +func Test_normal07_internalfmt() " basic test for internal formmatter to textwidth of 12 let list=range(1,11) call map(list, 'v:val." "') @@ -270,7 +270,7 @@ func! Test_normal07_internalfmt() bw! endfunc -func! Test_normal08_fold() +func Test_normal08_fold() " basic tests for foldopen/folddelete if !has("folding") return @@ -329,7 +329,7 @@ func! Test_normal08_fold() bw! endfunc -func! Test_normal09_operatorfunc() +func Test_normal09_operatorfunc() " Test operatorfunc call Setup_NewWindow() " Add some spaces for counting @@ -359,7 +359,7 @@ func! Test_normal09_operatorfunc() bw! endfunc -func! Test_normal09a_operatorfunc() +func Test_normal09a_operatorfunc() " Test operatorfunc call Setup_NewWindow() " Add some spaces for counting @@ -385,7 +385,7 @@ func! Test_normal09a_operatorfunc() unlet! g:opt endfunc -func! Test_normal10_expand() +func Test_normal10_expand() " Test for expand() 10new call setline(1, ['1', 'ifooar,,cbar']) @@ -420,7 +420,7 @@ func! Test_normal10_expand() bw! endfunc -func! Test_normal11_showcmd() +func Test_normal11_showcmd() " test for 'showcmd' 10new exe "norm! ofoobar\<esc>" @@ -435,7 +435,7 @@ func! Test_normal11_showcmd() bw! endfunc -func! Test_normal12_nv_error() +func Test_normal12_nv_error() " Test for nv_error 10new call setline(1, range(1,5)) @@ -445,7 +445,7 @@ func! Test_normal12_nv_error() bw! endfunc -func! Test_normal13_help() +func Test_normal13_help() " Test for F1 call assert_equal(1, winnr()) call feedkeys("\<f1>", 'txi') @@ -454,7 +454,7 @@ func! Test_normal13_help() bw! endfunc -func! Test_normal14_page() +func Test_normal14_page() " basic test for Ctrl-F and Ctrl-B call Setup_NewWindow() exe "norm! \<c-f>" @@ -488,7 +488,7 @@ func! Test_normal14_page() bw! endfunc -func! Test_normal14_page_eol() +func Test_normal14_page_eol() 10new norm oxxxxxxx exe "norm 2\<c-f>" @@ -497,7 +497,7 @@ func! Test_normal14_page_eol() bw! endfunc -func! Test_normal15_z_scroll_vert() +func Test_normal15_z_scroll_vert() " basic test for z commands that scroll the window call Setup_NewWindow() 100 @@ -586,7 +586,7 @@ func! Test_normal15_z_scroll_vert() bw! endfunc -func! Test_normal16_z_scroll_hor() +func Test_normal16_z_scroll_hor() " basic test for z commands that scroll the window 10new 15vsp @@ -652,7 +652,7 @@ func! Test_normal16_z_scroll_hor() bw! endfunc -func! Test_normal17_z_scroll_hor2() +func Test_normal17_z_scroll_hor2() " basic test for z commands that scroll the window " using 'sidescrolloff' setting 10new @@ -719,7 +719,7 @@ func! Test_normal17_z_scroll_hor2() bw! endfunc -func! Test_normal18_z_fold() +func Test_normal18_z_fold() " basic tests for foldopen/folddelete if !has("folding") return @@ -1090,7 +1090,7 @@ func! Test_normal18_z_fold() bw! endfunc -func! Test_normal19_z_spell() +func Test_normal19_z_spell() if !has("spell") || !has('syntax') return endif @@ -1245,7 +1245,7 @@ func! Test_normal19_z_spell() bw! endfunc -func! Test_normal20_exmode() +func Test_normal20_exmode() if !has("unix") " Reading from redirected file doesn't work on MS-Windows return @@ -1263,24 +1263,38 @@ func! Test_normal20_exmode() bw! endfunc -func! Test_normal21_nv_hat() - set hidden - new - " to many buffers opened already, will not work - "call assert_fails(":b#", 'E23') - "call assert_equal('', @#) - e Xfoobar - e Xfile2 - call feedkeys("\<c-^>", 't') - call assert_equal("Xfile2", fnamemodify(bufname('%'), ':t')) - call feedkeys("f\<c-^>", 't') - call assert_equal("Xfile2", fnamemodify(bufname('%'), ':t')) - " clean up - set nohidden - bw! +func Test_normal21_nv_hat() + + " Edit a fresh file and wipe the buffer list so that there is no alternate + " file present. Next, check for the expected command failures. + edit Xfoo | %bw + call assert_fails(':buffer #', 'E86') + call assert_fails(':execute "normal! \<C-^>"', 'E23') + + " Test for the expected behavior when switching between two named buffers. + edit Xfoo | edit Xbar + call feedkeys("\<C-^>", 'tx') + call assert_equal('Xfoo', fnamemodify(bufname('%'), ':t')) + call feedkeys("\<C-^>", 'tx') + call assert_equal('Xbar', fnamemodify(bufname('%'), ':t')) + + " Test for the expected behavior when only one buffer is named. + enew | let l:nr = bufnr('%') + call feedkeys("\<C-^>", 'tx') + call assert_equal('Xbar', fnamemodify(bufname('%'), ':t')) + call feedkeys("\<C-^>", 'tx') + call assert_equal('', bufname('%')) + call assert_equal(l:nr, bufnr('%')) + + " Test that no action is taken by "<C-^>" when an operator is pending. + edit Xfoo + call feedkeys("ci\<C-^>", 'tx') + call assert_equal('Xfoo', fnamemodify(bufname('%'), ':t')) + + %bw! endfunc -func! Test_normal22_zet() +func Test_normal22_zet() " Test for ZZ " let shell = &shell " let &shell = 'sh' @@ -1308,7 +1322,7 @@ func! Test_normal22_zet() " let &shell = shell endfunc -func! Test_normal23_K() +func Test_normal23_K() " Test for K command new call append(0, ['helphelp.txt', 'man', 'aa%bb', 'cc|dd']) @@ -1373,7 +1387,7 @@ func! Test_normal23_K() bw! endfunc -func! Test_normal24_rot13() +func Test_normal24_rot13() " Testing for g?? g?g? new call append(0, 'abcdefghijklmnopqrstuvwxyzäüö') @@ -1387,7 +1401,7 @@ func! Test_normal24_rot13() bw! endfunc -func! Test_normal25_tag() +func Test_normal25_tag() " Testing for CTRL-] g CTRL-] g] " CTRL-W g] CTRL-W CTRL-] CTRL-W g CTRL-] h @@ -1454,7 +1468,7 @@ func! Test_normal25_tag() helpclose endfunc -func! Test_normal26_put() +func Test_normal26_put() " Test for ]p ]P [p and [P new call append(0, ['while read LINE', 'do', ' ((count++))', ' if [ $? -ne 0 ]; then', " echo 'Error writing file'", ' fi', 'done']) @@ -1473,7 +1487,7 @@ func! Test_normal26_put() bw! endfunc -func! Test_normal27_bracket() +func Test_normal27_bracket() " Test for [' [` ]' ]` call Setup_NewWindow() 1,21s/.\+/ & b/ @@ -1524,7 +1538,7 @@ func! Test_normal27_bracket() bw! endfunc -func! Test_normal28_parenthesis() +func Test_normal28_parenthesis() " basic testing for ( and ) new call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here']) @@ -1718,7 +1732,7 @@ fun! Test_normal31_r_cmd() bw! endfunc -func! Test_normal32_g_cmd1() +func Test_normal32_g_cmd1() " Test for g*, g# new call append(0, ['abc.x_foo', 'x_foobar.abc']) @@ -1849,7 +1863,7 @@ fun! Test_normal33_g_cmd2() bw! endfunc -func! Test_g_ctrl_g() +func Test_g_ctrl_g() new let a = execute(":norm! g\<c-g>") @@ -2139,7 +2153,7 @@ fun! Test_normal41_insert_reg() bw! endfunc -func! Test_normal42_halfpage() +func Test_normal42_halfpage() " basic test for Ctrl-D and Ctrl-U call Setup_NewWindow() call assert_equal(5, &scroll) @@ -2207,7 +2221,7 @@ fun! Test_normal43_textobject1() bw! endfunc -func! Test_normal44_textobjects2() +func Test_normal44_textobjects2() " basic testing for is and as text objects new call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here']) @@ -2262,7 +2276,7 @@ func! Test_normal44_textobjects2() bw! endfunc -func! Test_normal45_drop() +func Test_normal45_drop() if !has('dnd') " The ~ register does not exist call assert_beeps('norm! "~') @@ -2280,7 +2294,7 @@ func! Test_normal45_drop() bw! endfunc -func! Test_normal46_ignore() +func Test_normal46_ignore() new " How to test this? " let's just for now test, that the buffer @@ -2299,7 +2313,7 @@ func! Test_normal46_ignore() bw! endfunc -func! Test_normal47_visual_buf_wipe() +func Test_normal47_visual_buf_wipe() " This was causing a crash or ml_get error. enew! call setline(1,'xxx') @@ -2313,7 +2327,7 @@ func! Test_normal47_visual_buf_wipe() set nomodified endfunc -func! Test_normal47_autocmd() +func Test_normal47_autocmd() " disabled, does not seem to be possible currently throw "Skipped: not possible to test cursorhold autocmd while waiting for input in normal_cmd" new @@ -2331,14 +2345,14 @@ func! Test_normal47_autocmd() bw! endfunc -func! Test_normal48_wincmd() +func Test_normal48_wincmd() new exe "norm! \<c-w>c" call assert_equal(1, winnr('$')) call assert_fails(":norm! \<c-w>c", "E444") endfunc -func! Test_normal49_counts() +func Test_normal49_counts() new call setline(1, 'one two three four five six seven eight nine ten') 1 @@ -2347,7 +2361,7 @@ func! Test_normal49_counts() bw! endfunc -func! Test_normal50_commandline() +func Test_normal50_commandline() if !has("timers") || !has("cmdline_hist") || !has("vertsplit") return endif @@ -2378,7 +2392,7 @@ func! Test_normal50_commandline() bw! endfunc -func! Test_normal51_FileChangedRO() +func Test_normal51_FileChangedRO() if !has("autocmd") return endif @@ -2395,7 +2409,7 @@ func! Test_normal51_FileChangedRO() call delete("Xreadonly.log") endfunc -func! Test_normal52_rl() +func Test_normal52_rl() if !has("rightleft") return endif @@ -2428,7 +2442,7 @@ func! Test_normal52_rl() bw! endfunc -func! Test_normal53_digraph() +func Test_normal53_digraph() if !has('digraphs') return endif @@ -2516,6 +2530,29 @@ func Test_changelist() let &ul = save_ul endfunc +func Test_nv_hat_count() + %bwipeout! + let l:nr = bufnr('%') + 1 + call assert_fails(':execute "normal! ' . l:nr . '\<C-^>"', 'E92') + + edit Xfoo + let l:foo_nr = bufnr('Xfoo') + + edit Xbar + let l:bar_nr = bufnr('Xbar') + + " Make sure we are not just using the alternate file. + edit Xbaz + + call feedkeys(l:foo_nr . "\<C-^>", 'tx') + call assert_equal('Xfoo', fnamemodify(bufname('%'), ':t')) + + call feedkeys(l:bar_nr . "\<C-^>", 'tx') + call assert_equal('Xbar', fnamemodify(bufname('%'), ':t')) + + %bwipeout! +endfunc + func Test_delete_until_paragraph() new normal grádv} @@ -2617,3 +2654,80 @@ Piece of Java close! endfunc + +fun! Test_normal_gdollar_cmd() + if !has("jumplist") + return + endif + " Tests for g cmds + call Setup_NewWindow() + " Make long lines that will wrap + %s/$/\=repeat(' foobar', 10)/ + 20vsp + set wrap + " Test for g$ with count + norm! gg + norm! 0vg$y + call assert_equal(20, col("'>")) + call assert_equal('1 foobar foobar foob', getreg(0)) + norm! gg + norm! 0v4g$y + call assert_equal(72, col("'>")) + call assert_equal('1 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.."\n", getreg(0)) + norm! gg + norm! 0v6g$y + call assert_equal(40, col("'>")) + call assert_equal('1 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '2 foobar foobar foobar foobar foobar foo', getreg(0)) + set nowrap + " clean up + norm! gg + norm! 0vg$y + call assert_equal(20, col("'>")) + call assert_equal('1 foobar foobar foob', getreg(0)) + norm! gg + norm! 0v4g$y + call assert_equal(20, col("'>")) + call assert_equal('1 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '2 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '3 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '4 foobar foobar foob', getreg(0)) + norm! gg + norm! 0v6g$y + call assert_equal(20, col("'>")) + call assert_equal('1 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '2 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '3 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '4 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '5 foobar foobar foobar foobar foobar foobar foobar foobar foobar foobar'.. "\n".. + \ '6 foobar foobar foob', getreg(0)) + " Move to last line, also down movement is not possible, should still move + " the cursor to the last visible char + norm! G + norm! 0v6g$y + call assert_equal(20, col("'>")) + call assert_equal('100 foobar foobar fo', getreg(0)) + bw! +endfunc + +func Test_normal_gk() + " needs 80 column new window + new + vert 80new + put =[repeat('x',90)..' {{{1', 'x {{{1'] + norm! gk + " In a 80 column wide terminal the window will be only 78 char + " (because Vim will leave space for the other window), + " but if the terminal is larger, it will be 80 chars, so verify the + " cursor column correctly. + call assert_equal(winwidth(0)+1, col('.')) + call assert_equal(winwidth(0)+1, virtcol('.')) + norm! j + call assert_equal(6, col('.')) + call assert_equal(6, virtcol('.')) + norm! gk + call assert_equal(95, col('.')) + call assert_equal(95, virtcol('.')) + bw! + bw! +endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 83ef3c2fce..597be0aa89 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -3315,6 +3315,20 @@ func Test_qfjump() call Xqfjump_tests('l') endfunc +" Test helpgrep with lang specifier +func Xtest_helpgrep_with_lang_specifier(cchar) + call s:setup_commands(a:cchar) + Xhelpgrep Vim@en + call assert_equal('help', &filetype) + call assert_notequal(0, g:Xgetlist({'nr' : '$'}).nr) + new | only +endfunc + +func Test_helpgrep_with_lang_specifier() + call Xtest_helpgrep_with_lang_specifier('c') + call Xtest_helpgrep_with_lang_specifier('l') +endfunc + " The following test used to crash Vim. " Open the location list window and close the regular window associated with " the location list. When the garbage collection runs now, it incorrectly diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index c87c0a0af4..43c1f06c44 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -117,15 +117,71 @@ func Test_window_vertical_split() bw endfunc +" Test the ":wincmd ^" and "<C-W>^" commands. func Test_window_split_edit_alternate() - e Xa - e Xb + " Test for failure when the alternate buffer/file no longer exists. + edit Xfoo | %bw + call assert_fails(':wincmd ^', 'E23') + + " Test for the expected behavior when we have two named buffers. + edit Xfoo | edit Xbar wincmd ^ - call assert_equal('Xa', bufname(winbufnr(1))) - call assert_equal('Xb', bufname(winbufnr(2))) + call assert_equal('Xfoo', bufname(winbufnr(1))) + call assert_equal('Xbar', bufname(winbufnr(2))) + only - bw Xa Xb + " Test for the expected behavior when the alternate buffer is not named. + enew | let l:nr1 = bufnr('%') + edit Xfoo | let l:nr2 = bufnr('%') + wincmd ^ + call assert_equal(l:nr1, winbufnr(1)) + call assert_equal(l:nr2, winbufnr(2)) + only + + " FIXME: this currently fails on AppVeyor, but passes locally + if !has('win32') + " Test the Normal mode command. + call feedkeys("\<C-W>\<C-^>", 'tx') + call assert_equal(l:nr2, winbufnr(1)) + call assert_equal(l:nr1, winbufnr(2)) + endif + + %bw! +endfunc + +" Test the ":[count]wincmd ^" and "[count]<C-W>^" commands. +func Test_window_split_edit_bufnr() + + %bwipeout + let l:nr = bufnr('%') + 1 + call assert_fails(':execute "normal! ' . l:nr . '\<C-W>\<C-^>"', 'E92') + call assert_fails(':' . l:nr . 'wincmd ^', 'E16') + call assert_fails(':0wincmd ^', 'E16') + + edit Xfoo | edit Xbar | edit Xbaz + let l:foo_nr = bufnr('Xfoo') + let l:bar_nr = bufnr('Xbar') + let l:baz_nr = bufnr('Xbaz') + + " FIXME: this currently fails on AppVeyor, but passes locally + if !has('win32') + call feedkeys(l:foo_nr . "\<C-W>\<C-^>", 'tx') + call assert_equal('Xfoo', bufname(winbufnr(1))) + call assert_equal('Xbaz', bufname(winbufnr(2))) + only + + call feedkeys(l:bar_nr . "\<C-W>\<C-^>", 'tx') + call assert_equal('Xbar', bufname(winbufnr(1))) + call assert_equal('Xfoo', bufname(winbufnr(2))) + only + + execute l:baz_nr . 'wincmd ^' + call assert_equal('Xbaz', bufname(winbufnr(1))) + call assert_equal('Xbar', bufname(winbufnr(2))) + endif + + %bw! endfunc func Test_window_preview() @@ -700,6 +756,42 @@ func Test_relative_cursor_second_line_after_resize() let &so = so_save endfunc +func Test_split_noscroll() + let so_save = &so + enew + call setline(1, range(1, 8)) + normal 100% + split + + 1wincmd w + let winid1 = win_getid() + let info1 = getwininfo(winid1)[0] + + 2wincmd w + let winid2 = win_getid() + let info2 = getwininfo(winid2)[0] + + call assert_equal(1, info1.topline) + call assert_equal(1, info2.topline) + + " window that fits all lines by itself, but not when split: closing other + " window should restore fraction. + only! + call setline(1, range(1, &lines - 10)) + exe &lines / 4 + let winid1 = win_getid() + let info1 = getwininfo(winid1)[0] + call assert_equal(1, info1.topline) + new + redraw + close + let info1 = getwininfo(winid1)[0] + call assert_equal(1, info1.topline) + + bwipe! + let &so = so_save +endfunc + " Tests for the winnr() function func Test_winnr() only | tabonly diff --git a/src/nvim/window.c b/src/nvim/window.c index 1e6de73549..4d8eaa9dcc 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -131,8 +131,20 @@ do_window ( case '^': CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - cmd_with_count("split #", (char_u *)cbuf, sizeof(cbuf), Prenum); - do_cmdline_cmd(cbuf); + + if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : Prenum) == NULL) { + if (Prenum == 0) { + EMSG(_(e_noalt)); + } else { + EMSGN(_("E92: Buffer %" PRId64 " not found"), Prenum); + } + break; + } + + if (!curbuf_locked() && win_split(0, 0) == OK) { + (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, + (linenr_T)0, GETF_ALT, false); + } break; /* open new window */ @@ -5598,10 +5610,14 @@ void scroll_to_fraction(win_T *wp, int prev_height) int sline, line_size; int height = wp->w_height_inner; - // Don't change w_topline when height is zero. Don't set w_topline when - // 'scrollbind' is set and this isn't the current window. + // Don't change w_topline in any of these cases: + // - window height is 0 + // - 'scrollbind' is set and this isn't the current window + // - window height is sufficient to display the whole buffer and first line + // is visible. if (height > 0 && (!wp->w_p_scb || wp == curwin) + && (height < wp->w_buffer->b_ml.ml_line_count || wp->w_topline > 1) ) { /* * Find a value for w_topline that shows the cursor at the same diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 351c4b4bcf..e9a7c8c2df 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -736,9 +736,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | {12:tw}o lines | + Inc substitution on | + {12:tw}o lines | | - {15:~ }| - {15:~ }| {11:[No Name] }| |2| {12:tw}o lines | |4| {12:tw}o lines | @@ -793,9 +793,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | {12:tw}o lines | + Inc substitution on | + {12:tw}o lines | | - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| {12:tw}o lines | |4| {12:tw}o lines | @@ -814,9 +814,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | o lines | + Inc substitution on | + o lines | | - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| o lines | |4| o lines | @@ -833,9 +833,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | {12:x}o lines | + Inc substitution on | + {12:x}o lines | | - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| {12:x}o lines | |4| {12:x}o lines | @@ -852,9 +852,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | o lines | + Inc substitution on | + o lines | | - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| o lines | |4| o lines | @@ -874,9 +874,9 @@ describe(":substitute, inccommand=split", function() screen:expect([[ Inc substitution on | {12:XX}o lines | + Inc substitution on | + {12:XX}o lines | | - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| {12:XX}o lines | |4| {12:XX}o lines | @@ -938,11 +938,11 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw") -- 'cursorline' is NOT active during preview. screen:expect([[ + Inc substitution on | {12:tw}o lines | Inc substitution on | {12:tw}o lines | | - {15:~ }| {11:[No Name] [+] }| |2| {12:tw}o lines | |4| {12:tw}o lines | @@ -2205,11 +2205,11 @@ describe(":substitute", function() feed("/KKK") screen:expect([[ + T T123 T T123 T2T TT T23423424| + x | afa {12:KKK}adf la;lkd {12:KKK}alx | | {15:~ }| - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |3| afa {12:KKK}adf la;lkd {12:KKK}alx | {15:~ }| @@ -2485,11 +2485,11 @@ describe(":substitute", function() wait() feed([[:%s/\(some\)\@<lt>!thing/one/]]) screen:expect([[ + something | every{12:one} | someone | {15:~ }| {15:~ }| - {15:~ }| {11:[No Name] [+] }| |2| every{12:one} | {15:~ }| @@ -2527,11 +2527,11 @@ describe(":substitute", function() wait() feed([[:%s/some\(thing\)\@!/every/]]) screen:expect([[ + something | + everything | {12:every}one | {15:~ }| {15:~ }| - {15:~ }| - {15:~ }| {11:[No Name] [+] }| |3| {12:every}one | {15:~ }| diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 3bd6b81ff1..440bae58e0 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -419,9 +419,9 @@ describe('ui/mouse/input', function() meths.set_option('showtabline', 2) screen:expect([[ {fill:test-test2 }| + testing | mouse | support and selectio^n | - {0:~ }| | ]]) meths.set_var('reply', {}) @@ -539,9 +539,9 @@ describe('ui/mouse/input', function() feed_command('tabprevious') -- go to first tab screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| + testing | mouse | support and selectio^n | - {0:~ }| :tabprevious | ]]) feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 37eb550835..fabcc05ce6 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1491,20 +1491,20 @@ describe('builtin popupmenu', function() command("split") screen:expect([[ + xx | choice^ | - {1:~ }| {n:word }{1: }| {s:choice }{4: }| {n:text } | - {n:thing }{1: }| + {n:thing } | {3:[No Name] [+] }| {2:-- INSERT --} | ]]) meths.input_mouse('wheel', 'down', '', 0, 6, 15) screen:expect{grid=[[ + xx | choice^ | - {1:~ }| {n:word }{1: }| {s:choice }{4: }| {n:text } | |