diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 13 | ||||
-rw-r--r-- | src/nvim/eval.c | 46 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 105 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 6 | ||||
-rw-r--r-- | src/nvim/ex_cmds_defs.h | 27 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 101 | ||||
-rw-r--r-- | src/nvim/getchar.c | 6 | ||||
-rw-r--r-- | src/nvim/message.c | 19 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 46 | ||||
-rw-r--r-- | src/nvim/testdir/runtest.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_alot.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_filter_cmd.vim | 76 | ||||
-rw-r--r-- | src/nvim/version.c | 12 |
13 files changed, 319 insertions, 140 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7477118d6f..7def8c1684 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2365,12 +2365,17 @@ void buflist_list(exarg_T *eap) && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) { continue; } - msg_putchar('\n'); - if (buf_spname(buf) != NULL) + if (buf_spname(buf) != NULL) { STRLCPY(NameBuff, buf_spname(buf), MAXPATHL); - else - home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); + } else { + home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true); + } + if (message_filtered(NameBuff)) { + continue; + } + + msg_putchar('\n'); len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", buf->b_fnum, buf->b_p_bl ? ' ' : 'u', diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b0f47d8e45..1bf85ccd8b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21779,52 +21779,6 @@ void last_set_msg(scid_T scriptID) } } -/* - * List v:oldfiles in a nice way. - */ -void ex_oldfiles(exarg_T *eap) -{ - list_T *l = get_vim_var_list(VV_OLDFILES); - listitem_T *li; - long nr = 0; - - if (l == NULL) - msg((char_u *)_("No old files")); - else { - msg_start(); - msg_scroll = TRUE; - for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { - msg_outnum(++nr); - MSG_PUTS(": "); - msg_outtrans((char_u *)tv_get_string(&li->li_tv)); - msg_clr_eos(); - msg_putchar('\n'); - ui_flush(); /* output one line at a time */ - os_breakcheck(); - } - /* Assume "got_int" was set to truncate the listing. */ - got_int = FALSE; - - // File selection prompt on ":browse oldfiles" - if (cmdmod.browse) { - quit_more = false; - nr = prompt_for_number(false); - msg_starthere(); - if (nr > 0 && nr <= l->lv_len) { - const char *const p = tv_list_find_str(l, nr - 1); - if (p == NULL) { - return; - } - char *const s = (char *)expand_env_save((char_u *)p); - eap->arg = (char_u *)s; - eap->cmdidx = CMD_edit; - do_exedit(eap, NULL); - xfree(s); - } - } - } -} - // reset v:option_new, v:option_old and v:option_type void reset_v_option_vars(void) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 7726e0fc6d..f73d4c16b6 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1490,6 +1490,11 @@ void print_line(linenr_T lnum, int use_number, int list) { int save_silent = silent_mode; + // apply :filter /pat/ + if (message_filtered(ml_get(lnum))) { + return; + } + msg_start(); silent_mode = FALSE; info_message = TRUE; /* use mch_msg(), not mch_errmsg() */ @@ -6158,3 +6163,103 @@ void ex_substitute(exarg_T *eap) ga_clear(&save_view); unblock_autocmds(); } + +/// Skip over the pattern argument of ":vimgrep /pat/[g][j]". +/// Put the start of the pattern in "*s", unless "s" is NULL. +/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. +/// If "s" is not NULL terminate the pattern with a NUL. +/// Return a pointer to the char just past the pattern plus flags. +char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) +{ + int c; + + if (vim_isIDc(*p)) { + // ":vimgrep pattern fname" + if (s != NULL) { + *s = p; + } + p = skiptowhite(p); + if (s != NULL && *p != NUL) { + *p++ = NUL; + } + } else { + // ":vimgrep /pattern/[g][j] fname" + if (s != NULL) { + *s = p + 1; + } + c = *p; + p = skip_regexp(p + 1, c, true, NULL); + if (*p != c) { + return NULL; + } + + // Truncate the pattern. + if (s != NULL) { + *p = NUL; + } + p++; + + // Find the flags + while (*p == 'g' || *p == 'j') { + if (flags != NULL) { + if (*p == 'g') { + *flags |= VGR_GLOBAL; + } else { + *flags |= VGR_NOJUMP; + } + } + p++; + } + } + return p; +} + +/// List v:oldfiles in a nice way. +void ex_oldfiles(exarg_T *eap) +{ + list_T *l = get_vim_var_list(VV_OLDFILES); + listitem_T *li; + long nr = 0; + + if (l == NULL) { + msg((char_u *)_("No old files")); + } else { + msg_start(); + msg_scroll = true; + for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { + nr++; + const char *fname = tv_get_string(&li->li_tv); + if (!message_filtered((char_u *)fname)) { + msg_outnum(nr); + MSG_PUTS(": "); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); + msg_clr_eos(); + msg_putchar('\n'); + ui_flush(); // output one line at a time + os_breakcheck(); + } + } + + // Assume "got_int" was set to truncate the listing. + got_int = false; + + // File selection prompt on ":browse oldfiles" + if (cmdmod.browse) { + quit_more = false; + nr = prompt_for_number(false); + msg_starthere(); + if (nr > 0 && nr <= l->lv_len) { + const char *const p = tv_list_find_str(l, nr - 1); + if (p == NULL) { + return; + } + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; + eap->cmdidx = CMD_edit; + cmdmod.browse = false; + do_exedit(eap, NULL); + xfree(s); + } + } + } +} diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 92f0669422..523740444d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -931,6 +931,12 @@ return { func='ex_filetype', }, { + command='filter', + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), + addr_type=ADDR_LINES, + func='ex_wrongmodifier', + }, + { command='find', flags=bit.bor(RANGE, NOTADR, BANG, FILE1, EDITCMD, ARGOPT, TRLBAR), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 133c37cef4..d72b83404e 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -6,6 +6,7 @@ #include "nvim/pos.h" // for linenr_T #include "nvim/normal.h" +#include "nvim/regexp_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds_enum.generated.h" @@ -163,18 +164,20 @@ struct expand { /// flag. This needs to be saved for recursive commands, put them in a /// structure for easy manipulation. typedef struct { - int split; ///< flags for win_split() - int tab; ///< > 0 when ":tab" was used - bool browse; ///< true to invoke file dialog - bool confirm; ///< true to invoke yes/no dialog - bool hide; ///< true when ":hide" was used - bool keepalt; ///< true when ":keepalt" was used - bool keepjumps; ///< true when ":keepjumps" was used - bool keepmarks; ///< true when ":keepmarks" was used - bool keeppatterns; ///< true when ":keeppatterns" was used - bool lockmarks; ///< true when ":lockmarks" was used - bool noswapfile; ///< true when ":noswapfile" was used - char_u *save_ei; ///< saved value of 'eventignore' + int split; ///< flags for win_split() + int tab; ///< > 0 when ":tab" was used + bool browse; ///< true to invoke file dialog + bool confirm; ///< true to invoke yes/no dialog + bool hide; ///< true when ":hide" was used + bool keepalt; ///< true when ":keepalt" was used + bool keepjumps; ///< true when ":keepjumps" was used + bool keepmarks; ///< true when ":keepmarks" was used + bool keeppatterns; ///< true when ":keeppatterns" was used + bool lockmarks; ///< true when ":lockmarks" was used + bool noswapfile; ///< true when ":noswapfile" was used + char_u *save_ei; ///< saved value of 'eventignore' + regmatch_T filter_regmatch; ///< set by :filter /pat/ + bool filter_force; ///< set for :filter! } cmdmod_T; #endif // NVIM_EX_CMDS_DEFS_H diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 7568d71ac0..72b2e0f29b 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1350,6 +1350,31 @@ static char_u * do_one_cmd(char_u **cmdlinep, cmdmod.keepjumps = true; continue; + case 'f': { // only accept ":filter {pat} cmd" + char_u *reg_pat; + + if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { + break; + } + if (*p == '!') { + cmdmod.filter_force = true; + p = skipwhite(p + 1); + if (*p == NUL || ends_excmd(*p)) { + break; + } + } + p = skip_vimgrep_pat(p, ®_pat, NULL); + if (p == NULL || *p == NUL) { + break; + } + cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); + if (cmdmod.filter_regmatch.regprog == NULL) { + break; + } + ea.cmd = p; + continue; + } + /* ":hide" and ":hide | cmd" are not modifiers */ case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3) || *p == NUL || ends_excmd(*p)) @@ -1452,6 +1477,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, } break; } + char_u *after_modifier = ea.cmd; ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0 && !(cstack->cs_flags[cstack-> @@ -1734,7 +1760,13 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (!ea.skip) { STRCPY(IObuff, _("E492: Not an editor command")); if (!(flags & DOCMD_VERBOSE)) { - append_command(*cmdlinep); + // If the modifier was parsed OK the error must be in the following + // command + if (after_modifier != NULL) { + append_command(after_modifier); + } else { + append_command(*cmdlinep); + } } errormsg = IObuff; did_emsg_syntax = TRUE; @@ -2104,6 +2136,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_echomsg: case CMD_echon: case CMD_execute: + case CMD_filter: case CMD_help: case CMD_hide: case CMD_ijump: @@ -2255,6 +2288,10 @@ doend: free_string_option(cmdmod.save_ei); } + if (cmdmod.filter_regmatch.regprog != NULL) { + vim_regfree(cmdmod.filter_regmatch.regprog); + } + cmdmod = save_cmdmod; if (save_msg_silent != -1) { @@ -2540,28 +2577,29 @@ static struct cmdmod { int minlen; int has_count; /* :123verbose :3tab */ } cmdmods[] = { - {"aboveleft", 3, FALSE}, - {"belowright", 3, FALSE}, - {"botright", 2, FALSE}, - {"browse", 3, FALSE}, - {"confirm", 4, FALSE}, - {"hide", 3, FALSE}, - {"keepalt", 5, FALSE}, - {"keepjumps", 5, FALSE}, - {"keepmarks", 3, FALSE}, - {"keeppatterns", 5, FALSE}, - {"leftabove", 5, FALSE}, - {"lockmarks", 3, FALSE}, - {"noautocmd", 3, FALSE}, - {"noswapfile", 3, FALSE}, - {"rightbelow", 6, FALSE}, - {"sandbox", 3, FALSE}, - {"silent", 3, FALSE}, - {"tab", 3, TRUE}, - {"topleft", 2, FALSE}, - {"unsilent", 3, FALSE}, - {"verbose", 4, TRUE}, - {"vertical", 4, FALSE}, + { "aboveleft", 3, false }, + { "belowright", 3, false }, + { "botright", 2, false }, + { "browse", 3, false }, + { "confirm", 4, false }, + { "filter", 4, false }, + { "hide", 3, false }, + { "keepalt", 5, false }, + { "keepjumps", 5, false }, + { "keepmarks", 3, false }, + { "keeppatterns", 5, false }, + { "leftabove", 5, false }, + { "lockmarks", 3, false }, + { "noautocmd", 3, false }, + { "noswapfile", 3, false }, + { "rightbelow", 6, false }, + { "sandbox", 3, false }, + { "silent", 3, false }, + { "tab", 3, true }, + { "topleft", 2, false }, + { "unsilent", 3, false }, + { "verbose", 4, true }, + { "vertical", 4, false }, }; /* @@ -3031,6 +3069,16 @@ const char * set_one_cmd_context( case CMD_windo: return arg; + case CMD_filter: + if (*arg != NUL) { + arg = (const char *)skip_vimgrep_pat((char_u *)arg, NULL, NULL); + } + if (arg == NULL || *arg == NUL) { + xp->xp_context = EXPAND_NOTHING; + return NULL; + } + return (const char *)skipwhite((const char_u *)arg); + case CMD_match: if (*arg == NUL || !ends_excmd(*arg)) { /* also complete "None" */ @@ -4851,9 +4899,12 @@ static void uc_list(char_u *name, size_t name_len) cmd = USER_CMD_GA(gap, i); a = cmd->uc_argt; - /* Skip commands which don't match the requested prefix */ - if (STRNCMP(name, cmd->uc_name, name_len) != 0) + // Skip commands which don't match the requested prefix and + // commands filtered out. + if (STRNCMP(name, cmd->uc_name, name_len) != 0 + || message_filtered(cmd->uc_name)) { continue; + } /* Put out the title first time */ if (!found) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9d32df5a68..6e21ee96e8 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1592,7 +1592,7 @@ vungetc ( /* unget one character (can only be done once!) */ /// This may do a blocking wait if "advance" is TRUE. /// /// if "advance" is TRUE (vgetc()): -/// really get the character. +/// Really get the character. /// KeyTyped is set to TRUE in the case the user typed the key. /// KeyStuffed is TRUE if the character comes from the stuff buffer. /// if "advance" is FALSE (vpeekc()): @@ -3168,6 +3168,10 @@ showmap ( { size_t len = 1; + if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)) { + return; + } + if (msg_didout || msg_silent != 0) { msg_putchar('\n'); if (got_int) /* 'q' typed at MORE prompt */ diff --git a/src/nvim/message.c b/src/nvim/message.c index 696855e3aa..057ce75f79 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -31,6 +31,7 @@ #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/normal.h" +#include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/ui.h" @@ -148,6 +149,12 @@ msg_attr_keep ( int retval; char_u *buf = NULL; + // Skip messages not match ":filter pattern". + // Don't filter when there is an error. + if (!emsg_on_display && message_filtered(s)) { + return true; + } + if (attr == 0) { set_vim_var_string(VV_STATUSMSG, (char *) s, -1); } @@ -1783,6 +1790,18 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, msg_check(); } +/// Return true when ":filter pattern" was used and "msg" does not match +/// "pattern". +bool message_filtered(char_u *msg) +{ + if (cmdmod.filter_regmatch.regprog == NULL) { + return false; + } + + bool match = vim_regexec(&cmdmod.filter_regmatch, msg, (colnr_T)0); + return cmdmod.filter_force ? match : !match; +} + /* * Scroll the screen up one line for displaying the next message line. */ diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 112498ae20..0ec0d5df9d 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3766,52 +3766,6 @@ theend: } /* - * Skip over the pattern argument of ":vimgrep /pat/[g][j]". - * Put the start of the pattern in "*s", unless "s" is NULL. - * If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. - * If "s" is not NULL terminate the pattern with a NUL. - * Return a pointer to the char just past the pattern plus flags. - */ -char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) -{ - int c; - - if (vim_isIDc(*p)) { - /* ":vimgrep pattern fname" */ - if (s != NULL) - *s = p; - p = skiptowhite(p); - if (s != NULL && *p != NUL) - *p++ = NUL; - } else { - /* ":vimgrep /pattern/[g][j] fname" */ - if (s != NULL) - *s = p + 1; - c = *p; - p = skip_regexp(p + 1, c, TRUE, NULL); - if (*p != c) - return NULL; - - /* Truncate the pattern. */ - if (s != NULL) - *p = NUL; - ++p; - - /* Find the flags */ - while (*p == 'g' || *p == 'j') { - if (flags != NULL) { - if (*p == 'g') - *flags |= VGR_GLOBAL; - else - *flags |= VGR_NOJUMP; - } - ++p; - } - } - return p; -} - -/* * Restore current working directory to "dirname_start" if they differ, taking * into account whether it is set locally or globally. */ diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 1cf7ab475c..140b67a1a5 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -72,6 +72,7 @@ let v:testing = 1 set directory^=. set backspace= set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd +set listchars=eol:$ " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE='Xnvim.log' diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 99d9835996..5da9a8b0f4 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -8,6 +8,7 @@ source test_ex_undo.vim source test_expr.vim source test_expr_utf8.vim source test_feedkeys.vim +source test_filter_cmd.vim source test_filter_map.vim source test_goto.vim source test_jumps.vim diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim new file mode 100644 index 0000000000..5aa5fa64df --- /dev/null +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -0,0 +1,76 @@ +" Test the :filter command modifier + +func Test_filter() + edit Xdoesnotmatch + edit Xwillmatch + call assert_equal('"Xwillmatch"', substitute(execute('filter willma ls'), '[^"]*\(".*"\)[^"]*', '\1', '')) + bwipe Xdoesnotmatch + bwipe Xwillmatch + + new + call setline(1, ['foo1', 'foo2', 'foo3', 'foo4', 'foo5']) + call assert_equal("\nfoo2\nfoo4", execute('filter /foo[24]/ 1,$print')) + call assert_equal("\n 2 foo2\n 4 foo4", execute('filter /foo[24]/ 1,$number')) + call assert_equal("\nfoo2$\nfoo4$", execute('filter /foo[24]/ 1,$list')) + + call assert_equal("\nfoo1$\nfoo3$\nfoo5$", execute('filter! /foo[24]/ 1,$list')) + bwipe! + + command XTryThis echo 'this' + command XTryThat echo 'that' + command XDoThat echo 'that' + let lines = split(execute('filter XTry command'), "\n") + call assert_equal(3, len(lines)) + call assert_match("XTryThat", lines[1]) + call assert_match("XTryThis", lines[2]) + delcommand XTryThis + delcommand XTryThat + delcommand XDoThat + + map f1 the first key + map f2 the second key + map f3 not a key + let lines = split(execute('filter the map f'), "\n") + call assert_equal(2, len(lines)) + call assert_match("f2", lines[0]) + call assert_match("f1", lines[1]) + unmap f1 + unmap f2 + unmap f3 +endfunc + +func Test_filter_fails() + call assert_fails('filter', 'E471:') + call assert_fails('filter pat', 'E476:') + call assert_fails('filter /pat', 'E476:') + call assert_fails('filter /pat/', 'E476:') + call assert_fails('filter /pat/ asdf', 'E492:') + + call assert_fails('filter!', 'E471:') + call assert_fails('filter! pat', 'E476:') + call assert_fails('filter! /pat', 'E476:') + call assert_fails('filter! /pat/', 'E476:') + call assert_fails('filter! /pat/ asdf', 'E492:') +endfunc + +function s:complete_filter_cmd(filtcmd) + let keystroke = "\<TAB>\<C-R>=execute('let cmdline = getcmdline()')\<CR>\<C-C>" + let cmdline = '' + call feedkeys(':' . a:filtcmd . keystroke, 'ntx') + return cmdline +endfunction + +func Test_filter_cmd_completion() + " Do not complete pattern + call assert_equal("filter \t", s:complete_filter_cmd('filter ')) + call assert_equal("filter pat\t", s:complete_filter_cmd('filter pat')) + call assert_equal("filter /pat\t", s:complete_filter_cmd('filter /pat')) + call assert_equal("filter /pat/\t", s:complete_filter_cmd('filter /pat/')) + + " Complete after string pattern + call assert_equal('filter pat print', s:complete_filter_cmd('filter pat pri')) + + " Complete after regexp pattern + call assert_equal('filter /pat/ print', s:complete_filter_cmd('filter /pat/ pri')) + call assert_equal('filter #pat# print', s:complete_filter_cmd('filter #pat# pri')) +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index b1aa8e2a4b..b579cdef12 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -181,7 +181,7 @@ static const int included_patches[] = { 2266, 2265, 2264, - // 2263, + 2263, // 2262 NA // 2261 NA // 2260 NA @@ -198,14 +198,14 @@ static const int included_patches[] = { 2249, 2248, // 2247 NA - // 2246, - // 2245, - // 2244, + 2246, + 2245, + 2244, // 2243 NA 2242, 2241, 2240, - // 2239, + 2239, // 2238 NA 2237, 2236, @@ -213,7 +213,7 @@ static const int included_patches[] = { // 2234 NA 2233, // 2232 NA - // 2231, + 2231, 2230, // 2229, 2228, |