diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 772527532e..1b2eb774c9 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() */ diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 2566dd2f48..523740444d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -932,7 +932,7 @@ return { }, { command='filter', - flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM), + flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM), addr_type=ADDR_LINES, func='ex_wrongmodifier', }, diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index b10775c001..d72b83404e 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -177,6 +177,7 @@ typedef struct { 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 45a11482d0..2eaf92df00 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1356,6 +1356,13 @@ static char_u * do_one_cmd(char_u **cmdlinep, 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; @@ -4883,9 +4890,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 c83dfcd63f..057ce75f79 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1794,8 +1794,12 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, /// "pattern". bool message_filtered(char_u *msg) { - return cmdmod.filter_regmatch.regprog != NULL - && !vim_regexec(&cmdmod.filter_regmatch, msg, (colnr_T)0); + 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; } /* 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_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index f85a11ce45..0bbd905c85 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -4,6 +4,39 @@ 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() @@ -12,4 +45,10 @@ func Test_filter_fails() 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 diff --git a/src/nvim/version.c b/src/nvim/version.c index f2a97cf360..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