mirror of
https://github.com/neovim/neovim.git
synced 2025-11-28 21:20:45 +00:00
Note some bugs were judged to have too ugly a fix to solve, tests to
demonstrate these problems, and the explanation behind not fixing them
are below.
describe('register . problems', function()
before_each(reset)
-- The difficulty here is: The basic requirement is that the text
-- inserted is treated as if it were typed in insert mode. This is why
-- the paste method is to enter insert mode and enter the ". register
-- into readbuf1.
-- We can't add a count into the readbuf here because the insert mode
-- count is implemented with readbuf2 which is checked for characters
-- after readbuf1.
-- Hence, the ".gp command (which adds extra characters into readbuf1
-- to emulate leaving the cursor after the text by moving the cursor
-- after inserting the text) would insert the motion characters into
-- the buffer instead of using them to move after the insert has been
-- done.
-- I could probably get this working properly with a special flag put
-- into start_redo_ins() and set in do_put(), but I think this adds
-- much more complexity than fixing this bug justifies.
pending('should not change the ". register with ".2p', function()
local orig_register = funcs.getreg('.')
feed('2".p')
eq(orig_register, funcs.getreg('.'))
end)
describe("cursor positioning after undo and redo with '.'", function()
before_each(reset)
local function make_cursor_test(macro_string)
return function()
feed(macro_string)
local afterpos = funcs.getcurpos()
local orig_string = curbuf_contents()
feed('u.')
eq(afterpos, funcs.getcurpos())
expect(orig_string)
end
end
-- The difficulty here is: setting the cursor after the end of the
-- pasted text is done by adding a motion command to the
-- stuffbuffer after the insert.
-- Modifying 'redobuff' is done in the code that handles inserting
-- text and moving around.
-- I could add a special case in ins_esc() that checks for a flag
-- set in do_put() to add the motion character to the redo buffer,
-- but I think that is starting to get way too convoluted for the
-- benefit.
pending('should be the same after ".gp and ".gpu.',
make_cursor_test('".gp'))
-- The difficulty here is: putting forwards is implemented by using
-- 'a' instead of 'i' to start insert.
-- Undoing with 'u' an insert that began with 'a' leaves the cursor
-- where the first character was inserted, not where the cursor was
-- when the 'a' was pressed.
-- We account for this the first time by saving the cursor position
-- in do_put(), but this isn't stored in redobuff for a second time
-- around.
-- We can't change how such a fundamental action as undo after
-- inserting with 'a' behaves, we could add in a special case
-- whereby we set a flag in do_put() and read it when entering
-- insert mode but this seems like way too much to fix such a minor
-- bug.
pending('should be the same after ".pu. and ".pu.u.',
make_cursor_test('".pu.'))
end)
end)
This commit is contained in:
120
src/nvim/ops.c
120
src/nvim/ops.c
@@ -2637,12 +2637,81 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
||||
* special characters (newlines, etc.).
|
||||
*/
|
||||
if (regname == '.') {
|
||||
(void)stuff_inserted((dir == FORWARD ? (count == -1 ? 'o' : 'a') :
|
||||
(count == -1 ? 'O' : 'i')), count, FALSE);
|
||||
/* Putting the text is done later, so can't really move the cursor to
|
||||
* the next character. Use "l" to simulate it. */
|
||||
if ((flags & PUT_CURSEND) && gchar_cursor() != NUL)
|
||||
stuffcharReadbuff('l');
|
||||
bool non_linewise_vis = (VIsual_active && VIsual_mode != 'V');
|
||||
|
||||
// PUT_LINE has special handling below which means we use 'i' to start.
|
||||
char command_start_char = non_linewise_vis ? 'c' :
|
||||
(flags & PUT_LINE ? 'i' : (dir == FORWARD ? 'a' : 'i'));
|
||||
|
||||
// To avoid being the affect of 'autoindent' on linewise puts, we create a
|
||||
// new line with `:put _`.
|
||||
if (flags & PUT_LINE) {
|
||||
do_put('_', NULL, dir, 1, PUT_LINE);
|
||||
}
|
||||
|
||||
// If given a count when putting linewise, we stuff the readbuf with the
|
||||
// dot register 'count' times split by newlines.
|
||||
if (flags & PUT_LINE) {
|
||||
stuffcharReadbuff(command_start_char);
|
||||
for (; count > 0; count--) {
|
||||
(void)stuff_inserted(NUL, 1, count != 1);
|
||||
if (count != 1) {
|
||||
// To avoid 'autoindent' affecting the text, use Ctrl_U to remove any
|
||||
// whitespace. Can't just insert Ctrl_U into readbuf1, this would go
|
||||
// back to the previous line in the case of 'noautoindent' and
|
||||
// 'backspace' includes "eol". So we insert a dummy space for Ctrl_U
|
||||
// to consume.
|
||||
stuffReadbuff((char_u *)"\n ");
|
||||
stuffcharReadbuff(Ctrl_U);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(void)stuff_inserted(command_start_char, count, false);
|
||||
}
|
||||
// Putting the text is done later, so can't move the cursor to the next
|
||||
// character. Simulate it with motion commands after the insert.
|
||||
if (flags & PUT_CURSEND) {
|
||||
if (flags & PUT_LINE) {
|
||||
stuffReadbuff((char_u *)"j0");
|
||||
} else {
|
||||
// Avoid ringing the bell from attempting to move into the space after
|
||||
// the current line. We can stuff the readbuffer with "l" if:
|
||||
// 1) 'virtualedit' is "all" or "onemore"
|
||||
// 2) We are not at the end of the line
|
||||
// 3) We are not (one past the end of the line && on the last line)
|
||||
// This allows a visual put over a selection one past the end of the
|
||||
// line joining the current line with the one below.
|
||||
|
||||
// curwin->w_cursor.col marks the byte position of the cursor in the
|
||||
// currunt line. It increases up to a max of
|
||||
// STRLEN(ml_get(curwin->w_cursor.lnum)). With 'virtualedit' and the
|
||||
// cursor past the end of the line, curwin->w_cursor.coladd is
|
||||
// incremented instead of curwin->w_cursor.col.
|
||||
char_u *cursor_pos = get_cursor_pos_ptr();
|
||||
bool one_past_line = (*cursor_pos == NUL);
|
||||
bool end_of_line = false;
|
||||
if (!one_past_line) {
|
||||
end_of_line = (*(cursor_pos + mb_ptr2len(cursor_pos)) == NUL);
|
||||
}
|
||||
|
||||
bool virtualedit_allows = (ve_flags == VE_ALL
|
||||
|| ve_flags == VE_ONEMORE);
|
||||
bool end_of_file = (
|
||||
(curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum)
|
||||
&& one_past_line);
|
||||
if (virtualedit_allows || !(end_of_line || end_of_file)) {
|
||||
stuffcharReadbuff('l');
|
||||
}
|
||||
}
|
||||
} else if (flags & PUT_LINE) {
|
||||
stuffReadbuff((char_u *)"g'[");
|
||||
}
|
||||
|
||||
// So the 'u' command restores cursor position after ".p, save the cursor
|
||||
// position now (though not saving any text).
|
||||
if (command_start_char == 'a') {
|
||||
u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2831,14 +2900,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
||||
else
|
||||
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
|
||||
|
||||
if (has_mbyte)
|
||||
/* move to start of next multi-byte character */
|
||||
curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr());
|
||||
else if (c != TAB || ve_flags != VE_ALL)
|
||||
++curwin->w_cursor.col;
|
||||
++col;
|
||||
} else
|
||||
// move to start of next multi-byte character
|
||||
curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr());
|
||||
col++;
|
||||
} else {
|
||||
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
|
||||
}
|
||||
|
||||
col += curwin->w_cursor.coladd;
|
||||
if (ve_flags == VE_ALL
|
||||
@@ -2892,8 +2959,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
||||
bd.startspaces = incr - bd.endspaces;
|
||||
--bd.textcol;
|
||||
delcount = 1;
|
||||
if (has_mbyte)
|
||||
bd.textcol -= (*mb_head_off)(oldp, oldp + bd.textcol);
|
||||
bd.textcol -= (*mb_head_off)(oldp, oldp + bd.textcol);
|
||||
if (oldp[bd.textcol] != TAB) {
|
||||
/* Only a Tab can be split into spaces. Other
|
||||
* characters will have to be moved to after the
|
||||
@@ -2975,21 +3041,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
||||
// if type is kMTCharWise, FORWARD is the same as BACKWARD on the next
|
||||
// char
|
||||
if (dir == FORWARD && gchar_cursor() != NUL) {
|
||||
if (has_mbyte) {
|
||||
int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr());
|
||||
int bytelen = (*mb_ptr2len)(get_cursor_pos_ptr());
|
||||
|
||||
/* put it on the next of the multi-byte character. */
|
||||
col += bytelen;
|
||||
if (yanklen) {
|
||||
curwin->w_cursor.col += bytelen;
|
||||
curbuf->b_op_end.col += bytelen;
|
||||
}
|
||||
} else {
|
||||
++col;
|
||||
if (yanklen) {
|
||||
++curwin->w_cursor.col;
|
||||
++curbuf->b_op_end.col;
|
||||
}
|
||||
// put it on the next of the multi-byte character.
|
||||
col += bytelen;
|
||||
if (yanklen) {
|
||||
curwin->w_cursor.col += bytelen;
|
||||
curbuf->b_op_end.col += bytelen;
|
||||
}
|
||||
}
|
||||
curbuf->b_op_start = curwin->w_cursor;
|
||||
@@ -3027,7 +3085,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
|
||||
}
|
||||
if (VIsual_active)
|
||||
lnum++;
|
||||
} while (VIsual_active && lnum <= curbuf->b_visual.vi_end.lnum);
|
||||
} while (VIsual_active
|
||||
&& (lnum <= curbuf->b_visual.vi_end.lnum
|
||||
|| lnum <= curbuf->b_visual.vi_start.lnum));
|
||||
|
||||
if (VIsual_active) { /* reset lnum to the last visual line */
|
||||
lnum--;
|
||||
|
||||
Reference in New Issue
Block a user