Merge #5582 from justinmk/icm

'inccommand': Support :smagic/:snomagic. Less jumping around.
This commit is contained in:
Justin M. Keyes
2016-11-12 13:32:48 +01:00
committed by GitHub
9 changed files with 156 additions and 99 deletions

View File

@@ -1155,21 +1155,14 @@ A jump table for the options with a short description can be found at |Q_op|.
*'buftype'* *'bt'* *E382*
'buftype' 'bt' string (default: "")
local to buffer
{not available when compiled without the |+quickfix|
feature}
The value of this option specifies the type of a buffer:
<empty> normal buffer
nofile buffer which is not related to a file and will not be
written
nowrite buffer which will not be written
acwrite buffer which will always be written with BufWriteCmd
autocommands.
quickfix quickfix buffer, contains list of errors |:cwindow|
or list of locations |:lwindow|
help help buffer (you are not supposed to set this
manually)
terminal terminal buffer, this is set automatically when a
terminal is created. |terminal-emulator|
acwrite buffer will always be written with |BufWriteCmd|s
help help buffer (do not set this manually)
nofile buffer is not related to a file, will not be written
nowrite buffer will not be written
quickfix list of errors |:cwindow| or locations |:lwindow|
terminal |terminal-emulator| buffer
This option is used together with 'bufhidden' and 'swapfile' to
specify special kinds of buffers. See |special-buffers|.
@@ -3420,10 +3413,10 @@ A jump table for the options with a short description can be found at |Q_op|.
'inccommand' 'icm' string (default "")
global
"nosplit" : Shows the effects of a command incrementally, as you type.
"split" : Also shows partial off-screen results in a preview window.
"nosplit": Shows the effects of a command incrementally, as you type.
"split" : Also shows partial off-screen results in a preview window.
Currently only works for |:substitute|. |hl-Substitute|
Works for |:substitute|, |:smagic|, |:snomagic|. |hl-Substitute|
*'include'* *'inc'*
'include' 'inc' string (default "^\s*#\s*include")

View File

@@ -56,7 +56,7 @@ syn keyword vimGroup contained Comment Constant String Character Number Boolean
syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu
syn match vimHLGroup contained "Conceal"
syn keyword vimOnlyHLGroup contained VisualNOS
syn keyword nvimHLGroup contained EndOfBuffer IncSubstitute TermCursor TermCursorNC QuickFixLine
syn keyword nvimHLGroup contained EndOfBuffer Substitute TermCursor TermCursorNC QuickFixLine
"}}}2
syn case match
" Special Vim Highlighting (not automatic) {{{1

View File

@@ -5273,14 +5273,20 @@ wipe_buffer (
}
}
/// Creates or switches to a special-purpose buffer.
/// Creates or switches to a scratch buffer. :h special-buffers
/// Scratch buffer is:
/// - buftype=nofile bufhidden=hide noswapfile
/// - Always considered 'nomodified'
///
/// @param bufnr Buffer to switch to, or 0 to create a new buffer.
void buf_open_special(handle_T bufnr, char *bufname, char *buftype)
///
/// @see curbufIsChanged()
void buf_open_scratch(handle_T bufnr, char *bufname)
{
(void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
(void)setfname(curbuf, (char_u *)bufname, NULL, true);
set_option_value((char_u *)"bt", 0L, (char_u *)buftype, OPT_LOCAL);
set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL);
set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL);
RESET_BINDING(curwin);
}

View File

@@ -2188,7 +2188,6 @@ int do_ecmd(
if (fnum) {
buf = buflist_findnr(fnum);
} else {
ILOG("here");
if (flags & ECMD_ADDBUF) {
linenr_T tlnum = 1L;
@@ -3275,8 +3274,7 @@ buf_T *do_sub(exarg_T *eap)
return NULL;
}
int search_options = eap->is_live ? 0 : SEARCH_HIS;
if (search_regcomp(pat, RE_SUBST, which_pat, search_options,
if (search_regcomp(pat, RE_SUBST, which_pat, (eap->is_live ? 0 : SEARCH_HIS),
&regmatch) == FAIL) {
if (subflags.do_error) {
EMSG(_(e_invcmd));
@@ -3951,13 +3949,9 @@ skip:
// Show 'inccommand' preview if there are matched lines.
buf_T *preview_buf = NULL;
if (eap->is_live && matched_lines.size != 0 && pat != NULL && *p_icm != NUL) {
if (eap->is_live && *p_icm != NUL && matched_lines.size != 0 && pat != NULL) {
curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
preview_buf = show_sub(old_cursor, pat, sub, eap->line1, eap->line2,
&matched_lines);
} else if (*p_icm != NUL && eap->is_live) {
curwin->w_cursor = old_cursor; // don't move the cursor
preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines);
}
for (MatchedLine m; kv_size(matched_lines);) {
@@ -6004,8 +5998,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
/// Shows the effects of the :substitute command being typed ('inccommand').
/// If inccommand=split, shows a preview window and later restores the layout.
static buf_T *show_sub(pos_T old_cusr, char_u *pat, char_u *sub, linenr_T line1,
linenr_T line2, MatchedLineVec *matched_lines)
static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub,
MatchedLineVec *matched_lines)
FUNC_ATTR_NONNULL_ALL
{
static handle_T bufnr = 0; // special buffer, re-used on each visit
@@ -6025,49 +6019,35 @@ static buf_T *show_sub(pos_T old_cusr, char_u *pat, char_u *sub, linenr_T line1,
// disable file info message
set_option_value((char_u *)"shm", 0L, (char_u *)"F", 0);
bool outside_curline = (line1 != curwin->w_cursor.lnum
|| line2 != curwin->w_cursor.lnum);
bool outside_curline = (eap->line1 != old_cusr.lnum
|| eap->line2 != old_cusr.lnum);
bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size);
if (preview_buf == curbuf) { // Preview buffer cannot preview itself!
split = false;
preview_buf = NULL;
}
// Place cursor on the first match after the cursor. (If all matches are
// above, then do_sub already placed cursor on the last match.)
colnr_T cur_col = -1;
MatchedLine curmatch;
for (size_t j = 0; j < matched_lines->size && cur_col == -1; j++) {
curmatch = matched_lines->items[j];
if (curmatch.lnum == old_cusr.lnum) {
// On cursor line; iterate in-line matches to find one after cursor.
for (size_t i = 0; i < curmatch.cols.size; i++) {
if (curmatch.cols.items[i] >= old_cusr.col) {
cur_col = curmatch.cols.items[i];
curwin->w_cursor.lnum = curmatch.lnum;
curwin->w_cursor.col = cur_col;
break;
}
}
} else if (curmatch.lnum > old_cusr.lnum) {
// After cursor; put cursor on first match there.
cur_col = curmatch.cols.items[0];
// Place cursor on nearest matching line, to undo do_sub() cursor placement.
for (size_t i = 0; i < matched_lines->size; i++) {
MatchedLine curmatch = matched_lines->items[i];
if (curmatch.lnum >= old_cusr.lnum) {
curwin->w_cursor.lnum = curmatch.lnum;
curwin->w_cursor.col = cur_col;
}
curwin->w_cursor.col = curmatch.cols.items[0];
break;
} // Else: All matches are above, do_sub() already placed cursor.
}
if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) {
buf_open_special(preview_buf ? bufnr : 0, "[Preview]", "incsub");
buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]");
buf_clear();
preview_buf = curbuf;
set_option_value((char_u *)"bufhidden", 0L, (char_u *)"hide", OPT_LOCAL);
bufnr = preview_buf->handle;
curbuf->b_p_bl = false;
curbuf->b_p_ma = true;
curbuf->b_p_ul = -1;
curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin)
curwin->w_p_cul = false;
curwin->w_p_cuc = false;
curwin->w_p_spell = false;
curwin->w_p_fen = false;
@@ -6092,7 +6072,7 @@ static buf_T *show_sub(pos_T old_cusr, char_u *pat, char_u *sub, linenr_T line1,
old_line_size = line_size;
}
// put " | lnum|line" into str and append it to the preview buffer
// Put "|lnum| line" into `str` and append it to the preview buffer.
snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line);
ml_append(line, (char_u *)str, (colnr_T)line_size, false);
@@ -6132,8 +6112,8 @@ static buf_T *show_sub(pos_T old_cusr, char_u *pat, char_u *sub, linenr_T line1,
/// :substitute command
///
/// If 'inccommand' is empty this just calls do_sub().
/// If 'inccommand' is set, shows a "live" preview then removes the changes
/// If 'inccommand' is empty: calls do_sub().
/// If 'inccommand' is set: shows a "live" preview then removes the changes.
/// from undo history.
void ex_substitute(exarg_T *eap)
{
@@ -6142,19 +6122,26 @@ void ex_substitute(exarg_T *eap)
return;
}
block_autocmds(); // Disable events during command preview.
char_u *save_eap = eap->arg;
save_search_patterns();
int save_changedtick = curbuf->b_changedtick;
time_t save_b_u_time_cur = curbuf->b_u_time_cur;
u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
long save_b_p_ul = curbuf->b_p_ul;
int save_w_p_cul = curwin->w_p_cul;
int save_w_p_cuc = curwin->w_p_cuc;
emsg_off++; // No error messages during command preview.
curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes
block_autocmds(); // disable events before show_sub() opens window/buffer
emsg_off++; // No error messages for live commands
curwin->w_p_cul = false; // Disable 'cursorline'
curwin->w_p_cuc = false; // Disable 'cursorcolumn'
buf_T *preview_buf = do_sub(eap);
if (save_changedtick != curbuf->b_changedtick) {
// Undo invisibly. This also moves the cursor!
if (!u_undo_and_forget(1)) { abort(); }
// Restore newhead. It is meaningless when curhead is valid, but we must
// restore it so that undotree() is identical before/after the preview.
@@ -6167,6 +6154,8 @@ void ex_substitute(exarg_T *eap)
close_windows(preview_buf, false);
}
curbuf->b_p_ul = save_b_p_ul;
curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
eap->arg = save_eap;
restore_search_patterns();
emsg_off--;

View File

@@ -14,7 +14,6 @@
#define ECMD_OLDBUF 0x04 // use existing buffer if it exists
#define ECMD_FORCEIT 0x08 // ! used in Ex command
#define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list
#define ECMD_RESERVED_BUFNR 0x20 // bufnr argument is reserved bufnr
/* for lnum argument in do_ecmd() */

View File

@@ -7295,15 +7295,13 @@ void ex_may_print(exarg_T *eap)
}
}
/*
* ":smagic" and ":snomagic".
*/
/// ":smagic" and ":snomagic".
static void ex_submagic(exarg_T *eap)
{
int magic_save = p_magic;
p_magic = (eap->cmdidx == CMD_smagic);
do_sub(eap);
ex_substitute(eap);
p_magic = magic_save;
}
@@ -9669,5 +9667,7 @@ bool cmd_is_live(char_u *cmd)
}
find_command(&ea, NULL);
return (ea.cmdidx == CMD_substitute);
return ea.cmdidx == CMD_substitute
|| ea.cmdidx == CMD_smagic
|| ea.cmdidx == CMD_snomagic;
}

View File

@@ -1599,6 +1599,16 @@ static int command_line_changed(CommandLineState *s)
&& cmd_is_live(ccline.cmdbuff)) {
// process a "live" command ('inccommand')
do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_LIVE);
// restore the window "view"
curwin->w_cursor = s->old_cursor;
curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol;
curwin->w_topline = s->old_topline;
curwin->w_topfill = s->old_topfill;
curwin->w_botline = s->old_botline;
update_topline();
redrawcmdline();
}
@@ -5147,7 +5157,9 @@ static int ex_window(void)
cmdwin_type = get_cmdline_type();
// Create empty command-line buffer.
buf_open_special(0, "[Command Line]", "nofile");
buf_open_scratch(0, "[Command Line]");
// Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL);
curwin->w_p_rl = cmdmsg_rl;
cmdmsg_rl = false;
curbuf->b_p_ma = true;

View File

@@ -1685,7 +1685,8 @@ void u_redo(int count)
u_doit(count, false);
}
/// undo, and remove the undo branch from the undo tree.
/// Undo and remove the branch from the undo tree.
/// Also moves the cursor (as a "normal" undo would).
bool u_undo_and_forget(int count)
{
if (curbuf->b_u_synced == false) {

View File

@@ -413,7 +413,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
insert("X")
feed("IY<esc>")
feed(":%s/tw/MO/<esc>")
-- using execute("undo") here will result in a "Press ENTER" prompt
-- execute("undo") here would cause "Press ENTER".
feed("u")
expect(default_text:gsub("Inc", "XInc"))
feed("u")
@@ -430,6 +430,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
if case == "split" then
screen:expect([[
Inc substitution on |
^MOo lines |
|
{15:~ }|
@@ -438,7 +439,6 @@ describe(":substitute, 'inccommand' preserves undo", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
Already...st change |
]])
else
@@ -482,6 +482,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
if case == "split" then
screen:expect([[
Inc substitution on |
two line^s |
|
{15:~ }|
@@ -490,7 +491,6 @@ describe(":substitute, 'inccommand' preserves undo", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
Already...st change |
]])
else
@@ -522,6 +522,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
if case == "split" then
screen:expect([[
Inc substitution on |
^MOo lines |
|
{15:~ }|
@@ -530,7 +531,6 @@ describe(":substitute, 'inccommand' preserves undo", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
Already...st change |
]])
else
@@ -564,6 +564,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
feed("u")
if case == "split" then
screen:expect([[
Inc substitution on |
^MOo lines |
|
{15:~ }|
@@ -572,7 +573,6 @@ describe(":substitute, 'inccommand' preserves undo", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
Already...st change |
]])
else
@@ -602,7 +602,8 @@ describe(":substitute, 'inccommand' preserves undo", function()
if case == "split" then
screen:expect([[
^two lines |
^LInc substitution on|
two lines |
|
{15:~ }|
{15:~ }|
@@ -610,7 +611,6 @@ describe(":substitute, 'inccommand' preserves undo", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
Already...st change |
]])
elseif case == "" then
@@ -705,7 +705,7 @@ describe(":substitute, inccommand=split", function()
]])
end)
it('shows split window with empty replacement', function()
it('shows preview with empty replacement', function()
feed(":%s/tw/")
screen:expect([[
Inc substitution on |
@@ -727,11 +727,11 @@ describe(":substitute, inccommand=split", function()
feed("x")
screen:expect([[
xo lines |
Inc substitution on |
xo lines |
|
{15:~ }|
{15:~ }|
{11:[No Name] [+] }|
|2| {12:x}o lines |
|4| {12:x}o lines |
@@ -746,11 +746,11 @@ describe(":substitute, inccommand=split", function()
feed("<bs>")
screen:expect([[
o lines |
Inc substitution on |
o lines |
|
{15:~ }|
{15:~ }|
{11:[No Name] [+] }|
|2| o lines |
|4| o lines |
@@ -768,11 +768,11 @@ describe(":substitute, inccommand=split", function()
it('shows split window when typing replacement', function()
feed(":%s/tw/XX")
screen:expect([[
XXo lines |
Inc substitution on |
XXo lines |
|
{15:~ }|
{15:~ }|
{11:[No Name] [+] }|
|2| {12:XX}o lines |
|4| {12:XX}o lines |
@@ -808,16 +808,37 @@ describe(":substitute, inccommand=split", function()
]])
end)
it("'hlsearch' highlights the substitution, 'cursorline' does not", function()
execute("set hlsearch")
execute("set cursorline") -- Should NOT appear in the preview window.
feed(":%s/tw")
it("'hlsearch' is active, 'cursorline' is not", function()
execute("set hlsearch cursorline")
feed("gg")
-- Assert that 'cursorline' is active.
screen:expect([[
{16:^Inc substitution on }|
two lines |
Inc substitution on |
{9:tw}{16:o lines }|
two lines |
|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
:set hlsearch cursorline |
]])
feed(":%s/tw")
-- 'cursorline' is NOT active during preview.
screen:expect([[
Inc substitution on |
{9:tw}o lines |
Inc substitution on |
{9:tw}o lines |
|
{11:[No Name] [+] }|
|2| {9:tw}o lines |
|4| {9:tw}o lines |
@@ -831,7 +852,7 @@ describe(":substitute, inccommand=split", function()
]])
end)
it('highlights the replacement text correctly', function()
it('highlights the replacement text', function()
feed('ggO')
feed('M M M<esc>')
feed(':%s/M/123/g')
@@ -855,9 +876,10 @@ describe(":substitute, inccommand=split", function()
end)
it('actually replaces text', function()
feed(":%s/tw/XX/g<enter>")
feed(":%s/tw/XX/g<Enter>")
screen:expect([[
Inc substitution on |
XXo lines |
Inc substitution on |
^XXo lines |
@@ -871,7 +893,6 @@ describe(":substitute, inccommand=split", function()
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
:%s/tw/XX/g |
]])
end)
@@ -884,11 +905,11 @@ describe(":substitute, inccommand=split", function()
feed(":%s/tw/X")
screen:expect([[
Inc substitution on |
BBo lines |
Inc substitution on |
Xo lines |
Inc substitution on |
Xo lines |
{11:[No Name] [+] }|
|1001| {12:X}o lines |
|1003| {12:X}o lines |
@@ -922,13 +943,13 @@ describe(":substitute, inccommand=split", function()
end)
it('works with the n flag', function()
feed(":%s/tw/Mix/n<enter>")
feed(":%s/tw/Mix/n<Enter>")
screen:expect([[
^two lines |
Inc substitution on |
two lines |
|
{15:~ }|
Inc substitution on |
two lines |
^ |
{15:~ }|
{15:~ }|
{15:~ }|
@@ -944,7 +965,7 @@ describe(":substitute, inccommand=split", function()
end)
describe(":substitute, inccommand=nosplit", function()
describe("inccommand=nosplit", function()
if helpers.pending_win32(pending) then return end
local screen = Screen.new(20,10)
@@ -958,7 +979,42 @@ describe(":substitute, inccommand=nosplit", function()
if screen then screen:detach() end
end)
it('does not show a split window anytime', function()
it("works with :smagic, :snomagic", function()
execute("set hlsearch")
insert("Line *.3.* here")
feed(":%smagic/3.*/X") -- start :smagic command
screen:expect([[
Inc substitution on |
two lines |
Inc substitution on |
two lines |
Line *.X |
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
:%smagic/3.*/X^ |
]])
feed([[<C-\><C-N>]]) -- cancel
feed(":%snomagic/3.*/X") -- start :snomagic command
screen:expect([[
Inc substitution on |
two lines |
Inc substitution on |
two lines |
Line *.X here |
{15:~ }|
{15:~ }|
{15:~ }|
{15:~ }|
:%snomagic/3.*/X^ |
]])
end)
it('never shows preview buffer', function()
execute("set hlsearch")
feed(":%s/tw")
@@ -1163,7 +1219,7 @@ describe("'inccommand' and :cnoremap", function()
end)
describe("'inccommand': autocommands", function()
describe("'inccommand' autocommands", function()
before_each(clear)
-- keys are events to be tested
@@ -1251,7 +1307,7 @@ describe("'inccommand': autocommands", function()
end)
describe("'inccommand': split windows", function()
describe("'inccommand' split windows", function()
if helpers.pending_win32(pending) then return end
local screen
@@ -1268,10 +1324,12 @@ describe("'inccommand': split windows", function()
it('work after more splits', function()
refresh()
feed("gg")
execute("vsplit")
execute("split")
feed(":%s/tw")
screen:expect([[
Inc substitution on {10:|}Inc substitution on|
two lines {10:|}two lines |
{10:|} |
{15:~ }{10:|}{15:~ }|
@@ -1285,13 +1343,12 @@ describe("'inccommand': split windows", function()
{15:~ }{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{11:[No Name] [+] }{10:|}{15:~ }|
Inc substitution on {10:|}{15:~ }|
two lines {10:|}{15:~ }|
{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{15:~ }{10:|}{15:~ }|
{10:[No Name] [+] [No Name] [+] }|
|2| two lines |
|