vim-patch:7.4.2320

Problem:    Redraw problem when using 'incsearch'.
Solution:   Save the current view when deleting characters. (Christian
            Brabandt) Fix that the '" mark is set in the wrong position. Don't
            change the search start when using BS.

dda933d06c
This commit is contained in:
James McCoy 2017-06-25 12:15:58 -04:00
parent 3679752dbd
commit 54d5e90a2b
No known key found for this signature in database
GPG Key ID: DFE691AE331BA3DB
5 changed files with 174 additions and 39 deletions

View File

@ -100,13 +100,18 @@ typedef struct command_line_state {
char_u *lookfor; // string to match char_u *lookfor; // string to match
int hiscnt; // current history line in use int hiscnt; // current history line in use
int histype; // history type to be used int histype; // history type to be used
pos_T old_cursor; pos_T search_start; // where 'incsearch' starts searching
pos_T save_cursor;
colnr_T old_curswant; colnr_T old_curswant;
colnr_T init_curswant;
colnr_T old_leftcol; colnr_T old_leftcol;
colnr_T init_leftcol;
linenr_T old_topline; linenr_T old_topline;
linenr_T init_topline;
int old_topfill; int old_topfill;
int init_topfill;
linenr_T old_botline; linenr_T old_botline;
pos_T cursor_start; linenr_T init_botline;
pos_T match_start; pos_T match_start;
pos_T match_end; pos_T match_end;
int did_incsearch; int did_incsearch;
@ -171,6 +176,11 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
s->save_p_icm = vim_strsave(p_icm); s->save_p_icm = vim_strsave(p_icm);
s->ignore_drag_release = true; s->ignore_drag_release = true;
s->match_start = curwin->w_cursor; s->match_start = curwin->w_cursor;
s->init_curswant = curwin->w_curswant;
s->init_leftcol = curwin->w_leftcol;
s->init_topline = curwin->w_topline;
s->init_topfill = curwin->w_topfill;
s->init_botline = curwin->w_botline;
if (s->firstc == -1) { if (s->firstc == -1) {
s->firstc = NUL; s->firstc = NUL;
@ -184,8 +194,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.overstrike = false; // always start in insert mode ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end); clearpos(&s->match_end);
s->old_cursor = curwin->w_cursor; // needs to be restored later s->save_cursor = curwin->w_cursor; // may be restored later
s->cursor_start = s->old_cursor; s->search_start = curwin->w_cursor;
s->old_curswant = curwin->w_curswant; s->old_curswant = curwin->w_curswant;
s->old_leftcol = curwin->w_leftcol; s->old_leftcol = curwin->w_leftcol;
s->old_topline = curwin->w_topline; s->old_topline = curwin->w_topline;
@ -288,9 +298,15 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.xpc = NULL; ccline.xpc = NULL;
if (s->did_incsearch) { if (s->did_incsearch) {
curwin->w_cursor = s->old_cursor;
if (s->gotesc) { if (s->gotesc) {
curwin->w_cursor = s->cursor_start; curwin->w_cursor = s->save_cursor;
} else {
if (!equalpos(s->save_cursor, s->search_start)) {
// put the '" mark at the original position
curwin->w_cursor = s->save_cursor;
setpcmark();
}
curwin->w_cursor = s->search_start;
} }
curwin->w_curswant = s->old_curswant; curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol; curwin->w_leftcol = s->old_leftcol;
@ -939,10 +955,14 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars. // Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) { if (ccline.cmdlen == 0) {
s->old_cursor = s->cursor_start; s->search_start = s->save_cursor;
} else { // save view settings, so that the screen won't be restored at the
s->old_cursor = s->match_start; // wrong position
decl(&s->old_cursor); s->old_curswant = s->init_curswant;
s->old_leftcol = s->init_leftcol;
s->old_topline = s->init_topline;
s->old_topfill = s->init_topfill;
s->old_botline = s->init_botline;
} }
redrawcmd(); redrawcmd();
} else if (ccline.cmdlen == 0 && s->c != Ctrl_W } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
@ -962,6 +982,7 @@ static int command_line_handle_key(CommandLineState *s)
} }
msg_putchar(' '); // delete ':' msg_putchar(' '); // delete ':'
} }
s->search_start = s->save_cursor;
redraw_cmdline = true; redraw_cmdline = true;
return 0; // back to cmd mode return 0; // back to cmd mode
} }
@ -1017,7 +1038,7 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars. // Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) { if (ccline.cmdlen == 0) {
s->old_cursor = s->cursor_start; s->search_start = s->save_cursor;
} }
redrawcmd(); redrawcmd();
return command_line_changed(s); return command_line_changed(s);
@ -1250,7 +1271,7 @@ static int command_line_handle_key(CommandLineState *s)
// Add a character from under the cursor for 'incsearch' // Add a character from under the cursor for 'incsearch'
if (s->did_incsearch) { if (s->did_incsearch) {
curwin->w_cursor = s->match_end; curwin->w_cursor = s->match_end;
if (!equalpos(curwin->w_cursor, s->old_cursor)) { if (!equalpos(curwin->w_cursor, s->search_start)) {
s->c = gchar_cursor(); s->c = gchar_cursor();
// If 'ignorecase' and 'smartcase' are set and the // If 'ignorecase' and 'smartcase' are set and the
// command line has no uppercase characters, convert // command line has no uppercase characters, convert
@ -1453,23 +1474,23 @@ static int command_line_handle_key(CommandLineState *s)
emsg_off--; emsg_off--;
ui_busy_stop(); ui_busy_stop();
if (s->i) { if (s->i) {
s->old_cursor = s->match_start; s->search_start = s->match_start;
s->match_end = t; s->match_end = t;
s->match_start = t; s->match_start = t;
if (s->c == Ctrl_T && s->firstc == '/') { if (s->c == Ctrl_T && s->firstc == '/') {
// move just before the current match, so that // move just before the current match, so that
// when nv_search finishes the cursor will be // when nv_search finishes the cursor will be
// put back on the match // put back on the match
s->old_cursor = t; s->search_start = t;
(void)decl(&s->old_cursor); (void)decl(&s->search_start);
} }
if (lt(t, s->old_cursor) && s->c == Ctrl_G) { if (lt(t, s->search_start) && s->c == Ctrl_G) {
// wrap around // wrap around
s->old_cursor = t; s->search_start = t;
if (s->firstc == '?') { if (s->firstc == '?') {
(void)incl(&s->old_cursor); (void)incl(&s->search_start);
} else { } else {
(void)decl(&s->old_cursor); (void)decl(&s->search_start);
} }
} }
@ -1607,7 +1628,7 @@ static int command_line_changed(CommandLineState *s)
return 1; return 1;
} }
s->incsearch_postponed = false; s->incsearch_postponed = false;
curwin->w_cursor = s->old_cursor; // start at old position curwin->w_cursor = s->search_start; // start at old position
// If there is no command line, don't do anything // If there is no command line, don't do anything
if (ccline.cmdlen == 0) { if (ccline.cmdlen == 0) {
@ -1698,7 +1719,7 @@ static int command_line_changed(CommandLineState *s)
emsg_silent--; // Unblock error reporting emsg_silent--; // Unblock error reporting
// Restore the window "view". // Restore the window "view".
curwin->w_cursor = s->old_cursor; curwin->w_cursor = s->save_cursor;
curwin->w_curswant = s->old_curswant; curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol; curwin->w_leftcol = s->old_leftcol;
curwin->w_topline = s->old_topline; curwin->w_topline = s->old_topline;

View File

@ -5231,6 +5231,7 @@ static void nv_dollar(cmdarg_T *cap)
static void nv_search(cmdarg_T *cap) static void nv_search(cmdarg_T *cap)
{ {
oparg_T *oap = cap->oap; oparg_T *oap = cap->oap;
pos_T save_cursor = curwin->w_cursor;
if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) { if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) {
/* Translate "g??" to "g?g?" */ /* Translate "g??" to "g?g?" */
@ -5240,6 +5241,8 @@ static void nv_search(cmdarg_T *cap)
return; return;
} }
// When using 'incsearch' the cursor may be moved to set a different search
// start position.
cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0); cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0);
if (cap->searchbuf == NULL) { if (cap->searchbuf == NULL) {
@ -5248,7 +5251,8 @@ static void nv_search(cmdarg_T *cap)
} }
(void)normal_search(cap, cap->cmdchar, cap->searchbuf, (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
(cap->arg ? 0 : SEARCH_MARK)); (cap->arg || !equalpos(save_cursor, curwin->w_cursor))
? 0 : SEARCH_MARK);
} }
/* /*

View File

@ -33,6 +33,7 @@ func Test_search_cmdline()
" second match " second match
call feedkeys("/the\<C-G>\<cr>", 'tx') call feedkeys("/the\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the', getline('.')) call assert_equal(' 3 the', getline('.'))
call assert_equal([0, 0, 0, 0], getpos('"'))
:1 :1
" third match " third match
call feedkeys("/the".repeat("\<C-G>", 2)."\<cr>", 'tx') call feedkeys("/the".repeat("\<C-G>", 2)."\<cr>", 'tx')
@ -61,6 +62,7 @@ func Test_search_cmdline()
" no further match " no further match
call feedkeys("/the".repeat("\<C-G>", 8)."\<cr>", 'tx') call feedkeys("/the".repeat("\<C-G>", 8)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.')) call assert_equal(' 9 these', getline('.'))
call assert_equal([0, 0, 0, 0], getpos('"'))
" Test 3 " Test 3
" Ctrl-G goes from one match to the next " Ctrl-G goes from one match to the next
@ -182,11 +184,11 @@ func Test_search_cmdline()
1 1
" delete one char, add another " delete one char, add another
call feedkeys("/thei\<bs>s\<cr>", 'tx') call feedkeys("/thei\<bs>s\<cr>", 'tx')
call assert_equal(' 9 these', getline('.')) call assert_equal(' 2 these', getline('.'))
1 1
" delete one char, add another, go to previous match, add one char " delete one char, add another, go to previous match, add one char
call feedkeys("/thei\<bs>s\<bs>\<C-T>\<c-l>\<cr>", 'tx') call feedkeys("/thei\<bs>s\<bs>\<C-T>\<c-l>\<cr>", 'tx')
call assert_equal(' 8 them', getline('.')) call assert_equal(' 9 these', getline('.'))
1 1
" delete all chars, start from the beginning again " delete all chars, start from the beginning again
call feedkeys("/them". repeat("\<bs>",4).'the\>'."\<cr>", 'tx') call feedkeys("/them". repeat("\<bs>",4).'the\>'."\<cr>", 'tx')
@ -240,7 +242,33 @@ func Test_search_cmdline2()
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<C-T>\<C-T>\<cr>", 'tx') call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<C-T>\<C-T>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.')) call assert_equal(' 2 these', getline('.'))
" Test 2: keep the view,
" after deleting a character from the search cmd
call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'])
resize 5
1
call feedkeys("/foo\<bs>\<cr>", 'tx')
redraw
call assert_equal({'lnum': 10, 'leftcol': 0, 'col': 4, 'topfill': 0, 'topline': 6, 'coladd': 0, 'skipcol': 0, 'curswant': 4}, winsaveview())
" remove all history entries
for i in range(10)
call histdel('/')
endfor
" Test 3: reset the view,
" after deleting all characters from the search cmd
norm! 1gg0
" unfortunately, neither "/foo\<c-w>\<cr>", nor "/foo\<bs>\<bs>\<bs>\<cr>",
" nor "/foo\<c-u>\<cr>" works to delete the commandline.
" In that case Vim should return "E35 no previous regular expression",
" but it looks like Vim still sees /foo and therefore the test fails.
" Therefore, disableing this test
"call assert_fails(feedkeys("/foo\<c-w>\<cr>", 'tx'), 'E35')
"call assert_equal({'lnum': 1, 'leftcol': 0, 'col': 0, 'topfill': 0, 'topline': 1, 'coladd': 0, 'skipcol': 0, 'curswant': 0}, winsaveview())
" clean up " clean up
set noincsearch
call test_disable_char_avail(0) call test_disable_char_avail(0)
bw! bw!
endfunc endfunc

View File

@ -124,7 +124,7 @@ static const int included_patches[] = {
2323, 2323,
2322, 2322,
2321, 2321,
// 2320, 2320,
// 2319 NA // 2319 NA
2318, 2318,
2317, 2317,

View File

@ -54,6 +54,7 @@ describe('search cmdline', function()
3 {inc:the} | 3 {inc:the} |
/the^ | /the^ |
]]) ]])
eq({0, 0, 0, 0}, funcs.getpos('"'))
feed('<C-G>') feed('<C-G>')
screen:expect([[ screen:expect([[
3 the | 3 the |
@ -103,6 +104,8 @@ describe('search cmdline', function()
9 {inc:the}se | 9 {inc:the}se |
/the^ | /the^ |
]]) ]])
feed('<CR>')
eq({0, 0, 0, 0}, funcs.getpos('"'))
end end
end end
@ -276,40 +279,40 @@ describe('search cmdline', function()
5 there | 5 there |
/thei^ | /thei^ |
]]) ]])
-- Stay on this match when deleting a character -- Match from initial cursor position when modifying search
feed('<BS>') feed('<BS>')
screen:expect([[ screen:expect([[
4 {inc:the}ir | 1 |
5 there | 2 {inc:the}se |
/the^ | /the^ |
]]) ]])
-- New text advances to next match -- New text advances to next match
feed('s') feed('s')
screen:expect([[ screen:expect([[
9 {inc:thes}e | 1 |
10 foobar | 2 {inc:thes}e |
/thes^ | /thes^ |
]]) ]])
-- Stay on this match when deleting a character -- Stay on this match when deleting a character
feed('<BS>') feed('<BS>')
screen:expect([[ screen:expect([[
9 {inc:the}se | 1 |
10 foobar | 2 {inc:the}se |
/the^ | /the^ |
]]) ]])
-- Advance to previous match -- Advance to previous match
feed('<C-T>') feed('<C-T>')
screen:expect([[ screen:expect([[
8 {inc:the}m | 9 {inc:the}se |
9 these | 10 foobar |
/the^ | /the^ |
]]) ]])
-- Extend search to include next character -- Extend search to include next character
feed('<C-L>') feed('<C-L>')
screen:expect([[ screen:expect([[
8 {inc:them} | 9 {inc:thes}e |
9 these | 10 foobar |
/them^ | /thes^ |
]]) ]])
-- Deleting all characters resets the cursor position -- Deleting all characters resets the cursor position
feed('<BS><BS><BS><BS>') feed('<BS><BS><BS><BS>')
@ -320,14 +323,14 @@ describe('search cmdline', function()
]]) ]])
feed('the') feed('the')
screen:expect([[ screen:expect([[
1 |
2 {inc:the}se | 2 {inc:the}se |
3 the |
/the^ | /the^ |
]]) ]])
feed('\\>') feed('\\>')
screen:expect([[ screen:expect([[
2 these |
3 {inc:the} | 3 {inc:the} |
4 their |
/the\>^ | /the\>^ |
]]) ]])
end) end)
@ -389,4 +392,83 @@ describe('search cmdline', function()
/the^ | /the^ |
]]) ]])
end) end)
it('keeps the view after deleting a char from the search', function()
screen:detach()
screen = Screen.new(20, 6)
screen:attach()
screen:set_default_attr_ids({
inc = {reverse = true}
})
screen:set_default_attr_ignore({
{bold=true, reverse=true}, {bold=true, foreground=Screen.colors.Blue1}
})
tenlines()
feed('/foo')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 {inc:foo}bar |
/foo^ |
]])
feed('<BS>')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 {inc:fo}obar |
/fo^ |
]])
feed('<CR>')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 ^foobar |
/fo |
]])
eq({lnum = 10, leftcol = 0, col = 4, topfill = 0, topline = 6,
coladd = 0, skipcol = 0, curswant = 4},
funcs.winsaveview())
end)
it('restores original view after failed search', function()
screen:detach()
screen = Screen.new(40, 3)
screen:attach()
screen:set_default_attr_ids({
inc = {reverse = true},
err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
more = { bold = true, foreground = Screen.colors.SeaGreen4 },
})
tenlines()
feed('0')
feed('/foo')
screen:expect([[
9 these |
10 {inc:foo}bar |
/foo^ |
]])
feed('<C-W>')
screen:expect([[
1 |
2 these |
/^ |
]])
feed('<CR>')
screen:expect([[
/ |
{err:E35: No previous regular expression} |
{more:Press ENTER or type command to continue}^ |
]])
feed('<CR>')
eq({lnum = 1, leftcol = 0, col = 0, topfill = 0, topline = 1,
coladd = 0, skipcol = 0, curswant = 0},
funcs.winsaveview())
end)
end) end)