mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #5782 'Visual-mode put from @. register'
This commit is contained in:
		| @@ -1517,10 +1517,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) | ||||
|         coladvance(curwin->w_curswant); | ||||
|       } | ||||
|       cap->count0 = redo_VIsual_count; | ||||
|       if (redo_VIsual_count != 0) | ||||
|         cap->count1 = redo_VIsual_count; | ||||
|       else | ||||
|         cap->count1 = 1; | ||||
|       cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); | ||||
|     } else if (VIsual_active) { | ||||
|       if (!gui_yank) { | ||||
|         /* Save the current VIsual area for '< and '> marks, and "gv" */ | ||||
| @@ -7727,16 +7724,22 @@ static void nv_put(cmdarg_T *cap) | ||||
|         savereg = copy_register(regname); | ||||
|       } | ||||
|  | ||||
|       /* Now delete the selected text. */ | ||||
|       cap->cmdchar = 'd'; | ||||
|       cap->nchar = NUL; | ||||
|       cap->oap->regname = NUL; | ||||
|       nv_operator(cap); | ||||
|       do_pending_operator(cap, 0, false); | ||||
|       empty = (curbuf->b_ml.ml_flags & ML_EMPTY); | ||||
|       // To place the cursor correctly after a blockwise put, and to leave the | ||||
|       // text in the correct position when putting over a selection with | ||||
|       // 'virtualedit' and past the end of the line, we use the 'c' operator in | ||||
|       // do_put(), which requires the visual selection to still be active. | ||||
|       if (!VIsual_active || VIsual_mode == 'V' || regname != '.') { | ||||
|         // Now delete the selected text. | ||||
|         cap->cmdchar = 'd'; | ||||
|         cap->nchar = NUL; | ||||
|         cap->oap->regname = NUL; | ||||
|         nv_operator(cap); | ||||
|         do_pending_operator(cap, 0, false); | ||||
|         empty = (curbuf->b_ml.ml_flags & ML_EMPTY); | ||||
|  | ||||
|       /* delete PUT_LINE_BACKWARD; */ | ||||
|       cap->oap->regname = regname; | ||||
|         // delete PUT_LINE_BACKWARD; | ||||
|         cap->oap->regname = regname; | ||||
|       } | ||||
|  | ||||
|       /* When deleted a linewise Visual area, put the register as | ||||
|        * lines to avoid it joined with the next line.  When deletion was | ||||
|   | ||||
							
								
								
									
										118
									
								
								src/nvim/ops.c
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								src/nvim/ops.c
									
									
									
									
									
								
							| @@ -2637,12 +2637,79 @@ 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 'autoindent' on linewise puts, 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 eol = false; | ||||
|         if (!one_past_line) { | ||||
|           eol = (*(cursor_pos + mb_ptr2len(cursor_pos)) == NUL); | ||||
|         } | ||||
|  | ||||
|         bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE); | ||||
|         bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum | ||||
|                    && one_past_line; | ||||
|         if (ve_allows || !(eol || eof)) { | ||||
|           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 +2898,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 +2957,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 +3039,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 +3083,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--; | ||||
|   | ||||
| @@ -13,6 +13,8 @@ local check_logs = global_helpers.check_logs | ||||
| local neq = global_helpers.neq | ||||
| local eq = global_helpers.eq | ||||
| local ok = global_helpers.ok | ||||
| local map = global_helpers.map | ||||
| local filter = global_helpers.filter | ||||
|  | ||||
| local start_dir = lfs.currentdir() | ||||
| local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim' | ||||
| @@ -570,6 +572,8 @@ return function(after_each) | ||||
|     neq = neq, | ||||
|     expect = expect, | ||||
|     ok = ok, | ||||
|     map = map, | ||||
|     filter = filter, | ||||
|     nvim = nvim, | ||||
|     nvim_async = nvim_async, | ||||
|     nvim_prog = nvim_prog, | ||||
|   | ||||
							
								
								
									
										936
									
								
								test/functional/normal/put_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										936
									
								
								test/functional/normal/put_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,936 @@ | ||||
| local Screen = require('test.functional.ui.screen') | ||||
| local helpers = require('test.functional.helpers')(after_each) | ||||
|  | ||||
| local clear = helpers.clear | ||||
| local insert = helpers.insert | ||||
| local feed = helpers.feed | ||||
| local expect = helpers.expect | ||||
| local eq = helpers.eq | ||||
| local map = helpers.map | ||||
| local filter = helpers.filter | ||||
| local execute = helpers.execute | ||||
| local curbuf_contents = helpers.curbuf_contents | ||||
| local funcs = helpers.funcs | ||||
| local dedent = helpers.dedent | ||||
| local getreg = funcs.getreg | ||||
|  | ||||
| local function reset() | ||||
|   clear() | ||||
|   insert([[ | ||||
|   Line of words 1 | ||||
|   Line of words 2]]) | ||||
|   execute('goto 1') | ||||
|   feed('itest_string.<esc>u') | ||||
|   funcs.setreg('a', 'test_stringa', 'V') | ||||
|   funcs.setreg('b', 'test_stringb\ntest_stringb\ntest_stringb', 'b') | ||||
|   funcs.setreg('"', 'test_string"', 'v') | ||||
| end | ||||
|  | ||||
| -- We check the last inserted register ". in each of these tests because it is | ||||
| -- implemented completely differently in do_put(). | ||||
| -- It is implemented differently so that control characters and imap'ped | ||||
| -- characters work in the same manner when pasted as when inserted. | ||||
| describe('put command', function() | ||||
|   -- Put a call to clear() here to force the connection to the server. | ||||
|   -- This means we can use the funcs.*() functions while mangling text before | ||||
|   -- the actual tests are run. | ||||
|   clear() | ||||
|   before_each(reset) | ||||
|  | ||||
|   local function visual_marks_zero() | ||||
|     for _,v in pairs(funcs.getpos("'<")) do | ||||
|       if v ~= 0 then | ||||
|         return false | ||||
|       end | ||||
|     end | ||||
|     for _,v in pairs(funcs.getpos("'>")) do | ||||
|       if v ~= 0 then | ||||
|         return false | ||||
|       end | ||||
|     end | ||||
|     return true | ||||
|   end | ||||
|  | ||||
|   -- {{{ Where test definitions are run | ||||
|   local function run_test_variations(test_variations, extra_setup) | ||||
|     reset() | ||||
|     if extra_setup then extra_setup() end | ||||
|     local init_contents = curbuf_contents() | ||||
|     local init_cursorpos = funcs.getcurpos() | ||||
|     local assert_no_change = function (exception_table, after_undo) | ||||
|       expect(init_contents) | ||||
|       -- When putting the ". register forwards, undo doesn't move | ||||
|       -- the cursor back to where it was before. | ||||
|       -- This is because it uses the command character 'a' to | ||||
|       -- start the insert, and undo after that leaves the cursor | ||||
|       -- one place to the right (unless we were at the end of the | ||||
|       -- line when we pasted). | ||||
|       if not (exception_table.undo_position and after_undo) then | ||||
|         eq(funcs.getcurpos(), init_cursorpos) | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     for _, test in pairs(test_variations) do | ||||
|       it(test.description, function() | ||||
|         if extra_setup then extra_setup() end | ||||
|         local orig_dotstr = funcs.getreg('.') | ||||
|         helpers.ok(visual_marks_zero()) | ||||
|         -- Make sure every test starts from the same conditions | ||||
|         assert_no_change(test.exception_table, false) | ||||
|         local was_cli = test.test_action() | ||||
|         test.test_assertions(test.exception_table, false) | ||||
|         -- Check that undo twice puts us back to the original conditions | ||||
|         -- (i.e. puts the cursor and text back to before) | ||||
|         feed('u') | ||||
|         assert_no_change(test.exception_table, true) | ||||
|  | ||||
|         -- Should not have changed the ". register | ||||
|         -- If we paste the ". register with a count we can't avoid | ||||
|         -- changing this register, hence avoid this check. | ||||
|         if not test.exception_table.dot_reg_changed then | ||||
|           eq(funcs.getreg('.'), orig_dotstr) | ||||
|         end | ||||
|  | ||||
|         -- Doing something, undoing it, and then redoing it should | ||||
|         -- leave us in the same state as just doing it once. | ||||
|         -- For :ex actions we want '@:', for normal actions we want '.' | ||||
|  | ||||
|         -- The '.' redo doesn't work for visual put so just exit if | ||||
|         -- it was tested. | ||||
|         -- We check that visual put was used by checking if the '< and | ||||
|         -- '> marks were changed. | ||||
|         if not visual_marks_zero() then | ||||
|           return | ||||
|         end | ||||
|  | ||||
|         if test.exception_table.undo_position then | ||||
|           funcs.setpos('.', init_cursorpos) | ||||
|         end | ||||
|         if was_cli then | ||||
|           feed('@:') | ||||
|         else | ||||
|           feed('.') | ||||
|         end | ||||
|  | ||||
|         test.test_assertions(test.exception_table, true) | ||||
|       end) | ||||
|     end | ||||
|   end -- run_test_variations() | ||||
|   -- }}} | ||||
|  | ||||
|   local function create_test_defs(test_defs, command_base, command_creator, -- {{{ | ||||
|                                   expect_base, expect_creator) | ||||
|     local rettab = {} | ||||
|     local exceptions | ||||
|     for _, v in pairs(test_defs) do | ||||
|       if v[4] then | ||||
|         exceptions = v[4] | ||||
|       else | ||||
|         exceptions = {} | ||||
|       end | ||||
|       table.insert(rettab, | ||||
|       { | ||||
|         test_action = command_creator(command_base, v[1]), | ||||
|         test_assertions = expect_creator(expect_base, v[2]), | ||||
|         description = v[3], | ||||
|         exception_table = exceptions, | ||||
|       }) | ||||
|     end | ||||
|     return rettab | ||||
|   end -- create_test_defs() }}} | ||||
|  | ||||
|   local function find_cursor_position(expect_string) -- {{{ | ||||
|     -- There must only be one occurance of the character 'x' in | ||||
|     -- expect_string. | ||||
|     -- This function removes that occurance, and returns the position that | ||||
|     -- it was in. | ||||
|     -- This returns the cursor position that would leave the 'x' in that | ||||
|     -- place if we feed 'ix<esc>' and the string existed before it. | ||||
|     for linenum, line in pairs(funcs.split(expect_string, '\n', 1)) do | ||||
|       local column = line:find('x') | ||||
|       if column then | ||||
|         return {linenum, column}, expect_string:gsub('x', '') | ||||
|       end | ||||
|     end | ||||
|   end -- find_cursor_position() }}} | ||||
|  | ||||
|   -- Action function creators {{{ | ||||
|   local function create_p_action(test_map, substitution) | ||||
|     local temp_val = test_map:gsub('p', substitution) | ||||
|     return function() | ||||
|       feed(temp_val) | ||||
|       return false | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   local function create_put_action(command_base, substitution) | ||||
|     local temp_val = command_base:gsub('put', substitution) | ||||
|     return function() | ||||
|       execute(temp_val) | ||||
|       return true | ||||
|     end | ||||
|   end | ||||
|   -- }}} | ||||
|  | ||||
|   -- Expect function creator {{{ | ||||
|   local function expect_creator(conversion_function, expect_base, conversion_table) | ||||
|     local temp_expect_string = conversion_function(expect_base, conversion_table) | ||||
|     local cursor_position, expect_string = find_cursor_position(temp_expect_string) | ||||
|     return function(exception_table, after_redo) | ||||
|       expect(expect_string) | ||||
|  | ||||
|       -- Have to use getcurpos() instead of curwinmeths.get_cursor() in | ||||
|       -- order to account for virtualedit. | ||||
|       -- We always want the curswant element in getcurpos(), which is | ||||
|       -- sometimes different to the column element in | ||||
|       -- curwinmeths.get_cursor(). | ||||
|       -- NOTE: The ".gp command leaves the cursor after the pasted text | ||||
|       -- when running, but does not when the command is redone with the | ||||
|       -- '.' command. | ||||
|       if not (exception_table.redo_position and after_redo) then | ||||
|         local actual_position = funcs.getcurpos() | ||||
|         eq(cursor_position, {actual_position[2], actual_position[5]}) | ||||
|       end | ||||
|     end | ||||
|   end -- expect_creator() }}} | ||||
|  | ||||
|   -- Test definitions {{{ | ||||
|   local function copy_def(def) | ||||
|     local rettab = { '', {}, '', nil } | ||||
|     rettab[1] = def[1] | ||||
|     for k,v in pairs(def[2]) do | ||||
|       rettab[2][k] = v | ||||
|     end | ||||
|     rettab[3] = def[3] | ||||
|     if def[4] then | ||||
|       rettab[4] = {} | ||||
|       for k,v in pairs(def[4]) do | ||||
|         rettab[4][k] = v | ||||
|       end | ||||
|     end | ||||
|     return rettab | ||||
|   end | ||||
|  | ||||
|   local normal_command_defs = { | ||||
|     { | ||||
|       'p', | ||||
|       {cursor_after = false, put_backwards = false, dot_register = false}, | ||||
|       'pastes after cursor with p', | ||||
|     }, | ||||
|     { | ||||
|       'gp', | ||||
|       {cursor_after = true, put_backwards = false, dot_register = false}, | ||||
|       'leaves cursor after text with gp', | ||||
|     }, | ||||
|     { | ||||
|       '".p', | ||||
|       {cursor_after = false, put_backwards = false, dot_register = true}, | ||||
|       'works with the ". register', | ||||
|     }, | ||||
|     { | ||||
|       '".gp', | ||||
|       {cursor_after = true, put_backwards = false, dot_register = true}, | ||||
|       'gp works with the ". register', | ||||
|       {redo_position = true}, | ||||
|     }, | ||||
|     { | ||||
|       'P', | ||||
|       {cursor_after = false, put_backwards = true, dot_register = false}, | ||||
|       'pastes before cursor with P', | ||||
|     }, | ||||
|     { | ||||
|       'gP', | ||||
|       {cursor_after = true, put_backwards = true, dot_register = false}, | ||||
|       'gP pastes before cursor and leaves cursor after text', | ||||
|     }, | ||||
|     { | ||||
|       '".P', | ||||
|       {cursor_after = false, put_backwards = true, dot_register = true}, | ||||
|       'P works with ". register', | ||||
|     }, | ||||
|     { | ||||
|       '".gP', | ||||
|       {cursor_after = true, put_backwards = true, dot_register = true}, | ||||
|       'gP works with ". register', | ||||
|       {redo_position = true}, | ||||
|     }, | ||||
|   } | ||||
|  | ||||
|   -- Add a definition applying a count for each definition above. | ||||
|   -- Could do this for each transformation (p -> P, p -> gp etc), but I think | ||||
|   -- it's neater this way (balance between being explicit and too verbose). | ||||
|   for i = 1,#normal_command_defs do | ||||
|     local cur = normal_command_defs[i] | ||||
|  | ||||
|     -- Make modified copy of current definition that includes a count. | ||||
|     local newdef = copy_def(cur) | ||||
|     newdef[2].count = 2 | ||||
|     cur[2].count = 1 | ||||
|     newdef[1] = '2' .. newdef[1] | ||||
|     newdef[3] = 'double ' .. newdef[3] | ||||
|  | ||||
|     if cur[2].dot_register then | ||||
|       if not cur[4] then | ||||
|         newdef[4] = {} | ||||
|       end | ||||
|       newdef[4].dot_reg_changed = true | ||||
|     end | ||||
|  | ||||
|     normal_command_defs[#normal_command_defs + 1] = newdef | ||||
|   end | ||||
|  | ||||
|   local ex_command_defs = { | ||||
|     { | ||||
|       'put', | ||||
|       {put_backwards = false, dot_register = false}, | ||||
|       'pastes linewise forwards with :put', | ||||
|     }, | ||||
|     { | ||||
|       'put!', | ||||
|       {put_backwards = true, dot_register = false}, | ||||
|       'pastes linewise backwards with :put!', | ||||
|     }, | ||||
|     { | ||||
|       'put .', | ||||
|       {put_backwards = false, dot_register = true}, | ||||
|       'pastes linewise with the dot register', | ||||
|     }, | ||||
|     { | ||||
|       'put! .', | ||||
|       {put_backwards = true, dot_register = true}, | ||||
|       'pastes linewise backwards with the dot register', | ||||
|     }, | ||||
|   } | ||||
|  | ||||
|   local function non_dotdefs(def_table) | ||||
|     return filter(function(d) return not d[2].dot_register end, def_table) | ||||
|   end | ||||
|  | ||||
|   -- }}} | ||||
|  | ||||
|   -- Conversion functions {{{ | ||||
|   local function convert_characterwise(expect_base, conversion_table, | ||||
|                                        virtualedit_end, visual_put) | ||||
|     expect_base = dedent(expect_base) | ||||
|     -- There is no difference between 'P' and 'p' when VIsual_active | ||||
|     if not visual_put then | ||||
|       if conversion_table.put_backwards then | ||||
|         -- Special case for virtualedit at the end of a line. | ||||
|         local replace_string | ||||
|         if not virtualedit_end then | ||||
|           replace_string = 'test_stringx"%1' | ||||
|         else | ||||
|           replace_string = 'test_stringx"' | ||||
|         end | ||||
|         expect_base = expect_base:gsub('(.)test_stringx"', replace_string) | ||||
|       end | ||||
|     end | ||||
|     if conversion_table.count > 1 then | ||||
|       local rep_string = 'test_string"' | ||||
|       local extra_puts =  rep_string:rep(conversion_table.count - 1) | ||||
|       expect_base = expect_base:gsub('test_stringx"', extra_puts .. 'test_stringx"') | ||||
|     end | ||||
|     if conversion_table.cursor_after then | ||||
|       expect_base = expect_base:gsub('test_stringx"', 'test_string"x') | ||||
|     end | ||||
|     if conversion_table.dot_register then | ||||
|       expect_base = expect_base:gsub('(test_stringx?)"', '%1.') | ||||
|     end | ||||
|     return expect_base | ||||
|   end -- convert_characterwise() | ||||
|  | ||||
|   local function make_back(string) | ||||
|     local prev_line | ||||
|     local rettab = {} | ||||
|     local string_found = false | ||||
|     for _, line in pairs(funcs.split(string, '\n', 1)) do | ||||
|       if line:find('test_string') then | ||||
|         string_found = true | ||||
|         table.insert(rettab, line) | ||||
|       else | ||||
|         if string_found then | ||||
|           if prev_line then | ||||
|             table.insert(rettab, prev_line) | ||||
|             prev_line = nil | ||||
|           end | ||||
|           table.insert(rettab, line) | ||||
|         else | ||||
|           table.insert(rettab, prev_line) | ||||
|           prev_line = line | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     -- In case there are no lines after the text that was put. | ||||
|     if prev_line and string_found then | ||||
|       table.insert(rettab, prev_line) | ||||
|     end | ||||
|     return table.concat(rettab, '\n') | ||||
|   end -- make_back() | ||||
|  | ||||
|   local function convert_linewise(expect_base, conversion_table, _, use_a, indent) | ||||
|     expect_base = dedent(expect_base) | ||||
|     if conversion_table.put_backwards then | ||||
|       expect_base = make_back(expect_base) | ||||
|     end | ||||
|     local p_str = 'test_string"' | ||||
|     if use_a then | ||||
|       p_str = 'test_stringa' | ||||
|     end | ||||
|  | ||||
|     if conversion_table.dot_register then | ||||
|       expect_base = expect_base:gsub('x' .. p_str, 'xtest_string.') | ||||
|       p_str = 'test_string.' | ||||
|     end | ||||
|  | ||||
|     if conversion_table.cursor_after then | ||||
|       expect_base = expect_base:gsub('x' .. p_str .. '\n', p_str .. '\nx') | ||||
|     end | ||||
|  | ||||
|     -- The 'indent' argument is only used here because a single put with an | ||||
|     -- indent doesn't require special handling. It doesn't require special | ||||
|     -- handling because the cursor is never put before the indent, hence | ||||
|     -- the modification of 'test_stringx"' gives the same overall answer as | ||||
|     -- modifying '    test_stringx"'. | ||||
|  | ||||
|     -- Only happens when using normal mode command actions. | ||||
|     if conversion_table.count and conversion_table.count > 1 then | ||||
|       if not indent then | ||||
|         indent = '' | ||||
|       end | ||||
|       local rep_string = indent .. p_str .. '\n' | ||||
|       local extra_puts =  rep_string:rep(conversion_table.count - 1) | ||||
|       local orig_string, new_string | ||||
|       if conversion_table.cursor_after then | ||||
|         orig_string = indent .. p_str .. '\nx' | ||||
|         new_string = extra_puts .. orig_string | ||||
|       else | ||||
|         orig_string = indent .. 'x' .. p_str .. '\n' | ||||
|         new_string = orig_string .. extra_puts | ||||
|       end | ||||
|       expect_base = expect_base:gsub(orig_string, new_string) | ||||
|     end | ||||
|     return expect_base | ||||
|   end | ||||
|  | ||||
|   local function put_x_last(orig_line, p_str) | ||||
|     local prev_end, cur_end, cur_start = 0, 0, 0 | ||||
|     while cur_start do | ||||
|       prev_end = cur_end | ||||
|       cur_start, cur_end = orig_line:find(p_str, prev_end) | ||||
|     end | ||||
|     -- Assume (because that is the only way I call it) that p_str matches | ||||
|     -- the pattern 'test_string.' | ||||
|     return orig_line:sub(1, prev_end - 1) .. 'x' .. orig_line:sub(prev_end) | ||||
|   end | ||||
|  | ||||
|   local function convert_blockwise(expect_base, conversion_table, visual, | ||||
|                                    use_b, trailing_whitespace) | ||||
|     expect_base = dedent(expect_base) | ||||
|     local p_str = 'test_string"' | ||||
|     if use_b then | ||||
|       p_str = 'test_stringb' | ||||
|     end | ||||
|  | ||||
|     if conversion_table.dot_register then | ||||
|       expect_base = expect_base:gsub('(x?)' .. p_str, '%1test_string.') | ||||
|       -- Looks strange, but the dot is a special character in the pattern | ||||
|       -- and a literal character in the replacement. | ||||
|       expect_base = expect_base:gsub('test_stringx.', 'test_stringx.') | ||||
|       p_str = 'test_string.' | ||||
|     end | ||||
|  | ||||
|     -- No difference between 'p' and 'P' in visual mode. | ||||
|     if not visual then | ||||
|       if conversion_table.put_backwards then | ||||
|         -- One for the line where the cursor is left, one for all other | ||||
|         -- lines. | ||||
|         expect_base = expect_base:gsub('([^x])' .. p_str, p_str .. '%1') | ||||
|         expect_base = expect_base:gsub('([^x])x' .. p_str, 'x' .. p_str .. '%1') | ||||
|         if not trailing_whitespace then | ||||
|           expect_base = expect_base:gsub(' \n', '\n') | ||||
|           expect_base = expect_base:gsub(' $', '') | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     if conversion_table.count and conversion_table.count > 1 then | ||||
|       local p_pattern = p_str:gsub('%.', '%%.') | ||||
|       expect_base = expect_base:gsub(p_pattern, | ||||
|                                      p_str:rep(conversion_table.count)) | ||||
|       expect_base = expect_base:gsub('test_stringx([b".])', | ||||
|                                      p_str:rep(conversion_table.count - 1) | ||||
|                                      .. '%0') | ||||
|     end | ||||
|  | ||||
|     if conversion_table.cursor_after then | ||||
|       if not visual then | ||||
|         local prev_line | ||||
|         local rettab = {} | ||||
|         local prev_in_block = false | ||||
|         for _, line in pairs(funcs.split(expect_base, '\n', 1)) do | ||||
|           if line:find('test_string') then | ||||
|             if prev_line then | ||||
|               prev_line = prev_line:gsub('x', '') | ||||
|               table.insert(rettab, prev_line) | ||||
|             end | ||||
|             prev_line = line | ||||
|             prev_in_block = true | ||||
|           else | ||||
|             if prev_in_block then | ||||
|               prev_line = put_x_last(prev_line, p_str) | ||||
|               table.insert(rettab, prev_line) | ||||
|               prev_in_block = false | ||||
|             end | ||||
|             table.insert(rettab, line) | ||||
|           end | ||||
|         end | ||||
|         if prev_line and prev_in_block then | ||||
|           table.insert(rettab, put_x_last(prev_line, p_str)) | ||||
|         end | ||||
|  | ||||
|         expect_base = table.concat(rettab, '\n') | ||||
|       else | ||||
|         expect_base = expect_base:gsub('x(.)', '%1x') | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     return expect_base | ||||
|   end | ||||
|   -- }}} | ||||
|  | ||||
|   -- Convenience functions {{{ | ||||
|   local function run_normal_mode_tests(test_string, base_map, extra_setup, | ||||
|                                        virtualedit_end, selection_string) | ||||
|     local function convert_closure(e, c) | ||||
|       return convert_characterwise(e, c, virtualedit_end, selection_string) | ||||
|     end | ||||
|     local function expect_normal_creator(expect_base, conversion_table) | ||||
|       local test_expect = expect_creator(convert_closure, expect_base, conversion_table) | ||||
|       return function(exception_table, after_redo) | ||||
|         test_expect(exception_table, after_redo) | ||||
|         if selection_string then | ||||
|           eq(getreg('"'), selection_string) | ||||
|         else | ||||
|           eq(getreg('"'), 'test_string"') | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     run_test_variations( | ||||
|       create_test_defs( | ||||
|         normal_command_defs, | ||||
|         base_map, | ||||
|         create_p_action, | ||||
|         test_string, | ||||
|         expect_normal_creator | ||||
|       ), | ||||
|       extra_setup | ||||
|     ) | ||||
|   end -- run_normal_mode_tests() | ||||
|  | ||||
|   local function convert_linewiseer(expect_base, conversion_table) | ||||
|     return expect_creator(convert_linewise, expect_base, conversion_table) | ||||
|   end | ||||
|  | ||||
|   local function run_linewise_tests(expect_base, base_command, extra_setup) | ||||
|     local linewise_test_defs = create_test_defs( | ||||
|         ex_command_defs, base_command, | ||||
|         create_put_action, expect_base, convert_linewiseer) | ||||
|     run_test_variations(linewise_test_defs, extra_setup) | ||||
|   end -- run_linewise_tests() | ||||
|   -- }}} | ||||
|  | ||||
|   -- Actual tests | ||||
|   describe('default pasting', function() | ||||
|     local expect_string = [[ | ||||
|     Ltest_stringx"ine of words 1 | ||||
|     Line of words 2]] | ||||
|     run_normal_mode_tests(expect_string, 'p') | ||||
|  | ||||
|     run_linewise_tests([[ | ||||
|       Line of words 1 | ||||
|       xtest_string" | ||||
|       Line of words 2]], | ||||
|       'put' | ||||
|     ) | ||||
|   end) | ||||
|  | ||||
|   describe('linewise register', function() | ||||
|     -- put with 'p' | ||||
|     local local_ex_command_defs = non_dotdefs(normal_command_defs) | ||||
|     local base_expect_string = [[ | ||||
|     Line of words 1 | ||||
|     xtest_stringa | ||||
|     Line of words 2]] | ||||
|     local function local_convert_linewise(expect_base, conversion_table) | ||||
|       return convert_linewise(expect_base, conversion_table, nil, true) | ||||
|     end | ||||
|     local function expect_lineput(expect_base, conversion_table) | ||||
|       return expect_creator(local_convert_linewise, expect_base, conversion_table) | ||||
|     end | ||||
|     run_test_variations( | ||||
|       create_test_defs( | ||||
|         local_ex_command_defs, | ||||
|         '"ap', | ||||
|         create_p_action, | ||||
|         base_expect_string, | ||||
|         expect_lineput | ||||
|       ) | ||||
|     ) | ||||
|  | ||||
|     -- put with :put | ||||
|     local linewise_put_defs = non_dotdefs(ex_command_defs) | ||||
|     base_expect_string = [[ | ||||
|     Line of words 1 | ||||
|     xtest_stringa | ||||
|     Line of words 2]] | ||||
|     run_test_variations( | ||||
|       create_test_defs( | ||||
|         linewise_put_defs, | ||||
|         'put a', create_put_action, | ||||
|         base_expect_string, convert_linewiseer | ||||
|       ) | ||||
|     ) | ||||
|  | ||||
|   end) | ||||
|  | ||||
|   describe('blockwise register', function() | ||||
|     local blockwise_put_defs = non_dotdefs(normal_command_defs) | ||||
|     local test_base = [[ | ||||
|     Lxtest_stringbine of words 1 | ||||
|     Ltest_stringbine of words 2 | ||||
|      test_stringb]] | ||||
|  | ||||
|     local function expect_block_creator(expect_base, conversion_table) | ||||
|       return expect_creator(function(e,c) return convert_blockwise(e,c,nil,true) end, | ||||
|                     expect_base, conversion_table) | ||||
|     end | ||||
|  | ||||
|     run_test_variations( | ||||
|       create_test_defs( | ||||
|         blockwise_put_defs, | ||||
|         '"bp', | ||||
|         create_p_action, | ||||
|         test_base, | ||||
|         expect_block_creator | ||||
|       ) | ||||
|     ) | ||||
|   end) | ||||
|  | ||||
|   it('adds correct indentation when put with [p and ]p', function() | ||||
|     feed('G>>"a]pix<esc>') | ||||
|     -- luacheck: ignore | ||||
|     expect([[ | ||||
|     Line of words 1 | ||||
|     	Line of words 2 | ||||
|     	xtest_stringa]]) | ||||
|     feed('uu"a[pix<esc>') | ||||
|     -- luacheck: ignore | ||||
|     expect([[ | ||||
|     Line of words 1 | ||||
|     	xtest_stringa | ||||
|     	Line of words 2]]) | ||||
|   end) | ||||
|  | ||||
|   describe('linewise paste with autoindent', function() | ||||
|     -- luacheck: ignore | ||||
|     run_linewise_tests([[ | ||||
|         Line of words 1 | ||||
|         	Line of words 2 | ||||
|         xtest_string"]], | ||||
|         'put' | ||||
|       , | ||||
|       function() | ||||
|         funcs.setline('$', '	Line of words 2') | ||||
|         -- Set curswant to '8' to be at the end of the tab character | ||||
|         -- This is where the cursor is put back after the 'u' command. | ||||
|         funcs.setpos('.', {0, 2, 1, 0, 8}) | ||||
|         execute('set autoindent') | ||||
|       end | ||||
|     ) | ||||
|   end) | ||||
|  | ||||
|   describe('put inside tabs with virtualedit', function() | ||||
|     local test_string = [[ | ||||
|     Line of words 1 | ||||
|        test_stringx"     Line of words 2]] | ||||
|     run_normal_mode_tests(test_string, 'p', function() | ||||
|       funcs.setline('$', '	Line of words 2') | ||||
|       execute('set virtualedit=all') | ||||
|       funcs.setpos('.', {0, 2, 1, 2, 3}) | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   describe('put after the line with virtualedit', function() | ||||
|     local test_string = [[ | ||||
|     Line of words 1  test_stringx" | ||||
|     	Line of words 2]] | ||||
|     run_normal_mode_tests(test_string, 'p', function() | ||||
|       funcs.setline('$', '	Line of words 2') | ||||
|       execute('set virtualedit=all') | ||||
|       funcs.setpos('.', {0, 1, 16, 1, 17}) | ||||
|     end, true) | ||||
|   end) | ||||
|  | ||||
|   describe('Visual put', function() | ||||
|     describe('basic put', function() | ||||
|       local test_string = [[ | ||||
|       test_stringx" words 1 | ||||
|       Line of words 2]] | ||||
|       run_normal_mode_tests(test_string, 'v2ep', nil, nil, 'Line of') | ||||
|     end) | ||||
|     describe('over trailing newline', function() | ||||
|       local test_string =  'Line of test_stringx"Line of words 2' | ||||
|       run_normal_mode_tests(test_string, 'v$p', function() | ||||
|         funcs.setpos('.', {0, 1, 9, 0, 9}) | ||||
|       end, | ||||
|       nil, | ||||
|       'words 1\n') | ||||
|     end) | ||||
|     describe('linewise mode', function() | ||||
|       local test_string = [[ | ||||
|       xtest_string" | ||||
|       Line of words 2]] | ||||
|       local function expect_vis_linewise(expect_base, conversion_table) | ||||
|         return expect_creator(function(e, c) | ||||
|           return convert_linewise(e, c, nil, nil) | ||||
|         end, | ||||
|         expect_base, conversion_table) | ||||
|       end | ||||
|       run_test_variations( | ||||
|         create_test_defs( | ||||
|           normal_command_defs, | ||||
|           'Vp', | ||||
|           create_p_action, | ||||
|           test_string, | ||||
|           expect_vis_linewise | ||||
|         ), | ||||
|         function() funcs.setpos('.', {0, 1, 1, 0, 1}) end | ||||
|       ) | ||||
|  | ||||
|       describe('with whitespace at bol', function() | ||||
|         local function expect_vis_lineindented(expect_base, conversion_table) | ||||
|           local test_expect = expect_creator(function(e, c) | ||||
|               return convert_linewise(e, c, nil, nil, '    ') | ||||
|             end, | ||||
|             expect_base, conversion_table) | ||||
|           return function(exception_table, after_redo) | ||||
|             test_expect(exception_table, after_redo) | ||||
|             eq(getreg('"'), 'Line of words 1\n') | ||||
|           end | ||||
|         end | ||||
|         local base_expect_string = [[ | ||||
|             xtest_string" | ||||
|         Line of words 2]] | ||||
|         run_test_variations( | ||||
|           create_test_defs( | ||||
|             normal_command_defs, | ||||
|             'Vp', | ||||
|             create_p_action, | ||||
|             base_expect_string, | ||||
|             expect_vis_lineindented | ||||
|           ), | ||||
|           function() | ||||
|             feed('i    test_string.<esc>u') | ||||
|             funcs.setreg('"', '    test_string"', 'v') | ||||
|           end | ||||
|         ) | ||||
|       end) | ||||
|  | ||||
|     end) | ||||
|  | ||||
|     describe('blockwise visual mode', function() | ||||
|       local test_base = [[ | ||||
|         test_stringx"e of words 1 | ||||
|         test_string"e of words 2]] | ||||
|  | ||||
|       local function expect_block_creator(expect_base, conversion_table) | ||||
|         local test_expect = expect_creator(function(e, c) | ||||
|             return convert_blockwise(e, c, true) | ||||
|           end, expect_base, conversion_table) | ||||
|         return function(e,c) | ||||
|           test_expect(e,c) | ||||
|           eq(getreg('"'), 'Lin\nLin') | ||||
|         end | ||||
|       end | ||||
|  | ||||
|       local select_down_test_defs = create_test_defs( | ||||
|           normal_command_defs, | ||||
|           '<C-v>jllp', | ||||
|           create_p_action, | ||||
|           test_base, | ||||
|           expect_block_creator | ||||
|       ) | ||||
|       run_test_variations(select_down_test_defs) | ||||
|  | ||||
|  | ||||
|       -- Undo and redo of a visual block put leave the cursor in the top | ||||
|       -- left of the visual block area no matter where the cursor was | ||||
|       -- when it started. | ||||
|       local undo_redo_no = map(function(table) | ||||
|           local rettab = copy_def(table) | ||||
|           if not rettab[4] then | ||||
|             rettab[4] = {} | ||||
|           end | ||||
|           rettab[4].undo_position = true | ||||
|           rettab[4].redo_position = true | ||||
|           return rettab | ||||
|         end, | ||||
|         normal_command_defs) | ||||
|  | ||||
|       -- Selection direction doesn't matter | ||||
|       run_test_variations( | ||||
|         create_test_defs( | ||||
|           undo_redo_no, | ||||
|           '<C-v>kllp', | ||||
|           create_p_action, | ||||
|           test_base, | ||||
|           expect_block_creator | ||||
|         ), | ||||
|         function() funcs.setpos('.', {0, 2, 1, 0, 1}) end | ||||
|       ) | ||||
|  | ||||
|       describe('blockwise cursor after undo', function() | ||||
|         -- A bit of a hack of the reset above. | ||||
|         -- In the tests that selection direction doesn't matter, we | ||||
|         -- don't check the undo/redo position because it doesn't fit | ||||
|         -- the same pattern as everything else. | ||||
|         -- Here we fix this by directly checking the undo/redo position | ||||
|         -- in the test_assertions of our test definitions. | ||||
|         local function assertion_creator(_,_) | ||||
|           return function(_,_) | ||||
|             feed('u') | ||||
|             -- Have to use feed('u') here to set curswant, because | ||||
|             -- ex_undo() doesn't do that. | ||||
|             eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) | ||||
|             feed('<C-r>') | ||||
|             eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) | ||||
|           end | ||||
|         end | ||||
|  | ||||
|         run_test_variations( | ||||
|           create_test_defs( | ||||
|             undo_redo_no, | ||||
|             '<C-v>kllp', | ||||
|             create_p_action, | ||||
|             test_base, | ||||
|             assertion_creator | ||||
|           ), | ||||
|           function() funcs.setpos('.', {0, 2, 1, 0, 1}) end | ||||
|         ) | ||||
|       end) | ||||
|     end) | ||||
|  | ||||
|  | ||||
|     describe("with 'virtualedit'", function() | ||||
|       describe('splitting a tab character', function() | ||||
|         local base_expect_string = [[ | ||||
|         Line of words 1 | ||||
|           test_stringx"     Line of words 2]] | ||||
|         run_normal_mode_tests( | ||||
|           base_expect_string, | ||||
|           'vp', | ||||
|           function() | ||||
|             funcs.setline('$', '	Line of words 2') | ||||
|             execute('set virtualedit=all') | ||||
|             funcs.setpos('.', {0, 2, 1, 2, 3}) | ||||
|           end, | ||||
|           nil, | ||||
|           ' ' | ||||
|         ) | ||||
|       end) | ||||
|       describe('after end of line', function() | ||||
|         local base_expect_string = [[ | ||||
|         Line of words 1  test_stringx" | ||||
|         Line of words 2]] | ||||
|         run_normal_mode_tests( | ||||
|           base_expect_string, | ||||
|           'vp', | ||||
|           function() | ||||
|             execute('set virtualedit=all') | ||||
|             funcs.setpos('.', {0, 1, 16, 2, 18}) | ||||
|           end, | ||||
|           true, | ||||
|           ' ' | ||||
|         ) | ||||
|       end) | ||||
|     end) | ||||
|   end) | ||||
|  | ||||
|   describe('. register special tests', function() | ||||
|     before_each(reset) | ||||
|     it('applies control character actions', function() | ||||
|       feed('i<C-t><esc>u') | ||||
|       expect([[ | ||||
|       Line of words 1 | ||||
|       Line of words 2]]) | ||||
|       feed('".p') | ||||
|       expect([[ | ||||
|       	Line of words 1 | ||||
|       Line of words 2]]) | ||||
|       feed('u1go<C-v>j".p') | ||||
|       eq([[ | ||||
| 	ine of words 1 | ||||
| 	ine of words 2]], curbuf_contents()) | ||||
|     end) | ||||
|  | ||||
|     local function bell_test(actions, should_ring) | ||||
|       local screen = Screen.new() | ||||
|       screen:attach() | ||||
|       helpers.ok(not screen.bell and not screen.visualbell) | ||||
|       actions() | ||||
|       helpers.wait() | ||||
|       screen:wait(function() | ||||
|         if should_ring then | ||||
|           if not screen.bell and not screen.visualbell then | ||||
|             return 'Bell was not rung after action' | ||||
|           end | ||||
|         else | ||||
|           if screen.bell or screen.visualbell then | ||||
|             return 'Bell was rung after action' | ||||
|           end | ||||
|         end | ||||
|       end) | ||||
|       screen:detach() | ||||
|     end | ||||
|  | ||||
|     it('should not ring the bell with gp at end of line', function() | ||||
|       bell_test(function() feed('$".gp') end) | ||||
|  | ||||
|       -- Even if the last character is a multibyte character. | ||||
|       reset() | ||||
|       funcs.setline(1, 'helloม') | ||||
|       bell_test(function() feed('$".gp') end) | ||||
|     end) | ||||
|  | ||||
|     it('should not ring the bell with gp and end of file', function() | ||||
|       funcs.setpos('.', {0, 2, 1, 0}) | ||||
|       bell_test(function() feed('$vl".gp') end) | ||||
|     end) | ||||
|  | ||||
|     it('should ring the bell when deleting if not appropriate', function() | ||||
|         execute('goto 2') | ||||
|         feed('i<bs><esc>') | ||||
|         expect([[ | ||||
|         ine of words 1 | ||||
|         Line of words 2]]) | ||||
|         bell_test(function() feed('".P') end, true) | ||||
|     end) | ||||
|  | ||||
|     it('should restore cursor position after undo of ".p', function() | ||||
|       local origpos = funcs.getcurpos() | ||||
|       feed('".pu') | ||||
|       eq(origpos, funcs.getcurpos()) | ||||
|     end) | ||||
|  | ||||
|     it("should be unaffected by 'autoindent' with V\".2p", function() | ||||
|       execute('set autoindent') | ||||
|       feed('i test_string.<esc>u') | ||||
|       feed('V".2p') | ||||
|       expect([[ | ||||
|        test_string. | ||||
|        test_string. | ||||
|       Line of words 2]]) | ||||
|     end) | ||||
|   end) | ||||
| end) | ||||
|  | ||||
| @@ -91,6 +91,24 @@ local function tmpname() | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function map(func, tab) | ||||
|   local rettab = {} | ||||
|   for k, v in pairs(tab) do | ||||
|     rettab[k] = func(v) | ||||
|   end | ||||
|   return rettab | ||||
| end | ||||
|  | ||||
| local function filter(filter_func, tab) | ||||
|   local rettab = {} | ||||
|   for _, entry in pairs(tab) do | ||||
|     if filter_func(entry) then | ||||
|       table.insert(rettab, entry) | ||||
|     end | ||||
|   end | ||||
|   return rettab | ||||
| end | ||||
|  | ||||
| return { | ||||
|   eq = eq, | ||||
|   neq = neq, | ||||
| @@ -98,4 +116,6 @@ return { | ||||
|   check_logs = check_logs, | ||||
|   uname = uname, | ||||
|   tmpname = tmpname, | ||||
|   map = map, | ||||
|   filter = filter, | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes