aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ex_cmds.c12
-rw-r--r--src/nvim/ex_cmds.lua29
-rw-r--r--src/nvim/ex_cmds2.c44
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c48
-rw-r--r--src/nvim/if_cscope.c33
-rw-r--r--src/nvim/os/win_defs.h7
-rw-r--r--src/nvim/quickfix.c220
-rw-r--r--src/nvim/testdir/Makefile1
-rw-r--r--src/nvim/testdir/test_breakindent.in2
-rw-r--r--src/nvim/testdir/test_cdo.in107
-rw-r--r--src/nvim/testdir/test_cdo.ok66
-rw-r--r--src/nvim/testdir/test_eval.in33
-rw-r--r--src/nvim/testdir/test_eval.okbin11106 -> 10814 bytes
-rw-r--r--src/nvim/version.c10
15 files changed, 539 insertions, 74 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index d902234ef7..29cff5a2b2 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1989,7 +1989,7 @@ do_ecmd (
char_u *free_fname = NULL;
int retval = FAIL;
long n;
- linenr_T lnum;
+ pos_T orig_pos;
linenr_T topline = 0;
int newcol = -1;
int solcol = -1;
@@ -2351,7 +2351,7 @@ do_ecmd (
* Careful: open_buffer() and apply_autocmds() may change the current
* buffer and window.
*/
- lnum = curwin->w_cursor.lnum;
+ orig_pos = curwin->w_cursor;
topline = curwin->w_topline;
if (!oldbuf) { /* need to read the file */
swap_exists_action = SEA_DIALOG;
@@ -2379,11 +2379,9 @@ do_ecmd (
}
check_arg_idx(curwin);
- /*
- * If autocommands change the cursor position or topline, we should
- * keep it.
- */
- if (curwin->w_cursor.lnum != lnum) {
+ // If autocommands change the cursor position or topline, we should keep
+ // it. Also when it moves within a line.
+ if (!equalpos(curwin->w_cursor, orig_pos)) {
newlnum = curwin->w_cursor.lnum;
newcol = curwin->w_cursor.col;
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index b7a3505c99..702abf3cf0 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -34,6 +34,7 @@ local ADDR_ARGUMENTS = 2
local ADDR_LOADED_BUFFERS = 3
local ADDR_BUFFERS = 4
local ADDR_TABS = 5
+local ADDR_QUICKFIX = 6
-- The following table is described in ex_cmds_defs.h file.
return {
@@ -374,6 +375,12 @@ return {
func='ex_cd',
},
{
+ command='cdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
+ {
command='center',
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
addr_type=ADDR_LINES,
@@ -391,6 +398,14 @@ return {
addr_type=ADDR_LINES,
func='ex_cfile',
},
+ -- Even though 'cfdo' is alphabetically lower than 'cfile', it is after
+ -- 'cfile' in this cmd list to support the existing ":cf" abbreviation.
+ {
+ command='cfdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
{
command='cfirst',
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
@@ -1286,6 +1301,12 @@ return {
func='do_cscope',
},
{
+ command='ldo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
+ {
command='left',
flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
addr_type=ADDR_LINES,
@@ -1315,6 +1336,14 @@ return {
addr_type=ADDR_LINES,
func='ex_cfile',
},
+ -- Even though 'lfdo' is alphabetically lower than 'lfile', it is after
+ -- 'lfile' in this cmd list to support the existing ":lf" abbreviation.
+ {
+ command='lfdo',
+ flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL),
+ addr_type=ADDR_QUICKFIX,
+ func='ex_listdo',
+ },
{
command='lfirst',
flags=bit.bor(RANGE, NOTADR, COUNT, TRLBAR, BANG),
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 87a6283310..57153cf5a1 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1868,7 +1868,7 @@ void ex_argdelete(exarg_T *eap)
}
/*
- * ":argdo", ":windo", ":bufdo", ":tabdo"
+ * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
*/
void ex_listdo(exarg_T *eap)
{
@@ -1879,7 +1879,6 @@ void ex_listdo(exarg_T *eap)
char_u *save_ei = NULL;
char_u *p_shm_save;
-
if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
/* Don't do syntax HL autocommands. Skipping the syntax file is a
* great speed improvement. */
@@ -1914,7 +1913,10 @@ void ex_listdo(exarg_T *eap)
default:
break;
}
+
buf_T *buf = curbuf;
+ size_t qf_size = 0;
+
/* set pcmark now */
if (eap->cmdidx == CMD_bufdo) {
/* Advance to the first listed buffer after "eap->line1". */
@@ -1929,6 +1931,22 @@ void ex_listdo(exarg_T *eap)
if (buf != NULL) {
goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
}
+ } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ qf_size = qf_get_size(eap);
+ assert(eap->line1 >= 0);
+ if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
+ buf = NULL;
+ } else {
+ ex_cc(eap);
+
+ buf = curbuf;
+ i = eap->line1 - 1;
+ if (eap->addr_count <= 0) {
+ // Default to all quickfix/location list entries.
+ eap->line2 = qf_size;
+ }
+ }
} else {
setpcmark();
}
@@ -2009,9 +2027,27 @@ void ex_listdo(exarg_T *eap)
set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
- /* If autocommands took us elsewhere, quit here */
- if (curbuf->b_fnum != next_fnum)
+ // If autocommands took us elsewhere, quit here.
+ if (curbuf->b_fnum != next_fnum) {
+ break;
+ }
+ }
+
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ assert(i >= 0);
+ if ((size_t)i >= qf_size || i >= eap->line2) {
break;
+ }
+
+ size_t qf_idx = qf_get_cur_idx(eap);
+
+ ex_cnext(eap);
+
+ // If jumping to the next quickfix entry fails, quit here.
+ if (qf_get_cur_idx(eap) == qf_idx) {
+ break;
+ }
}
if (eap->cmdidx == CMD_windo) {
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 10d2eb688e..f46d1e6d47 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -72,6 +72,7 @@
#define ADDR_LOADED_BUFFERS 3
#define ADDR_BUFFERS 4
#define ADDR_TABS 5
+#define ADDR_QUICKFIX 6
typedef struct exarg exarg_T;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 59bda9345e..fad497928c 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1531,9 +1531,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
lnum = CURRENT_TAB_NR;
ea.line2 = lnum;
break;
+ case ADDR_QUICKFIX:
+ ea.line2 = qf_get_cur_valid_idx(&ea);
+ break;
}
ea.cmd = skipwhite(ea.cmd);
- lnum = get_address(&ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
+ lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
if (ea.cmd == NULL) /* error detected */
goto doend;
if (lnum == MAXLNUM) {
@@ -1582,6 +1585,13 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 = ARGCOUNT;
}
break;
+ case ADDR_QUICKFIX:
+ ea.line1 = 1;
+ ea.line2 = qf_get_size(&ea);
+ if (ea.line2 == 0) {
+ ea.line2 = 1;
+ }
+ break;
}
++ea.addr_count;
}
@@ -1962,6 +1972,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
ea.line2 = ARGCOUNT;
}
break;
+ case ADDR_QUICKFIX:
+ ea.line2 = qf_get_size(&ea);
+ if (ea.line2 == 0) {
+ ea.line2 = 1;
+ }
+ break;
}
}
@@ -2945,6 +2961,8 @@ set_one_cmd_context (
case CMD_botright:
case CMD_browse:
case CMD_bufdo:
+ case CMD_cdo:
+ case CMD_cfdo:
case CMD_confirm:
case CMD_debug:
case CMD_folddoclosed:
@@ -2954,7 +2972,9 @@ set_one_cmd_context (
case CMD_keepjumps:
case CMD_keepmarks:
case CMD_keeppatterns:
+ case CMD_ldo:
case CMD_leftabove:
+ case CMD_lfdo:
case CMD_lockmarks:
case CMD_noautocmd:
case CMD_noswapfile:
@@ -3367,7 +3387,8 @@ skip_range (
*
* Return MAXLNUM when no Ex address was found.
*/
-static linenr_T get_address(char_u **ptr,
+static linenr_T get_address(exarg_T *eap,
+ char_u **ptr,
int addr_type, // flag: one of ADDR_LINES, ...
int skip, // only skip the address, don't use it
int to_other_file // flag: may jump to other file
@@ -3405,6 +3426,9 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = CURRENT_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_cur_valid_idx(eap);
+ break;
}
break;
@@ -3436,6 +3460,12 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = LAST_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_size(eap);
+ if (lnum == 0) {
+ lnum = 1;
+ }
+ break;
}
break;
@@ -3578,6 +3608,9 @@ static linenr_T get_address(char_u **ptr,
case ADDR_TABS:
lnum = CURRENT_TAB_NR;
break;
+ case ADDR_QUICKFIX:
+ lnum = qf_get_cur_valid_idx(eap);
+ break;
}
}
@@ -3702,6 +3735,12 @@ static char_u *invalid_range(exarg_T *eap)
return (char_u *)_(e_invrange);
}
break;
+ case ADDR_QUICKFIX:
+ assert(eap->line2 >= 0);
+ if (eap->line2 != 1 && (size_t)eap->line2 > qf_get_size(eap)) {
+ return (char_u *)_(e_invrange);
+ }
+ break;
}
}
return NULL;
@@ -4589,6 +4628,7 @@ static struct {
{ADDR_TABS, "tabs"},
{ADDR_BUFFERS, "buffers"},
{ADDR_WINDOWS, "windows"},
+ {ADDR_QUICKFIX, "quickfix"},
{-1, NULL}
};
@@ -7013,9 +7053,7 @@ static void ex_put(exarg_T *eap)
*/
static void ex_copymove(exarg_T *eap)
{
- long n;
-
- n = get_address(&eap->arg, eap->addr_type, FALSE, FALSE);
+ long n = get_address(eap, &eap->arg, eap->addr_type, false, false);
if (eap->arg == NULL) { /* error detected */
eap->nextcmd = NULL;
return;
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 3585c26ca2..64323896d6 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -861,17 +861,16 @@ err_closing:
csinfo[i].hProc = pi.hProcess;
CloseHandle(pi.hThread);
- /* TODO - tidy up after failure to create files on pipe handles. */
- if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdin_wr,
- _O_TEXT|_O_APPEND)) < 0)
- || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL))
+ // TODO(neovim): tidy up after failure to create files on pipe handles.
+ if (((fd = _open_osfhandle((intptr_t)stdin_wr, _O_TEXT|_O_APPEND)) < 0)
+ || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL)) {
PERROR(_("cs_create_connection: fdopen for to_fp failed"));
- if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdout_rd,
- _O_TEXT|_O_RDONLY)) < 0)
- || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL))
+ }
+ if (((fd = _open_osfhandle((intptr_t)stdout_rd, _O_TEXT|_O_RDONLY)) < 0)
+ || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL)) {
PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
-
- /* Close handles for file descriptors inherited by the cscope process */
+ }
+ // Close handles for file descriptors inherited by the cscope process.
CloseHandle(stdin_rd);
CloseHandle(stdout_wr);
@@ -1146,22 +1145,6 @@ static void clear_csinfo(size_t i)
csinfo[i].to_fp = NULL;
}
-#ifndef UNIX
-static char *GetWin32Error(void)
-{
- char *msg = NULL;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, GetLastError(), 0, (LPSTR)&msg, 0, NULL);
- if (msg != NULL) {
- /* remove trailing \r\n */
- char *pcrlf = strstr(msg, "\r\n");
- if (pcrlf != NULL)
- *pcrlf = '\0';
- }
- return msg;
-}
-#endif
-
/*
* PRIVATE: cs_insert_filelist
*
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index aad9672ba7..9f9e5e277c 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -3,6 +3,7 @@
#include <windows.h>
#include <sys/stat.h>
+#include <stdio.h>
#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""}
#define TEMP_FILE_PATH_MAXLEN _MAX_PATH
@@ -18,6 +19,12 @@
# ifndef restrict
# define restrict __restrict
# endif
+# ifndef STDOUT_FILENO
+# define STDOUT_FILENO _fileno(stdout)
+# endif
+# ifndef STDERR_FILENO
+# define STDERR_FILENO _fileno(stderr)
+# endif
# ifndef S_IXUSR
# define S_IXUSR S_IEXEC
# endif
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 8e6ae46a3b..3abf43cb8c 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -1202,7 +1202,7 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr)
/*
* Check in which directory of the directory stack the given file can be
* found.
- * Returns a pointer to the directory name or NULL if not found
+ * Returns a pointer to the directory name or NULL if not found.
* Cleans up intermediate directory entries.
*
* TODO: How to solve the following problem?
@@ -2571,9 +2571,159 @@ static char_u *get_mef_name(void)
return name;
}
+/// Returns the number of valid entries in the current quickfix/location list.
+size_t qf_get_size(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 0;
+ }
+ }
+
+ int prev_fnum = 0;
+ size_t sz = 0;
+ qfline_T *qfp;
+ size_t i;
+ assert(qi->qf_lists[qi->qf_curlist].qf_count >= 0);
+ for (i = 0, qfp = qi->qf_lists[qi->qf_curlist].qf_start;
+ i < (size_t)qi->qf_lists[qi->qf_curlist].qf_count && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (!qfp->qf_valid) {
+ continue;
+ }
+
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo) {
+ // Count all valid entries.
+ sz++;
+ } else if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ sz++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ }
+
+ return sz;
+}
+
+/// Returns the current index of the quickfix/location list.
+/// Returns 0 if there is an error.
+size_t qf_get_cur_idx(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 0;
+ }
+ }
+
+ assert(qi->qf_lists[qi->qf_curlist].qf_index >= 0);
+ return (size_t)qi->qf_lists[qi->qf_curlist].qf_index;
+}
+
+/// Returns the current index in the quickfix/location list,
+/// counting only valid entries.
+/// Returns 1 if there are no valid entries.
+int qf_get_cur_valid_idx(exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_info_T *qi = &ql_info;
+
+ if (eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) {
+ // Location list.
+ qi = GET_LOC_LIST(curwin);
+ if (qi == NULL) {
+ return 1;
+ }
+ }
+
+ qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
+
+ // Check if the list has valid errors.
+ if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ return 1;
+ }
+
+ int prev_fnum = 0;
+ int eidx = 0;
+ qfline_T *qfp;
+ size_t i;
+ assert(qfl->qf_index >= 0);
+ for (i = 1, qfp = qfl->qf_start;
+ i <= (size_t)qfl->qf_index && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (!qfp->qf_valid) {
+ continue;
+ }
+
+ if (eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ eidx++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ } else {
+ eidx++;
+ }
+ }
+
+ return eidx != 0 ? eidx : 1;
+}
+
+/// Get the 'n'th valid error entry in the quickfix or location list.
+///
+/// Used by :cdo, :ldo, :cfdo and :lfdo commands.
+/// For :cdo and :ldo, returns the 'n'th valid error entry.
+/// For :cfdo and :lfdo, returns the 'n'th valid file entry.
+static size_t qf_get_nth_valid_entry(qf_info_T *qi, size_t n, bool fdo)
+ FUNC_ATTR_NONNULL_ALL
+{
+ qf_list_T *qfl = &qi->qf_lists[qi->qf_curlist];
+
+ // Check if the list has valid errors.
+ if (qfl->qf_count <= 0 || qfl->qf_nonevalid) {
+ return 1;
+ }
+
+ int prev_fnum = 0;
+ size_t eidx = 0;
+ size_t i;
+ qfline_T *qfp;
+ assert(qfl->qf_count >= 0);
+ for (i = 1, qfp = qfl->qf_start;
+ i <= (size_t)qfl->qf_count && qfp != NULL;
+ i++, qfp = qfp->qf_next) {
+ if (qfp->qf_valid) {
+ if (fdo) {
+ if (qfp->qf_fnum > 0 && qfp->qf_fnum != prev_fnum) {
+ // Count the number of files.
+ eidx++;
+ prev_fnum = qfp->qf_fnum;
+ }
+ } else {
+ eidx++;
+ }
+ }
+
+ if (eidx == n) {
+ break;
+ }
+ }
+
+ return i <= (size_t)qfl->qf_count ? i : 1;
+}
+
/*
* ":cc", ":crewind", ":cfirst" and ":clast".
* ":ll", ":lrewind", ":lfirst" and ":llast".
+ * ":cdo", ":ldo", ":cfdo" and ":lfdo".
*/
void ex_cc(exarg_T *eap)
{
@@ -2582,7 +2732,10 @@ void ex_cc(exarg_T *eap)
if (eap->cmdidx == CMD_ll
|| eap->cmdidx == CMD_lrewind
|| eap->cmdidx == CMD_lfirst
- || eap->cmdidx == CMD_llast) {
+ || eap->cmdidx == CMD_llast
+ || eap->cmdidx == CMD_llast
+ || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_lfdo) {
qi = GET_LOC_LIST(curwin);
if (qi == NULL) {
EMSG(_(e_loclist));
@@ -2590,21 +2743,42 @@ void ex_cc(exarg_T *eap)
}
}
- qf_jump(qi, 0,
- eap->addr_count > 0
- ? (int)eap->line2
- : (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll)
- ? 0
- : (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
- || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst)
- ? 1
- : 32767,
- eap->forceit);
+ int errornr;
+ if (eap->addr_count > 0) {
+ errornr = (int)eap->line2;
+ } else if (eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) {
+ errornr = 0;
+ } else if (eap->cmdidx == CMD_crewind || eap->cmdidx == CMD_lrewind
+ || eap->cmdidx == CMD_cfirst || eap->cmdidx == CMD_lfirst) {
+ errornr = 1;
+ } else {
+ errornr = 32767;
+ }
+
+ // For cdo and ldo commands, jump to the nth valid error.
+ // For cfdo and lfdo commands, jump to the nth valid file entry.
+ if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
+ size_t n;
+ if (eap->addr_count > 0) {
+ assert(eap->line1 >= 0);
+ n = (size_t)eap->line1;
+ } else {
+ n = 1;
+ }
+ size_t valid_entry = qf_get_nth_valid_entry(qi, n,
+ eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo);
+ assert(valid_entry <= INT_MAX);
+ errornr = (int)valid_entry;
+ }
+
+ qf_jump(qi, 0, errornr, eap->forceit);
}
/*
* ":cnext", ":cnfile", ":cNext" and ":cprevious".
* ":lnext", ":lNext", ":lprevious", ":lnfile", ":lNfile" and ":lpfile".
+ * ":cdo", ":ldo", ":cfdo" and ":lfdo".
*/
void ex_cnext(exarg_T *eap)
{
@@ -2615,7 +2789,10 @@ void ex_cnext(exarg_T *eap)
|| eap->cmdidx == CMD_lprevious
|| eap->cmdidx == CMD_lnfile
|| eap->cmdidx == CMD_lNfile
- || eap->cmdidx == CMD_lpfile) {
+ || eap->cmdidx == CMD_lpfile
+ || eap->cmdidx == CMD_lpfile
+ || eap->cmdidx == CMD_ldo
+ || eap->cmdidx == CMD_lfdo) {
qi = GET_LOC_LIST(curwin);
if (qi == NULL) {
EMSG(_(e_loclist));
@@ -2623,15 +2800,26 @@ void ex_cnext(exarg_T *eap)
}
}
- qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext)
+ int errornr;
+ if (eap->addr_count > 0 &&
+ (eap->cmdidx != CMD_cdo && eap->cmdidx != CMD_ldo &&
+ eap->cmdidx != CMD_cfdo && eap->cmdidx != CMD_lfdo)) {
+ errornr = (int)eap->line2;
+ } else {
+ errornr = 1;
+ }
+
+ qf_jump(qi, (eap->cmdidx == CMD_cnext || eap->cmdidx == CMD_lnext
+ || eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo)
? FORWARD
- : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile)
+ : (eap->cmdidx == CMD_cnfile || eap->cmdidx == CMD_lnfile
+ || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
? FORWARD_FILE
: (eap->cmdidx == CMD_cpfile || eap->cmdidx == CMD_lpfile
|| eap->cmdidx == CMD_cNfile || eap->cmdidx == CMD_lNfile)
? BACKWARD_FILE
: BACKWARD,
- eap->addr_count > 0 ? (int)eap->line2 : 1, eap->forceit);
+ errornr, eap->forceit);
}
/*
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 8c3e99c624..1a414a8847 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -29,6 +29,7 @@ SCRIPTS := test_eval.out \
test_charsearch.out \
test_close_count.out \
test_command_count.out \
+ test_cdo.out \
SCRIPTS_GUI := test16.out
diff --git a/src/nvim/testdir/test_breakindent.in b/src/nvim/testdir/test_breakindent.in
index b05aef7f8d..5a8e580c4a 100644
--- a/src/nvim/testdir/test_breakindent.in
+++ b/src/nvim/testdir/test_breakindent.in
@@ -101,7 +101,7 @@ fygjyl:let line2 = @0
:$put =line2
:"
:let g:test="Test 14: breakindent + visual blockwise delete #1"
-:set all& breakindent
+:set all& breakindent shada+=nX-test-breakindent.shada
:30vnew
:normal! 3a1234567890
:normal! a abcde
diff --git a/src/nvim/testdir/test_cdo.in b/src/nvim/testdir/test_cdo.in
new file mode 100644
index 0000000000..fb80ea1164
--- /dev/null
+++ b/src/nvim/testdir/test_cdo.in
@@ -0,0 +1,107 @@
+Tests for the :cdo, :cfdo, :ldo and :lfdo commands
+
+STARTTEST
+:so small.vim
+:if !has('quickfix') | e! test.ok | wq! test.out | endif
+
+:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1')
+:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2')
+:call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3')
+
+:function RunTests(cchar)
+: let nl="\n"
+
+: enew
+: " Try with an empty list
+: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " Populate the list and then try
+: exe a:cchar . "getexpr ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']"
+: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " Run command only on selected error lines
+: enew
+: exe "2,3" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: " Boundary condition tests
+: enew
+: exe "1,1" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: enew
+: exe "3" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: " Range test commands
+: enew
+: exe "%" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: enew
+: exe "1,$" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: enew
+: exe a:cchar . 'prev'
+: exe "." . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: " Invalid error lines test
+: enew
+: exe "27" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "4,5" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " Run commands from an unsaved buffer
+: let v:errmsg=''
+: enew
+: setlocal modified
+: exe "2,2" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: if v:errmsg =~# 'No write since last change'
+: let g:result .= 'Unsaved file change test passed' . nl
+: else
+: let g:result .= 'Unsaved file change test failed' . nl
+: endif
+
+: " If the executed command fails, then the operation should be aborted
+: enew!
+: let subst_count = 0
+: exe a:cchar . "do s/Line/xLine/ | let subst_count += 1"
+: if subst_count == 1 && getline('.') == 'xLine1'
+: let g:result .= 'Abort command on error test passed' . nl
+: else
+: let g:result .= 'Abort command on error test failed' . nl
+: endif
+
+: exe "2,2" . a:cchar . "do! let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " List with no valid error entries
+: edit! +2 Xtestfile1
+: exe a:cchar . "getexpr ['non-error 1', 'non-error 2', 'non-error 3']"
+: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "2" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: let v:errmsg=''
+: exe "%" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "1,$" . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "." . a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: let g:result .= v:errmsg
+
+: " List with only one valid entry
+: exe a:cchar . "getexpr ['Xtestfile3:3:1:Line3']"
+: exe a:cchar . "do let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " Tests for :cfdo and :lfdo commands
+: exe a:cchar . "getexpr ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']"
+: exe a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "3" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "2,3" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "%" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe "1,$" . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+: exe a:cchar . 'pfile'
+: exe "." . a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+
+: " List with only one valid entry
+: exe a:cchar . "getexpr ['Xtestfile2:2:5:Line2']"
+: exe a:cchar . "fdo let g:result .= expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' . nl"
+:endfunction
+
+:let result=''
+:" Tests for the :cdo quickfix list command
+:call RunTests('c')
+:let result .= "\n"
+:" Tests for the :ldo location list command
+:call RunTests('l')
+
+:edit! test.out
+:0put =result
+:wq!
+ENDTEST
+
diff --git a/src/nvim/testdir/test_cdo.ok b/src/nvim/testdir/test_cdo.ok
new file mode 100644
index 0000000000..ddcff4bbb8
--- /dev/null
+++ b/src/nvim/testdir/test_cdo.ok
@@ -0,0 +1,66 @@
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile2 2L 2C
+Unsaved file change test passed
+Abort command on error test passed
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile3 2L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile2 2L 2C
+Xtestfile2 2L 5C
+
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile2 2L 2C
+Unsaved file change test passed
+Abort command on error test passed
+Xtestfile2 2L 2C
+Xtestfile3 3L 1C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile3 2L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile1 1L 3C
+Xtestfile2 2L 2C
+Xtestfile3 2L 3C
+Xtestfile2 2L 2C
+Xtestfile2 2L 5C
+
diff --git a/src/nvim/testdir/test_eval.in b/src/nvim/testdir/test_eval.in
index b2b982a434..54cdb03ba2 100644
--- a/src/nvim/testdir/test_eval.in
+++ b/src/nvim/testdir/test_eval.in
@@ -2,12 +2,18 @@ Test for various eval features. vim: set ft=vim :
Note: system clipboard is saved, changed and restored.
+clipboard contents
+something else
+
STARTTEST
:so small.vim
:set noswapfile
:lang C
:fun AppendRegContents(reg)
- call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1))))
+ call AppendRegParts(a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1)))
+:endfun
+:fun AppendRegParts(reg, type, cont, strcont, cont1, strcont1)
+ call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, a:type, a:cont, a:strcont, a:cont1, a:strcont1))
endfun
:command -nargs=? AR :call AppendRegContents(<q-args>)
:fun SetReg(...)
@@ -121,18 +127,23 @@ call SetReg('/', ["abc/\n"])
call SetReg('=', ['"abc/"'])
call SetReg('=', ["\"abc/\n\""])
$put ='{{{1 System clipboard'
+if has('clipboard')
" Save and restore system clipboard.
" If no connection to X-Server is possible, test should succeed.
-:let _clipreg = ['+', getreg('+'), getregtype('+')]
-:let _clipopt = &cb
-:let &cb='unnamedplus'
-:1y
-:AR +
-:tabdo :windo :echo "hi"
-:3y
-:AR +
-:let &cb=_clipopt
-:call call('setreg', _clipreg)
+let _clipreg = ['*', getreg('*'), getregtype('*')]
+let _clipopt = &cb
+let &cb='unnamed'
+5y
+AR *
+tabdo :windo :echo "hi"
+6y
+AR *
+let &cb=_clipopt
+call call('setreg', _clipreg)
+else
+ call AppendRegParts('*', 'V', "clipboard contents\n", "['clipboard contents']", "clipboard contents\n", "['clipboard contents']")
+ call AppendRegParts('*', 'V', "something else\n", "['something else']", "something else\n", "['something else']")
+endif
$put ='{{{1 Errors'
call ErrExe('call setreg()')
call ErrExe('call setreg(1)')
diff --git a/src/nvim/testdir/test_eval.ok b/src/nvim/testdir/test_eval.ok
index 2cdb8f5da8..cf7a5cd418 100644
--- a/src/nvim/testdir/test_eval.ok
+++ b/src/nvim/testdir/test_eval.ok
Binary files differ
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 33c310a8b2..b097ac4702 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -135,7 +135,7 @@ static int included_patches[] = {
// 861 NA
// 860,
// 859,
- // 858,
+ 858,
// 857,
// 856,
// 855 NA
@@ -318,7 +318,7 @@ static int included_patches[] = {
// 678 NA
// 677 NA
// 676 NA
- // 675,
+ 675,
// 674 NA
673,
// 672,
@@ -381,15 +381,15 @@ static int included_patches[] = {
615,
// 614,
// 613,
- // 612,
+ 612,
// 611 NA
// 610 NA
609,
- // 608,
+ 608,
// 607,
606,
// 605,
- // 604,
+ 604,
// 603,
602,
601,